Spring Cloud Zuul Filter 和熔断

转一篇很不错的关于Spring Cloud Zuul 相关用法的文章,基本包含常用的一些场景,另外附上实际项目中的熔断、打印请求日志和登录验证的实例。

原文地址:https://www.cnblogs.com/shihaiming/p/8489006.html ,作者:https://www.cnblogs.com/shihaiming/

1.服务熔断

package com.ftk.hjs.zuul.server.hystrix;

import com.alibaba.fastjson.JSON;
import com.ftk.hjs.common.WebConstants;
import com.ftk.hjs.common.common.Response;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ServiceFallbackProvider implements FallbackProvider {

    private static final Logger logger = LoggerFactory.getLogger(ServiceFallbackProvider.class);
    @Autowired
    private RouteLocator routeLocator;
    @Autowired
    private UrlPathHelper urlPathHelper;

    //服务id,可以用* 或者 null 代表所有服务都过滤
    @Override
    public String getRoute() {
        return null;
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK; //请求网关成功了,所以是ok
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                RequestContext ctx = RequestContext.getCurrentContext();
                Route route = route(ctx.getRequest());
                logger.error(" >>>触发zuul-server断溶;zuulServletContextPath={{}}", route.getLocation());
                Response response = new Response(false);
                response.setMessage(WebConstants.SYSTEM_ERROR_MESSAGE);
                return new ByteArrayInputStream(JSON.toJSONString(response).getBytes("UTF-8")); //返回前端的内容
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); //设置头
                return httpHeaders;
            }
        };
    }

    //核心逻辑,获取请求路径,利用RouteLocator返回路由信息
    protected Route route(HttpServletRequest request) {
        String requestURI = urlPathHelper.getPathWithinApplication(request);
        return routeLocator.getMatchingRoute(requestURI);
    }

}

2.打印日志的拦截器

package com.ftk.hjs.zuul.server.filter;

import com.alibaba.fastjson.JSONObject;
import com.ftk.framework.redis.clients.collections.builder.RedisStrutureBuilder;
import com.ftk.framework.redis.clients.collections.keyValueRedisStructure;
import com.ftk.hjs.common.WebConstants;
import com.ftk.hjs.common.constant.RedisConstants;
import com.ftk.hjs.common.data.LoginUserData;
import com.ftk.hjs.common.utils.StringUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.util.StreamUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;

public class PrintRequestLogFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(PrintRequestLogFilter.class);

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;//要打印返回信息,必须得用"post"
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        try {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            InputStream in = request.getInputStream();
            String reqBbody = StreamUtils.copyToString(in, Charset.forName("UTF-8"));

            String principal = request.getHeader(WebConstants.TOKEN_KEY);

            if (StringUtil.isNotBlank(principal)) {
                String userToken = RedisConstants.UserNP.USER_TOKEN_KEY + principal;
                keyValueRedisStructure<String> structure = RedisStrutureBuilder.ofKeyValue(String.class)
                        .withNameSpace(RedisConstants.UserNP.USER_NAMESPACE)
                        .withttl(RedisConstants.UserNP.USER_TOKEN_EXPIRE).build();
                String token = structure.get(userToken);
                if (StringUtil.isNotBlank(token)) {
                    LoginUserData userData = JSONObject.parseObject(token, LoginUserData.class);
                    log.info("request token:{} , userNum:{} , mobilePhone:{} , channelNum:{}", principal, userData.getUserNum(), userData.getMobilePhone(), userData.getChannelNum());
                }

            }
            log.info("request url:{} , requestUrl:{}", request.getMethod(), request.getRequestURL().toString());

            if (reqBbody != null) {
                log.info("request body:{}", reqBbody);
            }
            String outBody = ctx.getResponseBody();
            if (outBody != null) {
                log.info("response body:{}", outBody);
            }
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
            ctx.setResponseBody(outBody);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

}

3.登录验证

package com.ftk.hjs.zuul.server.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ftk.framework.redis.clients.collections.MapStructure;
import com.ftk.framework.redis.clients.collections.builder.RedisStrutureBuilder;
import com.ftk.framework.redis.clients.collections.keyValueRedisStructure;
import com.ftk.hjs.common.WebConstants;
import com.ftk.hjs.common.common.Request;
import com.ftk.hjs.common.common.Response;
import com.ftk.hjs.common.constant.ErrCodeConstants;
import com.ftk.hjs.common.constant.RedisConstants;
import com.ftk.hjs.common.data.LoginUserData;
import com.ftk.hjs.common.utils.StringUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import lombok.Data;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.UrlPathHelper;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.nio.charset.Charset;
import java.util.*;

import static com.alibaba.fastjson.JSON.parseObject;

/**
 * 登录验证
 * Created by Frank on 2016/12/8.
 */
public class LoginFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(LoginFilter.class);

    private final RouteLocator routeLocator;
    private final UrlPathHelper urlPathHelper;

    private static List<String> oldServers = new ArrayList<>();
    private static List<String> newServers = new ArrayList<>();

    private List<String> excludeSuffixs = new ArrayList<>();

    public LoginFilter(RouteLocator routeLocator, UrlPathHelper urlPathHelper) {
        this.routeLocator = routeLocator;
        this.urlPathHelper = urlPathHelper;
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_FINANCIAL);
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_COMMON);
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_FUND);
        oldServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_INSURANCE);

        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_BANK);
        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_MESSAGE);
        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_USER);
        newServers.add(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD_ROUT);

        excludeSuffixs.addAll(Arrays.asList(".png", ".js", ".css"));
    }

    //核心逻辑,获取请求路径,利用RouteLocator返回路由信息
    protected Route route(HttpServletRequest request) {
        String requestURI = urlPathHelper.getPathWithinApplication(request);
        return routeLocator.getMatchingRoute(requestURI);
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String userNum = null;
        String body = "";
        String principal = request.getHeader(WebConstants.TOKEN_KEY);
        String osTypeKey = request.getHeader(WebConstants.OSTYPE_KEY);
        String channelNum = request.getHeader(WebConstants.CHANNEL_NUM);
        ctx.addZuulRequestHeader(WebConstants.OSTYPE_KEY, osTypeKey);
        ctx.addZuulRequestHeader(WebConstants.CHANNEL_NUM, channelNum);
        try {
            body = StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));
//            log.info(">>> zuul LoginFilter body={}", body);
            if(StringUtil.isBlank(principal)){
                principal = request.getParameter(WebConstants.TOKEN_KEY);
            }
            if (StringUtil.isNotBlank(principal)) {
                String userToken = RedisConstants.UserNP.USER_TOKEN_KEY + principal;
                //如果用户token为空,则肯定是没有登录
                if (StringUtil.isBlank(userToken)) {
                    return null;
                }

                keyValueRedisStructure<String> structure = RedisStrutureBuilder.ofKeyValue(String.class)
                        .withNameSpace(RedisConstants.UserNP.USER_NAMESPACE)
                        .withttl(RedisConstants.UserNP.USER_TOKEN_EXPIRE).build();
                String token = structure.get(userToken);
                if (StringUtil.isNotBlank(token)) {
                    LoginUserData userData = JSONObject.parseObject(token, LoginUserData.class);
                    if (userData != null) {
                        userNum = userData.getUserNum();
                        channelNum = userData.getChannelNum();
                        //延长用户token登录时间
                        structure.set(userToken, token);
                        ctx.addZuulRequestHeader(WebConstants.USER_NUM, userNum);
                        ctx.addZuulRequestHeader(WebConstants.CHANNEL_NUM, channelNum);
                        JSONObject jsonObject = new JSONObject();
                        if (!StringUtil.isEmpty(body)) {
                            jsonObject = JSONObject.parseObject(body);
                        }
                        jsonObject.put("userNum", userNum);
                        request.setAttribute("userNum", userNum);
                        final byte[] reqBodyBytes = jsonObject.toJSONString().getBytes();
                        ctx.setRequest(new HttpServletRequestWrapper(request) {
                            @Override
                            public ServletInputStream getInputStream() {
                                return new ServletInputStreamWrapper(reqBodyBytes);
                            }

                            @Override
                            public int getContentLength() {
                                return reqBodyBytes.length;
                            }

                            @Override
                            public long getContentLengthLong() {
                                return reqBodyBytes.length;
                            }
                        });
                    }
                }
            }

//            log.info(" >>> gateWay url={}, userTokenKey={}, userNum={}", request.getRequestURI(), principal, userNum);

            Route route = route(ctx.getRequest());
            String requestURI = request.getRequestURI();
            String zuulServletContextPath = route.getLocation().replace("-server", "");
            //验证接口是否需要登录
            MapStructure<Boolean> structure = RedisStrutureBuilder.ofMap(Boolean.class).withNameSpace(RedisConstants.SystemNP.SYSTEM_NAMESPACE).build();
            Map<String, Boolean> serviceMethod = structure.get(RedisConstants.SystemNP.SYSTEM_SERVICE_METHOD.concat(zuulServletContextPath));
            NeedLoginBean needLoginBean = adaptServiceMethod(requestURI, body, serviceMethod, zuulServletContextPath);
            log.info(">>> zuul LoginFilter needLoginBean={}", needLoginBean);
            //static 静态资源不进行接口验证

            for (String suffix : excludeSuffixs) {
                if (requestURI.endsWith(suffix)) {
                    return null;
                }
            }

            //选判断此接口是否存在zuul网关中
            if (!needLoginBean.isHasMethod()) {
                log.error(">>> 未知接口。requestType={}", requestURI);
                ctx.setSendZuulResponse(false); //不进行路由
                ctx.getResponse().setContentType("text/html;charset=UTF-8");
                Response response = new Response(needLoginBean.getRequestURI(), false);
                response.setMessage(WebConstants.SYSTEM_ERROR_MESSAGE);
                ctx.setResponseBody(JSON.toJSONString(response));
                return null;
            }
            boolean needLogin = needLoginBean.needLogin;
            if (needLogin && StringUtil.isBlank(userNum)) {
                log.error(">>> 当前接口需要登录,请先登录。requestType={}", requestURI);
                ctx.setSendZuulResponse(false); //不进行路由
                ctx.getResponse().setContentType("text/html;charset=UTF-8");
                Response response = new Response(needLoginBean.getRequestURI(), false);
                response.setErrorCode(ErrCodeConstants.NEED_LOGIN.getCode());
                response.setMessage(ErrCodeConstants.NEED_LOGIN.getMessage());
                ctx.setResponseBody(JSON.toJSONString(response));
            }
        } catch ( Exception e) {
            log.error(e.getMessage(), e);
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            Response response = new Response(request.getRequestURI(), false);
            response.setMessage(WebConstants.SYSTEM_ERROR_MESSAGE);
            ctx.setResponseBody(JSON.toJSONString(response));
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
        }
        return null;
    }

    public NeedLoginBean adaptServiceMethod(String requestURI, String req, Map<String, Boolean> serviceMethod, String zuulServletContextPath) {
        NeedLoginBean needLoginBean = new NeedLoginBean();//兼容老的服务调用方式
        if (oldServers.contains(zuulServletContextPath)) {
            Request bizRequest = parseObject(req, Request.class);
            needLoginBean.setRequestURI(bizRequest.getRequestType());
            Boolean needLogin = serviceMethod.get(bizRequest.getRequestType());
            if (needLogin == null) {
                //false说明此接口不在网关注册接口范围内
                needLoginBean.setHasMethod(false);
            } else {
                needLoginBean.setHasMethod(true);
                needLoginBean.setNeedLogin(needLogin);
            }

        } else if (newServers.contains(zuulServletContextPath)) {
            needLoginBean.setRequestURI(requestURI);
            //false说明此接口不在网关注册接口范围内
            PathMatcher matcher = new AntPathMatcher();
            Iterator it = serviceMethod.keySet().iterator();
            while (it.hasNext()) {
                String key = (String) it.next();
                boolean result = matcher.match(key, requestURI);
                if (result) {
                    needLoginBean.setHasMethod(true);
                    needLoginBean.setNeedLogin(serviceMethod.get(key));
                    break;
                }
            }

        } else {
            throw new RuntimeException(" >>>请求接口不存在");
        }
        return needLoginBean;
    }

    @ToString
    @Data
    private static class NeedLoginBean {
        String requestURI;
        boolean hasMethod;
        boolean needLogin;
    }
}

3.启动类

package com.ftk.hjs.zuul.server;

import com.ftk.framework.redis.clients.collections.factory.RedisConfig;
import com.ftk.framework.redis.clients.collections.factory.RedisConnection;
import com.ftk.hjs.zuul.server.filter.LoginFilter;
import com.ftk.hjs.zuul.server.filter.PrintRequestLogFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.util.UrlPathHelper;

import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@SpringCloudApplication
@EnableFeignClients
@ComponentScan(basePackages = {"com.ftk.hjs","com.ftk.framework"})
public class ZuulServerLauncher implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(ZuulServerLauncher.class);

    @Autowired
    private RedisConfig redisConfig;

    public static void main(String[] args) {
        SpringApplication.run(ZuulServerLauncher.class, args);
    }

    @Override
    public void run(String... strings){
        //初始化redis
//        RedisConnection.init(redisConfig);
        logger.info("ZuulServerLauncher has run !!! {} ", strings);

    }

    @Bean
    public LoginFilter accessFilter(RouteLocator routeLocator) {
        return new LoginFilter(routeLocator,new UrlPathHelper());
    }

    @Bean
    public PrintRequestLogFilter printRequestLogFilter() {
        return new PrintRequestLogFilter();
    }

    private CorsConfiguration addcorsConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        List<String> list = new ArrayList<>();
        list.add("*");
        corsConfiguration.setAllowedOrigins(list);
        /*
        // 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,只能是post方式等等)
        */
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(3600L);
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", addcorsConfig());
        return new CorsFilter(source);
    }

}

最后,以上代码均为部分代码,参照转载文章的说明和实例即可实现自己的网关功能。

原文地址:https://www.cnblogs.com/li-zhi-long/p/10871012.html

时间: 2024-10-23 11:43:17

Spring Cloud Zuul Filter 和熔断的相关文章

spring cloud zuul filter 设置过滤请求

利用spring cloud zuul 设置spring boot 中的filter ,出现了跨域问题,然后根据设置response可解决跨域,同时完成过滤请求 ********************************************************************************************************************* //主要执行的类public class PreRequestLogFilter extends Zu

Spring Cloud Zuul 添加 ZuulFilter

紧接着上篇随笔Spring Cloud Zuul写,添加过滤器,进行权限验证 1.添加过滤器 package com.dzpykj.filter; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Component; import com.netflix.zuul.ZuulFilter; import com.netfli

Spring Cloud Zuul 快速入门

服务网关和Zuul 为什么要有服务网关: 我们都知道在微服务架构中,系统会被拆分为很多个微服务.那么作为客户端要如何去调用这么多的微服务呢?难道要一个个的去调用吗?很显然这是不太实际的,我们需要有一个统一的接口与这些微服务打交道,这就是我们需要服务网关的原因. 我们已经知道,在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求.比如:用户查看一个商品的信息,它可能包含商品基本信息.价格信息.评论信息.折扣信息

SpringCloud---API网关服务---Spring Cloud Zuul

1.概述 1.1 微服务架构出现的问题   及  解决: 1.1.1 前言 每个微服务应用都提供对外的Restful API服务,它通过F5.Nginx等网络设备或工具软件实现对各个微服务的路由与负载均衡,并公开给外部客户端使用: 1.1.2 出现的问题 运维人员角度: 开发人员角度: 为了保证对外服务的安全性,在服务端实现都会加入一定的权限校验,同时为了安全,还会有签名校验等: 这样导致每个微服务应用都需要有冗余的代码,后期维护量非常大: 1.1.3 解决 为了解决这些常见的架构问题,API网

笔记:Spring Cloud Zuul 快速入门

Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了所有其他微服务的实例信息,这样的设计非常巧妙的将服务治理体系中维护的实例信息利用起来,使得维护服务实例的工作交给了服务治理框架自动完成,而对路由规则的维护,默认会将通过以服务名作为 ContextPath 的方式来创建路由映射,也可以做一些特别的配置,对于签名校验.登录校验等在微服务架构中的冗余问题

spring cloud zuul

spring cloud zuul官网:http://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.1.RELEASE/,具体配置不记 spring cloud zuul是配置路由的,我们配置的时候,喜欢这么配置例如: zuul: routes: api-a: path: /api-a/** serviceId: service-A 但是其实我们不配置也是可以路由的,如果我们的api-gateway网关的开的端口是555

利用Spring Cloud实现微服务- 熔断机制

1. 熔断机制介绍 在介绍熔断机制之前,我们需要了解微服务的雪崩效应.在微服务架构中,微服务是完成一个单一的业务功能,这样做的好处是可以做到解耦,每个微服务可以独立演进.但是,一个应用可能会有多个微服务组成,微服务之间的数据交互通过远程过程调用完成.这就带来一个问题,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的"扇出".如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的&

Spring Cloud Zuul 实现路由,获取参数

Spring Cloud Zuul作为网关,可实现请求验证过滤,路由到具体服务的作用. 举例: 比如请求订单接口,报文包括验证信息及具体请求参数. 通过配置路由到具体接口,通过参数调用接口,返回具体报文. 参数传递: 1.post请求,String类型json 2.接口获取@ResponseBody,将获取到String转json,再获取详细参数信息 验证:继承ZuulFilter类,重写run方法,调用具体验证服务,验证通过返回true.

Spring Cloud Zuul性能调整

Spring Cloud 版本: Dalston.SR5 这两天通过JMeter测了一下Spring Cloud Zuul的性能,用的是两台虚机8核8G和4核8G,宿主机是10核逻辑20核,代理的服务简单的返回字符串hello,vm堆内存1G够用 先说一下测试情况,值得一提的是测试并不严谨,因为用的是虚机,并且虚机上还跑了一些其它的东西,所以不能作为最终指导,仅供参考. 8核心的情况下: zuul的性能约是nginx(8个worker)的75%, nginx 8个worker 的cup总占用率为