zoukankan      html  css  js  c++  java
  • spring boot 2.0.3+spring cloud (Finchley)5、路由网关Spring Cloud Zuul

     Zuul作为微服务系统的网关组件,用于构建边界服务,致力于动态路由、过滤、监控、弹性伸缩和安全。

    为什么需要Zuul

    Zuul、Ribbon以及Eureka结合可以实现智能路由和负载均衡的功能;网关将所有服务的API接口统一聚合,统一对外暴露。外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性,保护了内部微服务单元的API接口;网关可以做用户身份认证和权限认证,防止非法请求操作API接口;网关可以实现监控功能,实时日志输出,对请求进行记录;网关可以实现流量监控,在高流量的情况下,对服务降级;API接口从内部服务分离出来,方便做测试。

    Zuul通过Servlet来实现,通过自定义的ZuulServlet来对请求进行控制。核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列过滤器。Zuul采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。

    Zuul生命周期如下图。 当一个客户端Request请求进入Zuul网关服务时,网关先进入”pre filter“,进行一系列的验证、操作或者判断。然后交给”routing filter“进行路由转发,转发到具体的服务实例进行逻辑处理、返回数据。当具体的服务处理完成后,最后由”post filter“进行处理,该类型的处理器处理完成之后,将Request信息返回客户端。 

    搭建Zuul服务

    需要配置好eureka,ribbon,可参考:

    spring boot 2.0.3+spring cloud (Finchley)1、搭建Eureka 以及构建高可用Eureka Server集群 

    spring boot 2.0.3+spring cloud (Finchley)2、搭建负载均衡Ribbon (Eureka+Ribbon+RestTemplate)

    spring boot 2.0.3+spring cloud (Finchley)3、声明式调用Feign

     新建工程eureka-zuul-client,pom文件引入相关依赖,包括主maven工程的pom文件,eureka client起步依赖,Zuul的起步依赖,web的起步依赖(已在主maven工程pom中配置)。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.cralor</groupId>
        <artifactId>eureka-zuul-client</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>eureka-zuul-client</name>
        <description>Demo project for Spring Boot</description>
    
        <parent>
            <groupId>com.cralor</groupId>
            <artifactId>chap9-zuul</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    在启动类加上注解@EnableZuulProxy

    @EnableZuulProxy
    @SpringBootApplication
    public class EurekaZuulClientApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(EurekaZuulClientApplication.class, args);
        }
    }

    在配置文件做相关配置,包括端口号5000,服务注册中心地址http://localhost:8761/eureka/,程序名service-zuul,其中zuul路由配置:zuul.routes.hiapi为”/hiapi/**“,zuul.routes.serviceId为”eureka-client“,这两个配置就可以将以”/hiapi“开头的Url路由到eureka-client服务,zuul.routes.hiapi中的”hiapi“是自己定义的需要指定它的path和serviceId,两者配合使用,就可以将指定类型的请求Url路由到指定的serviceId。同理,满足”/ribbonapi“开头的请求Url都会被分发到eureka-ribbon-client,满足”/feignapi“开头的请求Url都会被分发到eureka-feign-client服务。如果服务存在多个实例,zuul会结合ribbon做负载均衡。

    server:
      port: 5000
    spring:
      application:
        name: service-zuul
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/
    
    zuul:
      routes:
        hiapi:
          path: /hiapi/**
          serviceId: eureka-client
        ribbonapi:
          path: /ribbonapi/**
          serviceId: eureka-ribbon-client
        feignapi:
          path: /feignapi/**
          serviceId: eureka-feign-client

    依次启动工程eureka-server、eureka-client(启动两个实例端口为8762、8763)、eureka-ribbon-client、eureka-feign-client和eureka-zuul-client。浏览器多次访问http://localhost:5000/hiapi/hi?name=cralor,会交替显示

       

    可见zuul在路由转发做了负载均衡。同理多次访问http://localhost:5000/ribbonapi/hi?name=cralor和http://localhost:5000/feignapi/hi?name=cralor也可以看到类似内容。

     在Zuul上配置API接口的版本号

    如果想给每个服务的API接口加前缀,可使用zuul.prefix配置,例如http://localhost:5000/v1/hiapi/hi?name=cralor,即在所有的API接口上加一个v1作为版本号。

    zuul:
      prefix: /v1
      routes:
        hiapi:
          path: /hiapi/**
          serviceId: eureka-client
        ribbonapi:
          path: /ribbonapi/**
          serviceId: eureka-ribbon-client
        feignapi:
          path: /feignapi/**
          serviceId: eureka-feign-client

    重启eureka-zuul-client,访问http://localhost:5000/v1/hiapi/hi?name=cralor,结果如上。

    在Zuul上配置熔断器

    Zuul作为Netflix组件,可以与Ribbon、Eureka和Hystrix等组件相结合,实现负载均衡、熔断器的功能。默认情况下Zuul和Ribbon相结合,实现了负载均衡。实现熔断器功能需要实现FallbackProvider接口,实现该接口的两个方法,一个是getRoute(),用于指定熔断器功能应用于哪些路由的服务;另一个方法fallbackResponse()为进入熔断器功能时执行的逻辑。

    @Component
    public class MyFallbackProvider implements FallbackProvider {
        @Override
        public String getRoute() {
            return "eureka-client";
        }
    
        @Override
        public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
            System.out.println("route:"+route);
            System.out.println("exception:"+cause.getMessage());
            return new ClientHttpResponse() {
                @Override
                public HttpStatus getStatusCode() throws IOException {
                    return HttpStatus.OK;
                }
    
                @Override
                public int getRawStatusCode() throws IOException {
                    return 200;
                }
    
                @Override
                public String getStatusText() throws IOException {
                    return "ok";
                }
    
                @Override
                public void close() {
    
                }
    
                @Override
                public InputStream getBody() throws IOException {
                    return new ByteArrayInputStream("oooops!error,i'm the fallback.".getBytes());
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);
                    return headers;
                }
            };
        }
    }

    重启eureka-zuul-client,关闭eureka-client的所有实例,访问http://localhost:5000/v1/hiapi/hi?name=cralor,浏览器显示

    如果需要所有的路由服务都加熔断功能,需要在getRoute()方法上返回”*“的匹配符

        @Override
        public String getRoute() {
            return "*";
        }

    在Zuul使用过滤器

    Zuul包括以下4中过滤器

    PRE过滤器:是在请求路由到具体服务之前执行的,可以做安全验证,如身份验证,参数验证。

    ROUTING过滤器:它用于将请求 路由到具体的微服务实例。默认使用Http Client进行网络请求。

    POST过滤器:在请求已被路由到微服务后执行的。可用作收集统计信息、指标,以及将响应传输到客户端。

    ERROR过滤器:在其他过滤器发生错误时执行。

    zuul中默认实现的filter

    类型顺序过滤器功能
    pre -3 ServletDetectionFilter 标记处理Servlet的类型
    pre -2 Servlet30WrapperFilter 包装HttpServletRequest请求
    pre -1 FormBodyWrapperFilter 包装请求体
    route 1 DebugFilter 标记调试标志
    route 5 PreDecorationFilter 处理请求上下文供后续使用
    route 10 RibbonRoutingFilter serviceId请求转发
    route 100 SimpleHostRoutingFilter url请求转发
    route 500 SendForwardFilter forward请求转发
    post 0 SendErrorFilter 处理有错误的请求响应
    post 1000 SendResponseFilter 处理正常的请求响应

    禁用指定的Filter

    可以在 application.yml 中配置需要禁用的 filter,格式为zuul.<SimpleClassName>.<filterType>.disable=true
    比如要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter就设置

    zuul:
      SendResponseFilter:
        post:
          disable: true

    实现自定义滤器需要继承ZuulFilter,实现ZuulFilter中的抽象方法,包括filterType(),filterOrder()以及IZuulFilter的shouldFilter()和run()方法。filterType()为过滤器类型,有4中类型:pre、post、routing和error。filterOrder()是过滤顺序,它为一个int类型的值,值越小,越早执行该过滤器。shouldFilter()表示是否需要执行该过滤器逻辑,true表示执行,false表示不执行,如果true则执行run()方法。run()方法是具体的过滤的逻辑。本例中检查请求的参数中是否传了token这个参数,如果没有传,则请求不被路由到具体的服务实例,直接返回 响应,状态码为401。

    @Component
    public class MyFilter extends ZuulFilter {
    
        private static Logger log=LoggerFactory.getLogger(MyFilter.class);
    
        @Override
        public String filterType() {
            return "pre"; //定义filter的类型,有pre、route、post、error四种
        }
    
        @Override
        public int filterOrder() {
            return 0;//定义filter的顺序,数字越小表示顺序越高,越先执行
        }
    
        @Override
        public boolean shouldFilter() {
            return true;//表示是否需要执行该filter,true表示执行,false表示不执行
        }
    
        @Override
        public Object run() throws ZuulException {
            //filter需要执行的具体操作
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            String token = request.getParameter("token");
            System.out.println(token);
            if(token==null){
                log.warn("token is empty");
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                try {
                    ctx.getResponse().getWriter().write("token is empty");
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
            log.info("ok");
            return null;
        }
    }

    重启服务,访问http://localhost:5000/v1/hiapi/hi?name=cralor,显示

     地址栏输入http://localhost:5000/v1/hiapi/hi?name=cralor&token=ccc,显示

    可见,MyFilter这个Bean注入IOC容器后,对请求进行了过滤,并在请求路由转发之前进行了逻辑判断。

    案例代码地址:https://github.com/cralor7/springcloud

  • 相关阅读:
    我喜欢的电影
    QObject
    python-类
    pycharm活动模板
    pyqt5模块介绍
    第九章第四节 流体压强与流速的关系
    开源的推荐系统
    VNote: 一个舒适的Markdown笔记软件
    jira项目管理平台搭建
    Win10环境下,告别MarkdownPad,用Notepad++搭建编写md文档的环境
  • 原文地址:https://www.cnblogs.com/cralor/p/9234697.html
Copyright © 2011-2022 走看看