• 【SpringCloud构建微服务系列】微服务网关Zuul


    一、为什么要用微服务网关

    在微服务架构中,一般不同的微服务有不同的网络地址,而外部客户端(如手机APP)可能需要调用多个接口才能完成一次业务需求。例如一个电影购票的手机APP,可能会调用多个微服务的接口,才能完成一次购票的业务流程。

     

    电影购票业务
    电影购票业务

    如果让客户端直接与微服务通信会产生以下的问题:
    1.客户端会多次请求不同的微服务,增加了客户端的复杂性。
    2.存在跨域请求,在一定场景下处理比较复杂。
    3.认证复杂,每个服务都需要单独认证。
    4.难以重构
    5.某些微服务可能使用了防火墙/游览器不友好的协议,直接访问会有一定的困难。
    以上问题就可以借助微服务网关解决,微服务网关是介于客户端和服务端之间的中间层,所有的外部请求先经过微服务网关

     

    二、Zuul简介

    Zuul是Netflix开源的微服务网关,可以和Eureka,Ribbon,Hystrix等组件配合使用。Zuul的核心是一系列过滤器,这些过滤器可以完成以下功能:
    1.身份认证与安全
    2.审查与监控
    3.动态路由
    4.压力测试
    5.负载均衡
    6.静态响应处理等

    三、编写Zuul微服务网关

    1.创建一个Maven工程microservice-gateway-zuul,并添加依赖

    <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zuul</artifactId>
            </dependency>
    

    2.在启动类添加注解 @EnableZuulProxy声明一个Zuul代理,该代理使用Ribbon来定位注册在Eureka Server中的服务,同时还整合了Hystrix。

    1. @EnableZuulProxy 
    2. @SpringBootApplication 
    3. public class MicroserviceGatewayZuulApplication
    4.  
    5. public static void main(String[] args)
    6. SpringApplication.run(MicroserviceGatewayZuulApplication.class, args); 

    3.编写配置文件application.yml

    server:
      port: 8040
    spring:
      application:
        name: microservice-gateway-zuul
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
    

    说明:默认情况Zuul会代理所有注册到Eureka Server上的微服务,并且路由规则如下。
    http://ZUUL_HOST:ZUUL_PORT/微服务在Eureka上的ServiceId/** 会被转发到serviceId对应的微服务。
    测试过程省略。

    四、路由配置详解

    1.自定义指定微服务的访问路径

    zuul:
      routes:
        microservice-provider-user: /user/**
    

    格式zuul.routes.微服务的serviceId = 指定路径,这样microservice-provider-user微服务就会被映射到路径/user/**
    2.忽略指定微服务

    zuul:
      ignored-services: microservice-provider-user,microservice-consumer-movie
    

    用逗号分隔,来指定忽略多个微服务。
    3.忽略所有微服务,只路由指定微服务

    zuul:
      ignored-services: '*'
      routes:
        microservice-provider-user: /user/**
    

    4.同时指定微服务的serviceId和路径

    zuul:
      routes:
        user-route:  ## 这个路由名称可以随意命名
          service-id: microservice-provider-user
          path: /user/**
    

    这个同示例1是一样的
    5.同时指定path和URL

     zuul:
      routes:
        user-route:  ## 这个路由名称可以随意命名
          url: http://localhost:8000/ #指定的url
          path: /user/**              #url对应的路径
    

    但是这样有些问题,这种方式配置的路由不会作为HystrixCommand执行,也不能使用Ribbon来负载均衡多个URL。
    6.忽略某些路径

    zuul:
      routes:
        microservice-provider-user: /user/**
      ignored-patterns: /**/admin/** #忽略所有包含admin路径的
    

    这样就可以将微服务microservice-provider-user映射到/user/** 路径,同时忽略所有包含/admin/的路径。

    五、Zuul的header设置&上传文件&过滤器

    5.1敏感header的设置

    zuul:
      routes:
        microservice-provider-user:
          path: /users/**
          sensitive-headers: Cookie,Set-Cookie,Authorization
          url: https://downstream
    

    这样就可以为microservice-provider-user指定敏感header了,不过可以指定全局的。

    zuul:
      sensitive-headers: Cookie,Set-Cookie,Authorization
    

    5.2忽略header

    可以用zuul.ignoredHeaders属性丢弃一些header,例如:

    zuul:
      ignored-headers: header1,header2
    

    这样设置后,header1和header2就不会传播到其他微服务中。

    5.3使用Zuul上传文件

    对于小文件(1M以内),无需处理即可上传,对于大文件(10M以上)上传,需要为上传路径添加/zuul前缀,也可以使用zuul.servlet-path自定义前缀。
    1.创建一个Maven工程,microservice-file-upload,并添加以下依赖。

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka</artifactId>
            </dependency>
    

    2.在启动类上添加注解@EnableEurekaClient,@SpringBootApplication
    3.编写controller

    1. /** 
    2. * @author ship 
    3. * @Description 文件上传 
    4. * @Date: 2018-07-24 09:43 
    5. */ 
    6. @Controller 
    7. public class FileUploadController
    8.  
    9. @RequestMapping(value = "/upload",method = RequestMethod.POST) 
    10. public String upload(@RequestParam(value = "file",required = true) MultipartFile file)throws IOException
    11. File desFile = new File(file.getOriginalFilename()); 
    12. FileCopyUtils.copy(file.getBytes(),desFile); 
    13. return desFile.getAbsolutePath(); 

    4.配置文件application.yml

    server:
      port: 8050
    spring:
      application:
        name: microservice-file-upload
      http:
        multipart:
          max-file-size: 2000Mb   # 默认1M
          max-request-size: 2500Mb # 默认10M
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
      instance:
        prefer-ip-address: true
    ## 上传大文件将超时时间设置的长一些
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 6000
    ribbon:
      ConnectTimeOut: 3000
      ReadTimeOut: 60000
    

    测试过程省略,文中使用的springboot版本都是1.5.14.RELEASE。

    5.4过滤器

    Zuul的大部分功能都是通过过滤器实现的,定义了4种标准类型,分别是:
    1.PRE:这种过滤器在请求被路由之前调用,常用于身份验证、记录调试信息等。
    2.ROUTING:这种过滤器将请求路由到微服务。
    3.POST:这种过滤器在路由到微服务后执行。
    4.ERROR:在其他阶段发生错误时执行该过滤器。


    下面编写一个简单的过滤器,来实现打印请求日志。
    1.复制项目microservice-gateway-zuul改为microservice-gateway-zuul-filter
    2.编写自定义的Zuul过滤器

    /**
     * @author ship
     * @Description Zuul过滤器打印日志
     * @Date: 2018-07-23 14:33
     */
    public class PreRequestLogFilter extends ZuulFilter {
    
        private static final Logger log = LoggerFactory.getLogger(PreRequestLogFilter.class);
    
    
        @Override
        public String filterType() {
            //返回过滤器的类型,有pre,route,post,error等几种值
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 1;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            log.info(String.format("send %s request to %s",request.getMethod(),request.getRequestURL().toString()));
            return null;
        }
    }
    

    3.修改启动类,添加以下内容。

        @Bean
        public PreRequestLogFilter preRequestLogFilter(){
            return new PreRequestLogFilter();
        }
    

    测试先依次启动microservice-discovery-eureka,microservice-provider-user,microservice-gateway-zuul-filter,
    再访问http://localhost:8040/microservice-provider-user/1,即可获得如下日志。
    enter description here

    六、Zuul的容错与回退

    在SpringCloud中,Zuul默认已经整合了Hystrix(可自行测试Hystrix监控),下面说说如何实现回退。
    为Zuul添加回退主要是实现FallbackProvider接口(ZuulFallbackProvider已经过时),并返回一个ClientHttpResponse对象,下面开始代码编写。
    1.复制项目microservice-gateway-zuul更改为microservice-gateway-zuul-fallback
    2.编写回退类。

    /**
     * @author ship
     * @Description  在Edgware及更高版本,ZuulFallbackProvider已经过时。
     * @Date: 2018-07-23 13:07
     */
    @Component
    public class UserFallbackProvider implements FallbackProvider {
    
        private static final Logger log = LoggerFactory.getLogger(UserFallbackProvider.class);
    
        @Override
        public ClientHttpResponse fallbackResponse(Throwable cause) {
            log.error("微服务回退原因:"+cause.getMessage());
            return this.fallbackResponse();
        }
    
        @Override
        public String getRoute() {
            //表明为哪个微服务提供回退
            return "microservice-provider-user";
        }
    
        @Override
        public ClientHttpResponse fallbackResponse() {
            return new ClientHttpResponse() {
                @Override
                public HttpStatus getStatusCode() throws IOException {
                    //fallback时的状态码
                    return HttpStatus.OK;
                }
    
                @Override
                public int getRawStatusCode() throws IOException {
                    //返回数字类型的状态码,本例返回的就是200
                    return this.getStatusCode().value();
                }
    
                @Override
                public String getStatusText() throws IOException {
                    return this.getStatusCode().getReasonPhrase();
                }
    
                @Override
                public void close() {
    
                }
    
                @Override
                public InputStream getBody() throws IOException {
                    //响应体
                    return new ByteArrayInputStream("用户微服务不可用,请稍后再试。".getBytes());
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders headers = new HttpHeaders();
                    MediaType mediaType = new MediaType("application","json", Charset.forName("UTF-8"));
                    headers.setContentType(mediaType);
                    return headers;
                }
            };
        }
    
    
    }
    

    当microservice-provider-user微服务无法正常响应时,可以在游览器看到用户微服务不可用,请稍后再试。

    七、Zuul的高可用

    7.1 Zuul客户端也注册到了Eureka Server上

    这种情况下,只要将多个zuul节点注册到Eureka Server即可实现高可用。

    7.2 Zuul客户端未注册到了Eureka Server上

    假如Zuul客户端是一个手机app,我们可以借助一个负载均衡器实现Zuul的高可用,比如Nginx、HAProxy等。
    Zuul高可用

    八、小结

    关于微服务网关Zuul还有一些内容没有说,比如聚合微服务。代码全部提交在了我的github上,欢迎留言指教。

  • 相关阅读:
    python实现二分查找算法例子代码
    Python实现计算圆周率π的值到任意位的方法示例
    什么是TWS耳机
    [置顶] 单例模式lua实现
    推荐五星级C语言学习网站
    poj2689 Prime Distance 有难度 埃拉托斯尼斯筛法的运用
    局域网
    pager-taglib分页处理的使用
    创建ListView的基本步骤
    求最大连续子数列和(只扫描一次数列)
  • 原文地址:https://www.cnblogs.com/2YSP/p/9359711.html
Copyright © 2020-2023  润新知