SpringCloud----Zuul网关

概念

SpringCloud中的网关工具,,基于这个网关, 可以实现负载均衡、降级/熔断等策略

准备环境

新建zuul工程

  • springboot工程

image-20211213203631883

添加依赖

	<parent>
        <artifactId>springcloud1</artifactId>
        <groupId>cn.tedu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <groupId>cn.tedu</groupId>
    <artifactId>sp06-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp06-zuul</name>
    <description>sp06-zuul</description>
    <properties>
        <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.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

配置yml

spring:
  application:
    name: zuul

server:
  port: 3001
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

zuul:
  routes:
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**

启动类

  • 添加注解 @EnableZuulProxy
    • 可以过滤客户端请求, 在过滤器中可以检查访问权限

image-20211213203646064

测试

### 获取订单的商品列表
GET http://localhost:8001/ty4g33t3

### 减少商品库存
POST http://localhost:8001/decreaseNumber
Accept: application/json
Content-Type: application/json

[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]

### 获取用户
GET http://localhost:8101/7
###
GET http://localhost:8101/8
###
GET http://localhost:8101/9
###
GET http://localhost:8101/10
### 增加积分
GET http://localhost:8101/8/score?score=1000

### 获取订单
GET http://localhost:8201/iujhygf435tg
### 添加订单
GET http://localhost:8201/add


# ---------------------------------------------------------------------------------


### 获取订单的商品列表
GET http://localhost:3001/item-service/ty4g33t3

### 减少商品库存
POST http://localhost:3001/item-service/decreaseNumber
Accept: application/json
Content-Type: application/json

[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]

### 获取用户
GET http://localhost:3001/user-service/7
###
GET http://localhost:3001/user-service/8
###
GET http://localhost:3001/user-service/9
###
GET http://localhost:3001/user-service/10
### 增加积分
GET http://localhost:3001/user-service/8/score?score=1000

### 获取订单
GET http://localhost:3001/order-service/iujhygf435tg
### 添加订单
GET http://localhost:3001/order-service/add

Zuul 权限校验

​ zuul的过滤器 ZuulProxy, 可以过滤客户端请求, 在过滤器中可以检查访问权限

创建filter过滤类

image-20211213203655049

  1. 继承ZuulFilter并重写ZuulFilter的所有方法

@Component   // 自动创建实例
public class AccessFilter extends ZuulFilter {
    @Override
    public String filterType() {
//        return "pre";
        return FilterConstants.PRE_TYPE;  // 常量
    }

    @Override
    public int filterOrder() {
        return 6;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        String serviceId = (String) currentContext.get(FilterConstants.SERVICE_ID_KEY);//"serviceId"
        return "item-service".equals(serviceId);
    }


    @Override
    public Object run() throws ZuulException {
//         http://localhost:3001/item-service/dawdwa?token=dwadawdwad
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        String token = request.getParameter("token");
        if (StringUtils.isBlank(token)) { 
            currentContext.setSendZuulResponse(false); 
            String json = JsonResult.build().code(400).msg("未登录").toString();// toString() : 变成json格式字符串
            currentContext.addZuulResponseHeader("Content-Type"," application/Json; charset=UTF-8"); // 协议头属性
            currentContext.setResponseBody(json); // 设置协议体属性
        }
        return null;  // Zuul当前版本这个返回值不启任何作用, 所以返回null即可
    }
}

内容解析

filterType()方法: 设置过滤器的类型

  • 一共提供了四种类型: pre, routing, post, error
  • return “pre” : 设置为"pre"类型(字符串写法)
  • return FilterConstants.PRE_TYPE; (常量写法) 两种完全一样

filterOrder(): 设置顺序号

  • 前置过滤器中有五个默认过滤器, 自定义的过滤器放到末尾
  • return 6 设置为第6个过滤器
  • 第五个过滤器: 在上下文对象中放入serviceId, 后面过滤器中才能访问这个数据

shouldFilter() : 根据发起的请求, 设置是否执行过滤代码

  • 比如: 调用商品需要判断权限, 调用用户或订单不检查权限
  • RequestContext.getCurrentContext(): 获得请求上下文对象
  • String serviceId =(String)currentContext.get(FilterConstants.SERVICE_ID_KEY); : 从上下文对象获得调用的后台服务的serviceID
  • "item-service".equals(serviceId);: 判断调用的 Item-service 返回true

public Object run(): 设计过滤代码

  • RequestContext currentContext = RequestContext.getCurrentContext();: 获取上下文对象

  • HttpServletRequest request = currentContext.getRequest();: 获取request对象

  • String token = request.getParameter("token");: 接收 token 参数

  • if (StringUtils.isBlank(token)){}: 判断token是否存在

    • 使用了StringUtils提供的方法(isBlank)

      1. isBlank:

        image-20211213203709121

      2. isEmpty:

  • 如果为空, 证明不存在, 则执行里面代码

    • ponse(false);`: 上下文对象的方法(设置发送Zuul响应(关闭)) 阻止继续调用
      • String json = JsonResult.build().code(400).msg("未登录").toString();: 返回响应提示

        • toString : 将对象编程json字符串
        • 因为没有写用户登录, 使用自己编写的响应信息测试
      • currentContext.addZuulResponseHeader("Content-Type"," application/Json; charset=UTF-8"); 设置协议头属性

      • currentContext.setResponseBody(json); : 设置协议体属性

  • return null; : 返回null ( Zuul当前版本这个返回值不启任何作用, 不管写什么都返回 null, 所以返回 null 即可)

测试

  1. 通过网关不携带token访问
GET http://localhost:3001/item-service/ty4g33t3

会返回提示信息: 未登录

image-20211213203720612

  1. 通过网关携带token访问

得到商品信息:

image-20211213203729354

Zuul 集成 ribbon

  • 负载均衡默认启用

  • 重试默认禁用

    • 在最前面重试, 会造成后台服务器大面积压力倍增, 大面积出现故障
  • 启用重试

    • 添加 spring-retry依赖

    • yml 配置启用重试: Zuul.retryable=true

添加依赖

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

配置yml

# 对所有服务都有效
ribbon:
  MaxAutoRetries: 1

# 对 item-service 单独配置
item-service:
  ribbon:
    MaxAutoRetries: 0

Zuul 集成 Hystrix

Hystrix是容错和限流工具

  • Hystrix容错: 降级
  • Hystrix限流: 熔断

降级操作

添加依赖

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

新建fb包

image-20211213203737667

  1. 新建ItemFB实现类
@Component
public class ItemFB implements FallbackProvider {

    /*设置针对哪个后台服务进行降级
    * - item-service: 只针对商品服务降级
    * - * : 对所有服务
    * - null : 对所有服务*/
    @Override
    public String getRoute() {
        return "item-service";
    }

    /*向客户端返回的响应数据*/
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {  // 包含响应数据
            @Override
            public HttpStatus getStatusCode() throws IOException {
                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() {
                // 用来关闭下面的流
                // BAIS不占用系统资源, 不需要关闭
            }
            @Override
            public InputStream getBody() throws IOException {
                // JsonResult - {code:500, msg:xxx, data:null}
                String json = JsonResult.build().code(500).msg("后台服务出错, 请稍后重试").toString();
                return new ByteArrayInputStream(json.getBytes("UTF-8"));
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders h = new HttpHeaders();
                h.add("Content-Type","application/json;charset=UTF-8");
                return h;
            }
        };
    }
}

注意: ClientHttpResponse()里重写的方法在new时会自己创建

  1. 创建orderFB
@Component
public class OrderFB implements FallbackProvider {

    /*设置针对哪个后台服务进行降级
    * - item-service: 只针对商品服务降级
    * - * : 对所有服务
    * - null : 对所有服务*/
    @Override
    public String getRoute() {
        return "order-service";
    }

    /*向客户端返回的响应数据*/
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {  // 包含响应数据
            @Override
            public HttpStatus getStatusCode() throws IOException {
                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() {
                // 用来关闭下面的流
                // BAIS不占用系统资源, 不需要关闭
            }
            @Override
            public InputStream getBody() throws IOException {
                // JsonResult - {code:500, msg:xxx, data:null}
                String json = JsonResult.build().code(500).msg("后台服务出错, 请稍后重试").toString();
                return new ByteArrayInputStream(json.getBytes("UTF-8"));
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders h = new HttpHeaders();
                h.add("Content-Type","application/json;charset=UTF-8");
                return h;
            }
        };
    }
}

测试

http://localhost:3001/item-service/ty4g33t3?token=hhjgjhh

当访问设置了休眠的方法时不会再报错, 而是提示返回的出错信息

image-20211213203747539

限流/熔断操作

当流量过大时, 后台服务出现故障, 可以断开链路, 限制后台故障服务的流量, 等待他从故障中恢复

  • 断路器打开条件:
    • 多少秒内的请求次数(必须首先满足)
    • 请求出错率, 执行了降级代码
  • 断路器打开以后, 会进入半开状态
    • 在半开状态下, 会向后台服务尝试发送一次客户端调用
    • 如果调用成功, 自动关闭断路器, 恢复正常
    • 如果调用失败, 会继续保持打开状态, 一段时间后进入半开状态尝试调用

Hystrix dashboard 监控器

对Hystrix 降级和熔断的情况进行监控, 可以通过监控快速定位故障模块, (通过抓取服务的日志进行监控)

image-20211213203806372

查看监控指标

image-20211213203814523

image-20211213203822854

暴露 actuator 监控指标

添加yml配置

management:
  endpoints:
    web:
      exposure:
        include: "*"
  • “*” 表示所有的

hystrix dashboard 断路器仪表盘

  1. 创建 hystrix dashboard 工程

image-20211213203832662

  1. 添加依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
    <parent>
        <artifactId>springcloud1</artifactId>
        <groupId>cn.tedu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
  • 新建的spring boot 项目
  • 将parent标签改为工程父目录
  • 使用generate可以一键生成

image-20211213203841299

  1. 配置yml
server:
  port: 4001

hystrix:
  dashboard:
    # 允许抓取的服务器列表
    proxy-stream-allow-list: localhost
  1. 启动类添加 @EnableHystrixDashboard 注解

浏览器访问测试

  1. 数据监控路径

http://localhost:3001/actuator/hystrix.stream

  1. Eureka路径

http://localhost:4001/hystrix

image-20211213203849612

  • 打开数据监控路径

    image-20211213203857348

  • 将数据监控路径复制到Eureka点击进入

image-20211213203904270

Apache24 压力测试

  1. 在Apache24\bin目录下cmd

image-20211213203911652

ab -n 20000 -c 50 http://localhost:3001/item-service/35?token=sdasad

  • -c 并发请求次数 (并发50次)
  • -n 一共发送多少次请求 (共发送20000次请求)
  1. 打开仪表盘查看

http://localhost:4001/hystrix

image-20211213203919661

Turbine 聚合监控

聚合 Hystrix 监控数据, hystrix dashboard 仪表盘从 Turbine 抓取聚合后的数据

创建turbine工程

image-20211213203926640

  1. 父工程创建springboot项目

  2. 添加依赖

    •       <parent>
                <artifactId>springcloud1</artifactId>
                <groupId>cn.tedu</groupId>
                <version>1.0-SNAPSHOT</version>
            </parent>
            <modelVersion>4.0.0</modelVersion>
        
        <!--    <parent>-->
        <!--        <groupId>org.springframework.boot</groupId>-->
        <!--        <artifactId>spring-boot-starter-parent</artifactId>-->
        <!--        <version>2.5.6</version>-->
        <!--        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->
        <!--    </parent>-->
      
    •       <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-turbine</artifactId>
                </dependency>
      
  3. 配置yml

    •   server:
          port: 5001
        
        
        spring:
          application:
            name: turbion
        
        eureka:
          client:
            service-url:
              defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
        
        
        turbine:
          # 要聚合的服务
          app-config: zuul   # aaa, bbb, ccc (用逗号隔开可以写多个)
          # 起一个名字(默认: default)
          cluster-name-expression: new String("default")   # turbine设计者喝酒了, 就这么设计的
      
  4. 启动类添加注解 @EnableTurbine

测试

  1. 访问http://localhost:4001/hystrix 仪表盘

  2. 添加http://localhost:5001/turbine.stream 监控地址

    image-20211213203934875

image-20211213203942030

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

偶尔也吹晚风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值