• 服务的网关-Zuul(1.5.x)


    • 客户端维护大量的ip和port信息,直接访问指定服务
    • 认证和授权操作,需要在每一个模块中都添加认证和授权的操作
    • 项目的迭代,服务要拆分,服务要合并,需要客户端进行大量的变化
    • 统一的把安全性校验都放在Zuul中

    1 Zuul的快速入门

    创建Maven项目,修改为SpringBoot
    导入依赖

    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在启动类添加注解

    @EnableEurekaClient
    @EnableZuulProxy
    
    • 1
    • 2

    编写配置文件

    # 指定Eureka服务地址
    eureka:
      client:
        service-url:
          defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
    
    #指定服务的名称
    spring:
      application:
        name: ZUUL
    
    server:
      port: 80
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2 Zuul的监控界面

    导入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4

    访问地址:
    http://localhost/actuator/routes (http://ip:port/actuator/routes)

    编写配置文件

    # 查看zuul的监控界面(开发时,配置为*,上线,不要配置)
    management:
      endpoints:
        web:
          exposure:
            include: "*"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3 忽略服务配置
    # zuul的配置
    zuul:
      # 基于服务名忽略服务,无法查看 ,如果要忽略全部的服务  "*",默认配置的全部路径都会被忽略掉(自定义服务的配置,无法忽略的)
      ignored-services: eureka
      # 监控界面依然可以查看,在访问的时候,404
      ignored-patterns: /**/search/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4 自定义服务配置
    # zuul的配置
    zuul:
      # 指定自定义服务(方式一 , key(服务名):value(路径))
    #  routes:
    #    search: /ss/**
    #    customer: /cc/**
      # 指定自定义服务(方式二)
      routes:
        kehu:   # 自定义名称
          path: /ccc/**     # 映射的路径
          serviceId: customer   # 服务名称
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    5 灰度发布

    添加一个配置类

    
    @Bean
    public PatternServiceRouteMapper serviceRouteMapper() {
        return new PatternServiceRouteMapper(
            "(?^.+)-(?v.+$)",
            "${version}/${name}");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    准备一个服务,提供2个版本

    version: v1
    
    #指定服务的名称
    spring:
      application:
        name: CUSTOMER-${version}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    -Dversion=v2 -Dserver.port=9099

    修改Zuul的配置

    # zuul的配置
    zuul:
      # 基于服务名忽略服务,无法查看  , 如果需要用到-v的方式,一定要忽略掉
      # ignored-services: "*"
    
    • 1
    • 2
    • 3
    • 4

    ignored-services: “*”

    6 Zuul的过滤器执行流程

    客户端请求发送到Zuul服务上,首先通过PreFilter链,如果正常放行,会吧请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终再将响应信息交给客户端。

    zuul
    PreFilter
    ErrorFilter
    其他服务
    RoutingFilter
    客户端
    PostFilter

    Zuul过滤器入门
    创建POJO类,继承ZuulFilter抽象类

    @Component
    public class TestZuulFilter extends ZuulFilter {}
    
    • 1
    • 2

    指定当前过滤器的类型

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    
    • 1
    • 2
    • 3
    • 4

    指定过滤器的执行顺序,默认数值为5

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }
    
    • 1
    • 2
    • 3
    • 4

    配置是否启用

    @Override
    public boolean shouldFilter() {
        // 开启当前过滤器
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    指定过滤器中的具体业务代码

    @Override
    public Object run() throws ZuulException {
        System.out.println("prefix过滤器执行~~~");
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    PreFilter实现token校验 (作业)

    准备访问路径,请求参数传递token

    http://localhost/v2/customer/version?token=123

    创建AuthenticationFilter

    @Component
    public class AuthenticationFilter extends ZuulFilter {
        @Override
        public String filterType() {
            return FilterConstants.PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return PRE_DECORATION_FILTER_ORDER - 2;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            //..
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在run方法中编写具体的业务逻辑代码

    @Override
    public Object run() throws ZuulException {
        //1. 获取Request对象
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
    
        //2. 获取token参数
        String token = request.getParameter("token");
    
        //3. 对比token
        if(token == null || !"123".equalsIgnoreCase(token)) {
            //4. token校验失败,直接响应数据
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Zuul的降级

    创建POJO类,实现接口FallbackProvider

    @Component
    public class ZuulFallBack implements FallbackProvider {}
    
    • 1
    • 2

    重写两个方法

    @Override
    public String getRoute() {
        return "*";   // 代表指定全部出现问题的服务,都走这个降级方法
    }
    
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        System.out.println("降级的服务:" + route);
        cause.printStackTrace();
    
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                // 指定具体的HttpStatus
                return HttpStatus.INTERNAL_SERVER_ERROR;
            }
    
            @Override
            public int getRawStatusCode() throws IOException {
                // 返回的状态码
                return HttpStatus.INTERNAL_SERVER_ERROR.value();
            }
    
            @Override
            public String getStatusText() throws IOException {
                // 指定错误信息
                return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
            }
    
            @Override
            public void close() {
    
            }
    
            @Override
            public InputStream getBody() throws IOException {
                // 给用户响应的信息
                String msg = "当前服务:" + route + "出现问题!!!";
                return new ByteArrayInputStream(msg.getBytes());
            }
    
            @Override
            public HttpHeaders getHeaders() {
                // 指定响应头信息
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    Zuul动态路由(作业)

    创建一个过滤器
    // 执行顺序最好放在Pre过滤器的最后面

    在run方法中编写业务逻辑

    @Override
    public Object run() throws ZuulException {
        //1. 获取Request对象
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
    
        //2. 获取参数,redisKey
        String redisKey = request.getParameter("redisKey");  //路径
       
    
        //3. 直接判断
        设计模式23一种
        
        if(redisKey != null && redisKey.equalsIgnoreCase("customer")){
            // http://localhost:8080/customer
            context.put(FilterConstants.SERVICE_ID_KEY,"customer-v1");
            context.put(FilterConstants.REQUEST_URI_KEY,"/customer");
        }else if(redisKey != null && redisKey.equalsIgnoreCase("search")){
            // http://localhost:8081/search/1
            context.put(FilterConstants.SERVICE_ID_KEY,"search");
            context.put(FilterConstants.REQUEST_URI_KEY,"/search/1");
        }
    
        return null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    leetcode-hot100-矩阵
    私有gitlab的搭建和配置教程
    MyBatis基础之执行SQL
    图解LeetCode——1742. 盒子中小球的最大数量(难度:简单)
    第一周 一起走进摄影世界
    【Hack The Box】windows练习-- forest
    研发merge请求合并代码触发检测(gitlab + jenkins)
    python3通过winrm远程执行windows服务器dos命令
    【附源码】Python计算机毕业设计社区卫生预约挂号系统
    vue 打包 插槽 inject reactive draggable 动画 foreach pinia状态管理
  • 原文地址:https://blog.csdn.net/yc_Cabbage/article/details/126395038