美文网首页
6、spring cloud zuul使用

6、spring cloud zuul使用

作者: KissGoodby | 来源:发表于2018-11-19 18:20 被阅读0次

    使用zuul生成关联traceID

    这里我们使用zuul的过虑器,完成一个trace日志的功能,创建一个traceID,关联整个链路,打印在日志中。

    从一个请求的开始和结束,这整个链路traceID唯一,这在生产开发中也是很常见的功能,不仅仅可以将整个链路的日志关联起来,方便排查问题,还为后期日志的收集奠定基础。

    接下来,我们进入代码环节

    1. 首先搭建eureka-serverzuul-serverconsumer-serverprovider-server服务,这在之前都说过,这里不再详细说明,简单把controller代码和配置贴一下
      provider-server的controller,仅仅提供了一个服务
    @RestController
    public class ProviderController {
    
        private final Logger LOGGER = Logger.getLogger(ProviderController.class);
    
        /**
         * get方式接口
         * @param request 请求参数
         */
        @RequestMapping(value = "/provider", method = RequestMethod.GET)
        public String provider(@RequestParam String request) {
            LOGGER.info("========================================");
            LOGGER.info("provider service ");
            LOGGER.info("========================================");
            return "provider, " + request;
        }
    
    }
    

    consumer-server的controller

    @RestController
    public class Controller {
    
        private final Logger LOGGER = Logger.getLogger(Controller.class);
    
        @Autowired
        private RestTemplate restTemplate;
    
        /**
         * GET请求传参数1
         */
        @RequestMapping("/consumer/v1")
        public String consumerV1() {
            return restTemplate.getForEntity("http://zuul-server/api/provider?request={1}", String.class, "test").getBody();
        }
    
        /**
         * GET请求传参数2
         */
        @RequestMapping("/consumer/v2")
        public String consumerV2() {
            Map<String, String> map = Maps.newHashMap();
            map.put("request", "test");
            return restTemplate.getForEntity("http://provider-server/provider?request={request}", String.class, map).getBody();
        }
    }
    

    zuul-server的application配置

    #整合eureka
    eureka:
      instance:
        prefer-ip-address: true #注册服务的IP
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka
        register-with-eureka: true
        fetch-registry: true
    
    
    zuul:
      routes:
        consumer-server:  #手动定义路由映射
          path: /a/**
          url: consumer-server
        provider-server:    #手动定义路由映射
          path: /api/**
          url: provider-server
    

    现在分别启动服务,测试调用没有问题,路由信息如下:

    {
        "/a/**": "consumer-server",
        "/api/**": "provider-server",
        "/consumer-server/**": "consumer-server",
        "/provider-server/**": "provider-server"
    }
    

    这样基础工作完成。

    1. 来创建保存traceID的上下文,这里我新创建了一个module,将下面代码保存到新的jar包里面。
    /**
     * trace model
     *
     * @author hui.wang
     * @since 19 November 2018
     */
    public class Trace {
    
        private String traceId;
        private Date createTime;
    
        public String getTraceId() {
            return traceId;
        }
    
        public void setTraceId(String traceId) {
            this.traceId = traceId;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    }
    
    /**
     * @author hui.wang
     * @since 19 November 2018
     */
    public class TraceContext {
    
        private static final ThreadLocal<Trace> TRACE_CONTEXT = new ThreadLocal<Trace>();
    
        public static Trace getTraceContext() {
            return TRACE_CONTEXT.get();
        }
    
        public static void setTraceContext(Trace traceContext) {
            Assert.notNull(traceContext, "Only non-null traceContext instances are permitted");
            TRACE_CONTEXT.set(traceContext);
        }
    
        public static void clean() {
            TRACE_CONTEXT.remove();
        }
    }
    

    上面创建了trace model和trace context使用threadLocal保存上下文中的trace

    1. 接下来我们编写zuul的filter,zuul的filter分为前置过虑器,后置过虑器和路由过虑器,type类型如下:
    • pre:在请求被路由之前调用。
    • routing:在路由请求时候被调用。
    • post:在routing和error过滤器之后被调用。
    • error:处理请求时发生错误时被调用。
      首先写了前置过虑器TrackingFilter.java
    /**
     * zuul 前置过虑器
     * 设置关联ID
     *
     * @author hui.wang
     * @since 19 November 2018
     */
    @Component
    public class TrackingFilter extends ZuulFilter{
    
        private final Logger LOGGER = Logger.getLogger(TrackingFilter.class);
    
        private static final int FILTER_ORDER = 1;
        private static final boolean SHOULD_FILTER = true;
        private static final String FILTER_TYPE = "pre";
    
        /**
         * filter类型,前置过虑器,后置过虑器和路由过虑器
         */
        @Override
        public String filterType() {
            return FILTER_TYPE;
        }
    
        /**
         * 返回一个整数值,表示filter执行顺序
         */
        @Override
        public int filterOrder() {
            return FILTER_ORDER;
        }
    
        /**
         * 返回一个boolean,表示该过虑器是否执行
         */
        @Override
        public boolean shouldFilter() {
            return SHOULD_FILTER;
        }
    
        /**
         * 每次filter执行的代码
         */
        @Override
        public Object run() {
            if (StringUtils.isEmpty(FilterUtils.getTraceId())) {
                FilterUtils.setTraceId();
            }
    
            RequestContext requestContext = RequestContext.getCurrentContext();
            String URL = requestContext.getRequest().getRequestURL().toString();
            String traceId = FilterUtils.getTraceId();
            LOGGER.info("======================================");
            LOGGER.info("request url = " + URL + ", traceId = " + traceId);
            LOGGER.info("======================================");
            return null;
        }
    }
    

    代码也很简单,先判断header里面有没有trace_id,如果有,不做其他操作,只是打印日志,如果没有生成一个trace_id,放到header里面。这里需要说明一下trace_id是保存到HTTP header里面进行传递的。

    这里使用到了FilterUtils工具类,这里我也贴一下代码

    public class FilterUtils {
    
        public static final String TRACE_ID = "trace_id";
    
        /**
         * 获取随机ID
         */
        public static String generateTraceId() {
            return java.util.UUID.randomUUID().toString();
        }
    
        /**
         * 获取traceId
         */
        public static String getTraceId() {
            RequestContext requestContext = RequestContext.getCurrentContext();
    
            if (StringUtils.isNotEmpty(requestContext.getRequest().getHeader(TRACE_ID))) {
                return requestContext.getRequest().getHeader(TRACE_ID);
            }
            return requestContext.getZuulRequestHeaders().get(TRACE_ID);
        }
    
        /**
         * 设置traceId
         */
        public static void setTraceId() {
            RequestContext requestContext = RequestContext.getCurrentContext();
            requestContext.addZuulRequestHeader(TRACE_ID, generateTraceId());
        }
    }
    

    这里已经把前置zuul过滤器已经编写完成,有这个过滤器是不够的,还需要在请求完成后,将trace_id设置到response header里面进行传递。编写后置zuul 过滤器

    public class ResponseFilter extends ZuulFilter {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(ResponseFilter.class);
    
        private static final int FILTER_ORDER = 1;
        private static final boolean SHOULD_FILTER = true;
        private static final String FILTER_TYPE = "post";
    
        @Override
        public String filterType() {
            return FILTER_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return FILTER_ORDER;
        }
    
        @Override
        public boolean shouldFilter() {
            return SHOULD_FILTER;
        }
    
        @Override
        public Object run() {
            RequestContext requestContext = RequestContext.getCurrentContext();
            String URL = requestContext.getRequest().getRequestURL().toString();
            LOGGER.info("======================================");
            LOGGER.info("response url " + URL + "traceId = " + FilterUtils.getTraceId());
            LOGGER.info("======================================");
            requestContext.getResponse().addHeader(FilterUtils.TRACE_ID, FilterUtils.getTraceId());
            return null;
        }
    }
    

    这里代码也是很简单的,将上下文中的trace_id设置到response header里面

    1. zuul filter编写完成后,只是保证trace_id可以在网关路由阶段可以传递,但当请求打到了具体的应用,如果在应用内不作处理,这个trace_id在后面的链路就丢了,因此还要编写HTTP servlet过虑器,传递trace_id
    public class TraceFilter implements Filter{
    
        private static final Logger LOGGER = LoggerFactory.getLogger(TraceFilter.class);
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            try {
                try {
                    HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
                    if (StringUtils.isNotEmpty(httpServletRequest.getHeader(FilterUtils.TRACE_ID))) {
                        Trace trace = new Trace();
                        trace.setTraceId(httpServletRequest.getHeader(FilterUtils.TRACE_ID));
                        trace.setCreateTime(new Date());
                        TraceContext.setTraceContext(trace);
                        LOGGER.info("filter set trace success");
                    }
                } catch (Exception e) {
                    LOGGER.error("filter set trace error", e);
                }
                filterChain.doFilter(servletRequest, servletResponse);
            } finally {
                TraceContext.clean();
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }
    

    代码也很简单,就是判断header里面有没有trace_id,如果有的话,将trace_id保存到上下文中

    1. 上面完成后,只是保证从路由到应用,trace_id不会丢失,但是如果consumer-server调用provider-server的服务时候,如果trace_id没有被传播,此时调用到provider-server的服务时候,trace_id就丢失了,因此要编写spring Interceptor然后将Interceptor整合到RestTemplate中
    public class TraceInterceptor implements ClientHttpRequestInterceptor{
    
        @Override
        public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
            if (TraceContext.getTraceContext() != null && StringUtils.isNotEmpty(TraceContext.getTraceContext().getTraceId())) {
                HttpHeaders httpHeaders = httpRequest.getHeaders();
                httpHeaders.set(FilterUtils.TRACE_ID, TraceContext.getTraceContext().getTraceId());
            }
            return clientHttpRequestExecution.execute(httpRequest, bytes);
        }
    }
    

    代码也很简单,在上下文中取trace_id然后设置到请求的header里面。

    1. 配置filter和Interceptor
      zuul-server的启动类上配置zuul filter
    @SpringBootApplication
    @EnableZuulProxy
    public class ZuulAppllication {
    
        @Bean
        public TrackingFilter trackingFilter() {
            return new TrackingFilter();
        }
    
        @Bean
        public ResponseFilter responseFilter() {
            return new ResponseFilter();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulAppllication.class, args);
        }
    }
    

    consumer-server的启动类上配置filter和Interceptor

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ConsumerApplication {
    
        @LoadBalanced
        @Bean
        RestTemplate restTemplate() {
            RestTemplate template = new RestTemplate();
            List interceptors = template.getInterceptors();
            if (Objects.isNull(interceptors)) {
                template.setInterceptors(Lists.newArrayList(new TraceInterceptor()));
            } else {
                interceptors.add(new TraceInterceptor());
                template.setInterceptors(interceptors);
            }
            return template;
        }
    
        @Bean
        public TraceFilter traceFilter() {
            return new TraceFilter();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    }
    

    provider-server启动类上配置filter

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ProviderApplicatioin {
    
        @Bean
        public TraceFilter traceFilter() {
            return new TraceFilter();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(ProviderApplicatioin.class, args);
        }
    }
    

    这样代码基本完成,简单测试一下,访问一下http://localhost:8822/a/consumer/v1
    zuul-server的日志:

    2018-11-19 18:18:57.670  INFO 46324 --- [nio-8822-exec-9] c.h.w.s.c.t.a.zuulFilter.TrackingFilter  : ======================================
    2018-11-19 18:18:57.670  INFO 46324 --- [nio-8822-exec-9] c.h.w.s.c.t.a.zuulFilter.TrackingFilter  : request url = http://localhost:8822/a/consumer/v1, traceId = 4d25f02d-0a7a-4002-8694-32493f7daa1b
    2018-11-19 18:18:57.670  INFO 46324 --- [nio-8822-exec-9] c.h.w.s.c.t.a.zuulFilter.TrackingFilter  : ======================================
    2018-11-19 18:18:57.683  INFO 46324 --- [io-8822-exec-10] c.h.w.s.c.t.a.zuulFilter.TrackingFilter  : ======================================
    2018-11-19 18:18:57.683  INFO 46324 --- [io-8822-exec-10] c.h.w.s.c.t.a.zuulFilter.TrackingFilter  : request url = http://10.1.32.187:8822/api/provider, traceId = 4d25f02d-0a7a-4002-8694-32493f7daa1b
    2018-11-19 18:18:57.683  INFO 46324 --- [io-8822-exec-10] c.h.w.s.c.t.a.zuulFilter.TrackingFilter  : ======================================
    2018-11-19 18:18:57.700  INFO 46324 --- [io-8822-exec-10] c.h.w.s.c.t.a.zuulFilter.ResponseFilter  : ======================================
    2018-11-19 18:18:57.701  INFO 46324 --- [io-8822-exec-10] c.h.w.s.c.t.a.zuulFilter.ResponseFilter  : response url http://10.1.32.187:8822/api/providertraceId = 4d25f02d-0a7a-4002-8694-32493f7daa1b
    2018-11-19 18:18:57.701  INFO 46324 --- [io-8822-exec-10] c.h.w.s.c.t.a.zuulFilter.ResponseFilter  : ======================================
    2018-11-19 18:18:57.705  INFO 46324 --- [nio-8822-exec-9] c.h.w.s.c.t.a.zuulFilter.ResponseFilter  : ======================================
    2018-11-19 18:18:57.705  INFO 46324 --- [nio-8822-exec-9] c.h.w.s.c.t.a.zuulFilter.ResponseFilter  : response url http://localhost:8822/a/consumer/v1traceId = 4d25f02d-0a7a-4002-8694-32493f7daa1b
    2018-11-19 18:18:57.705  INFO 46324 --- [nio-8822-exec-9] c.h.w.s.c.t.a.zuulFilter.ResponseFilter  : ======================================
    

    zuul-server的日志可以看出整个链路请求过程
    consumer-server端的日志为:

    2018-11-19 18:18:57.676  INFO 46320 --- [nio-8111-exec-3] c.h.w.s.c.t.a.servletFIlter.TraceFilter  : filter set trace success
    2018-11-19 18:18:57.679  INFO 46320 --- [nio-8111-exec-3] c.h.w.s.cloud.controller.Controller      : =================================
    2018-11-19 18:18:57.679  INFO 46320 --- [nio-8111-exec-3] c.h.w.s.cloud.controller.Controller      : traceId = 4d25f02d-0a7a-4002-8694-32493f7daa1b
    2018-11-19 18:18:57.679  INFO 46320 --- [nio-8111-exec-3] c.h.w.s.cloud.controller.Controller      : =================================
    
    

    provider-server端的日志为:

    2018-11-19 18:18:57.690  INFO 46315 --- [nio-8081-exec-3] c.h.w.s.c.t.a.servletFIlter.TraceFilter  : filter set trace success
    2018-11-19 18:18:57.698  INFO 46315 --- [nio-8081-exec-3] c.h.w.s.c.controller.ProviderController  : ========================================
    2018-11-19 18:18:57.698  INFO 46315 --- [nio-8081-exec-3] c.h.w.s.c.controller.ProviderController  : provider service 被调用,traceId = 4d25f02d-0a7a-4002-8694-32493f7daa1b
    2018-11-19 18:18:57.698  INFO 46315 --- [nio-8081-exec-3] c.h.w.s.c.controller.ProviderController  : ========================================
    

    相关文章

      网友评论

          本文标题:6、spring cloud zuul使用

          本文链接:https://www.haomeiwen.com/subject/pysafqtx.html