• Zuul API网关


    Zuul API网关

    解决问题: 服务器集群的集成调用, 集成了ribbon, hystrix, actuator等

    zuul API 网关,为微服务应用提供统一的对外访问接口。
    zuul 还提供过滤器,对所有微服务提供统一的请求校验。

    快速开始

    前提

    例如我们有三中业务服务器, 服务器id名为: item-service, order-service, user-service

    并且配置了eureka注册中心

    1. 创建项目导入依赖

    创建springboot项目, 并导入依赖Zuul, Eureka Discovery Client

    <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>
    

    2. yml配置文件

    spring:
      application:
        name: zuul  # 服务器id/名称
        
    server:
      port: 3001  # 服务器端口
      
    # 配置eureka
    eureka:
      client:
        service-url:
          defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
    
    # 配置zuul
    # 默认规则:
    # 如果不配置,zuul会根据注册表的注册信息,完成自动配置
    # 最好自己手动配置,避免注册表信息不全,自动配置不完整
    zuul:
      routes:
        item-service: /item-service/**
        user-service: /user-service/**
        order-service: /order-service/**
    

    3. 主程序注解

    添加 @EnableZuulProxy@EnableDiscoveryClient (高版本springboot可省略此注解) 注解

    @EnableZuulProxy
    // @EnableDiscoveryClient // springboot高版本可省略
    @SpringBootApplication
    public class Sp11ZuulApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Sp11ZuulApplication.class, args);
    	}
    
    }
    

    4. 访问测试

    例如我们item-service有个/getItem请求, 我们可以访问我们的zuul服务器来发送请求, 如下

    http://localhost:3001/item-service/getItem // 3001 为zuul的端口, 即访问的zuul服务器

    Zuul 请求过滤

    请求过滤的本质就是对接口的访问权限进行限制,实现的思路大致有以下几种:

    1. 为每个服务写一套校验签名和鉴别权限的过滤器和拦截器。(代码是真的冗余)。
    2. 将校验权限剥离,构成一个独立的鉴权服务。(换汤不换药,还是代码冗余)。
    3. 在API网关中实现对客户端请求的校验。(请求过滤)。

    过滤方式

    • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
    • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
    • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
    • ERROR:在其他阶段发生错误时执行该过滤器。
      除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

    1. 定义过滤器

    过滤器需要继承ZuulFilter 抽象类, 并重写其4个方法, 使用@Component注解注入spring容器

    package cn.tedu.sp11.filter;
    
    import cn.tedu.web.util.JsonResult;
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Component
    public class AccessFilter extends ZuulFilter {
    
        /**
         * 指定过滤器的类型: pre(前置), post(发布), routing(路由), error(错误) <br>
         * 多数情况下用pre
         * @return 可以直接返回字符串, 例如"pre", 或者使用FilterConstants中的常量
         */
        @Override
        public String filterType() {
            return FilterConstants.PRE_TYPE;
        }
    
        /**
         * 顺序号 <br>
         *  在默认过滤器中, 第5个过滤器在上下文对象中添加了 service-id(服务id) <br>
         *  所以在第5个过滤器之后, 才能从上下文对象访问 service-id(服务id) <br>
         *  如果需要用到service-id(服务id), 建议使用5之后的数字
         * @return 6
         */
        @Override
        public int filterOrder() {
            return 6;
        }
    
        /**
         * 对当前请求, 是否要进行过滤 <br>
         * @return
         *      如果返回true, 要进行过滤, 会执行下面的过滤方法run() <br>
         *      如果返回false, 不需要过滤, 跳过run()方法, 继续执行后面的流程
         */
        @Override
        public boolean shouldFilter() {
            // 判断用户调用的是否是商品服务
            // 如果是商品服务进行过滤, 其他服务不过滤
    
            // 获取上下文请求对象
            RequestContext ctx = RequestContext.getCurrentContext();
    
            // 从上下文对象获取客户端调用的service-id
            String serviceId = (String) ctx.get(FilterConstants.SERVICE_ID_KEY);
    
            // 如果是商品服务, 返回true, 如果不是则返回false
            return "item-service".equals(serviceId);
        }
    
        /**
         * 过滤代码, 如果有token参数, 则继续执行, 如果为空, 则返回对应内容
         * @return 返回值再当前zuul版本中没有启动, 返回任何数据都无效, 作者考虑了返回值后续可能会有用
         * @throws ZuulException 异常
         */
        @Override
        public Object run() throws ZuulException {
            // http://localhost:3001/item-service/123?token=ds54f6af
    		// 获取上下文请求对象
            RequestContext ctx = RequestContext.getCurrentContext();
    		// 从上下文对象获request对象
            HttpServletRequest request = ctx.getRequest();
    		// 获取请求参数
            String token = request.getParameter("token");
    
            if (StringUtils.isBlank(token)) { // 判断是否为空串
                // 如果为空, 没有token参数, 阻止这次调用继续执行
                ctx.setSendZuulResponse(false);
    
                // 在这里, 直接向客户端发送响应
                // JsonResult: {code: 400, msg: not log in, data: null}
                ctx.setResponseStatusCode(JsonResult.NOT_LOGIN);
                ctx.setResponseBody(
                        JsonResult.err()
                        .code(JsonResult.NOT_LOGIN)
                        .msg("not log in")
                        .toString());
            }
            return null;
        }
    
    }
    

    2. 访问测试即可

    测试访问: http://localhost:3001/item-service/35

    http://localhost:3001/item-service/35?token=1234

    一个有token参数, 一个没有

    zuul集成ribbon

    负载均衡和重试

    zuul已经集成了ribbon, 默认已经实现了负载均衡, 但是没有默认开启重试功能(zuul框架不推荐开启重试)

    1. zuul + ribbon 重试

    需要 spring-retry 依赖 springboot依赖里没有, 从这里复制即可

    <dependency>
    	<groupId>org.springframework.retry</groupId>
    	<artifactId>spring-retry</artifactId>
    </dependency>
    

    2. 配置重试 yml

    需要配置zuul.retryable=true来开启重试功能, 可配置重试参数

    zuul:
      routes:
        item-service: /item-service/**
        user-service: /user-service/**
        order-service: /order-service/**
    
      retryable: true # 开启重试功能
    
    # zuul:
    #   retryable: true
    
    # 配置重试参数 (可选)
    # ribbon:
    #   ConnectTimeout: 1000
    #   ReadTimeout: 1000
    #   MaxAutoRetriesNextServer: 1
    #   MaxAutoRetries: 1
    

    zuul集成hystrix

    1. 创建降级类

    创建降级类, 需要实现FallbackProvider接口, 重写其两个方法, 记得加@Component注解, 注入ioc容器

    package cn.tedu.sp11.fallback;
    
    import cn.tedu.web.util.JsonResult;
    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 java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    @Component
    public class ItemFallback implements FallbackProvider {
    
        /**
         * 返回服务id, 即service-id <br>
         * 针对哪个服务应用当前降级类
         * @return 如果返回"*"或null, 表示对所有服务都执行当前降级类 <br>
         *     如果返回"item-service" 只对item-service服务降级
         */
        @Override
        public String getRoute() {
            return "item-service";
        }
    
        /**
         * 降级执行的代码
         * ClientHttpResponse 是封装降级响应的对象
         */
        @Override
        public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
            return new ClientHttpResponse() {
                // 状态文本和状态码
                @Override
                public HttpStatus getStatusCode() throws IOException {
                    return HttpStatus.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 {
                    // JsonResult:  {code: 200, msg: "调用后台服务失败", data: null}
                    String json = JsonResult.err().msg("调用商品服务失败").toString();
                    ByteArrayInputStream in = new ByteArrayInputStream(json.getBytes());
                    return in;
                }
    
                // 返回响应头
                @Override
                public HttpHeaders getHeaders() {
                    // Content-Type: application/json
                    HttpHeaders h = new HttpHeaders();
                    h.setContentType(MediaType.APPLICATION_JSON_UTF8);
                    return h;
                }
            };
        }
    }
    

    2. hystrix 超时时间

    为了方便测试, 这里设置为500, 一般设置比ribbon重试时间长即可

    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 500
    

    3. 测试

    自行想办法测试, 相信大家都会

    zuul + hystrix 数据监控

    暴露 hystrix.stream 监控端点

    • zuul 已经包含 actuator 依赖
    management:
      endpoints:
        web:
          exposure:
            include: hystrix.stream
    

    启动服务,查看暴露端点

    所有暴露的监控点: http://localhost:3001/actuator
    hystrix监控点: http://localhost:3001/actuator/hystrix.stream

    启动仪表盘项目

    关于dashboard 断路器仪表盘项目的创建: dashboard 断路器仪表盘

    启动仪表盘项目后, 填入上面的hystrix的监控点链接即可

    注意: 必须通过zuul网关访问后台服务才会长生监控数据

    zuul + turbine 聚合监控

    我们可以在turbin的项目中把zuul添加进去即可 (使用逗号隔开)

    turbine中配置

    turbine:
      app-config: order-service, zuul
      cluster-name-expression: new String("default")
    

    熔断测试

    并发工具下载地址: http://httpd.apache.org/docs/current/platform/windows.html#down

    熔断测试,ab -n 20000 -c 50 http://localhost:3001/order-service/123abc

    zuul Cookie过滤

    zuul 会过滤敏感 http 协议头,默认过滤以下协议头:

    • Cookie
    • Set-Cookie
    • Authorization

    可以设置 zuul 不过滤这些协议头

    zuul:
      sensitive-headers: 
    
  • 相关阅读:
    中断类型表
    ComboBox中Datasource、 DisplayMember和ValueMember 三个属性问题 初学者
    ComboBox中的 DataSourc、 DisplayMember 、 valueMember 属性顺序设置问题 初学者
    皮尔逊相关度评价(Pearson correlation Score)
    欧几里得距离评价(Euclidean Distance Score)
    Leonardo da Vince
    维基F#译文: 值与函数(中)
    维基F#译文: 值与函数(上)
    perl5的引用(perlreftut)
    维基F#译文:入门
  • 原文地址:https://www.cnblogs.com/zpKang/p/13599807.html
Copyright © 2020-2023  润新知