Spring Cloud Eureka--Zuul API网关 Hystrix实践

x33g5p2x  于2021-11-17 转载在 Spring  
字(14.2k)|赞(0)|评价(0)|浏览(366)

统一的入口

1.新建spring模块:sp06-zuul

2.添加依赖

eureka+zuul

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <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>Demo project for Spring Boot</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>1.0-SNAPSHOT</version>

        </dependency>
    </dependencies>

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

</project>

3.yml配置

spring:
  application:
    name: zuul
# eureka2001 3001 3001 4001 5001 6001
server:
  port: 3001
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
zuul:
  routes:
    # **包含深层子路径
    # *只包含一层路径
    # service-id 作为访问子路径,是默认设置
    # 根据注册表中的注册信息,zuul可以自动配置,防止注册表不全
    # 最好自己手动配置
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**

4.启动类注解

@EnableZuulProxy

package cn.tedu.sp06;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy
public class Sp06ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp06ZuulApplication.class, args);
    }
}

5. rest.http测试

### 获取订单的商品列表
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的过滤器XZuulProxy,可以过滤客户端请求,在过滤器中可以检查访问权限
    前置、路由、后置\、错误过滤器
    测试:http://localhost:3001/item-service/jsihfhj 没有登录不允许访问
    http://localhost:3001/item-service/jsihfhj?token=ndhfjdhsjfdjs bfjhdskj 有令牌,则一登陆允许访问 (真正项目token应放在协议头,很测试没有登录系统)

1.新建过滤器类

  • AccessFilter 按照Zull的规则实现 继承ZuulFilter

2.重写ZuulFilter接口的四个抽象方法

  • 设置过滤器的类型
  • 设置过滤器添加的顺序号
  • 针对当前请求判断是否要执行下面的过滤代码
  • 过滤代码
  • @Component 自动扫描自动创建实例
    实现代码
package cn.tedu.sp06.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 routing post error*/
    @Override
    public String filterType() {
// return "pre";
        return FilterConstants.PRE_TYPE;
    }

    /*设置过滤器添加的顺序号 前置过滤器中有五个默认过滤器 自定义过滤器放在末尾 位置为6*/
    @Override
    public int filterOrder() {
        return 6;
    }
    /*针对当前请求判断是否要执行下面的过滤代码*/
    @Override
    public boolean shouldFilter() {
        /*调用商品需要判断权限 * 调用用户/订单不检查权限(这里只演示实现,不考虑业务逻辑) * */
        //1.获得一个请求上下文对象
        RequestContext ctx=RequestContext.getCurrentContext();
        //2.从上下文对象获得调用后台服务的 serviceid 类型要转成字符串类型
        String serviceId = (String)ctx.get(FilterConstants.SERVICE_ID_KEY);//"serviceId"
        //3.如果调用了item-service 返回true
        return "item-service".equals(serviceId);
    }
    /*过滤代码,检查权限*/
    @Override
    public Object run() throws ZuulException {
        //1.获得上下文对象
        RequestContext ctx=RequestContext.getCurrentContext();
        //2.获得Request对象
        HttpServletRequest request = ctx.getRequest();
        //3.接收token参数
        String token = request.getParameter("token");
        //4.如果token不存在: ①null ②"" ③" "
        if(StringUtils.isBlank(token)){ //区别idEmpty()
            //5.阻止继续调用
            ctx.setSendZuulResponse(false);//发送zuul响应关闭
            //6.直接返回响应 提示未登录
            //JsonResult--{code:400,msg:未登录,data:null}
            String json=JsonResult.build().code(400).msg("not log in").toString();
            //响应协议头
            ctx.addZuulResponseHeader("Content-Type", "application/json;charset=UTF-8");
            //响应协议体
            ctx.setResponseBody(json);
        }
        return null;//zuul当前版本这个返回值不起任何作用
    }
}

设置过滤器的类型

实现效果

  • 为什么顺序号为6???
  • 前置过滤器中有五个默认过滤器PreDecorationFilter 自定义过滤器放在末尾 位置为6
    在第五个过滤器放入了serviceid

Zuul集成Ribbon

  • 负载均衡默认启用
  • 重试默认禁用
  • 在最前面重试会造成后台服务器大面积出现故障
    也可以启用重试,一般不会启用
    设置:
  • 添加spring-retry依赖
    xml <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
  • yml配置启用重试
spring:
  application:
    name: zuul
# eureka2001 3001 3001 4001 5001 6001
server:
  port: 3001
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
zuul:
  routes:
    # **包含深层子路径
    # *只包含一层路径
    # service-id 作为访问子路径,是默认设置
    # 根据注册表中的注册信息,zuul可以自动配置,防止注册表不全
    # 最好自己手动配置
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**
  retryable: true
# 对所有服务都有效的通用配置
ribbon:
  MaxAutoRetries: 1
# 对某一个单独配置
item-service:
  ribbon:
    MaxAutoRetries: 0

Zuul集成Hystrix

  • Hystrix是容错和限流工具(与sentinel相似)
  • Hystrix容错:通过降级处理
  • Hystrix限流:熔断

1. Zuul网关使用Hystrix进行同错处理,执行降级

  • Zuul默认已经启用Hystrix,任何基础配置都不用做
  • 调用后台服务失败,执行前面模块中的降级代码,返回降级结果
    ①给出错误提示
    ②返回缓存数据
    ③根据业务逻辑返回任何结果都可以

1.新建降级类 ItemFB 实现FallbackProvider接口

package cn.tedu.sp06.fb;

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.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

@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 msg data}
                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;
            }
        };
    }
}

2.新建降级类 ItemFB 实现FallbackProvider接口

package cn.tedu.sp06.fb;

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.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@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 msg data}
                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;
            }
        };
    }
}

实现效果

2. Zuul集成Hystrix实现限流,熔断

  • 当流量 过大,后台服务出现故障,可以断开链路,限制后台故障服务
  • 在06添加断路器,断开连接后,直接执行06的降级代码(不会进行远程调用),返回降级结果(短路请求),执行效率很高
    断路器打开条件:
    ①20次/10s 请求,必须首先满足
    ②50%请求出错,执行了降级代码
    if(20){if(50%){}}
  • 断路器打开之后,会进入半开状态
    在半开状态下,会向服务器尝试发送一次客户端调用
    调用成功,自动关闭断路器恢复正常;
    调用失败,继续保持打开状态。再进入半开状态
    打开 半开 打开 半开…循环

Hystrix dashboard

  • 对降流和熔断的情况进行监控,可以通过监控快速定位故障模块
  • 调用02.03.04在06产生监控日志 使用Actuator工具
  • Actuator是spring提供的监控指标工具,可以暴露项目的各种监控指标
    ①健康状态
    ②spring容器中的所有对象
    ③spring mvc 映射的所有路径
    ④环境变量
    ⑤堆内存镜像(java虚拟机使用的堆内存)

暴露Actuator监控指标

1. Actuator依赖→06

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

actuator依赖于zuul

2. 06yml配置

m.e.w.e.i="*"  # *:暴露所有监控指标
m.e.w.e.i=health,beans,mappings  # *:暴露指定监控指标
m.e.w.e.i=hystrix.stream

***********************************************************
#暴露actuator的监控指标
management:
  endpoints:
    web:
      exposure:
        include: "*"  #暴露所有监控

3.访问

http://localhost:3001/actuator

### actuator
GET http://localhost:3001/actuator
###
GET http://localhost:3001/actuator/health
###
GET http://localhost:3001/actuator/info
###
GET http://localhost:3001/actuator/beans
###
GET http://localhost:3001/actuator/mappings
###
GET http://localhost:3001/actuator/heapdump
###
GET http://localhost:3001/actuator/hystrix.stream

搭建Hystrix-dashboard(仪表盘)

1. 新建spring模块: sp07-hystrix-dashboard

2. 调整pom.xml 添加hystrix dashboard依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>springcloud1</artifactId>
        <groupId>cn.tedu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp07-hystrix-dashboard</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp07-hystrix-dashboard</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.1</version>
                <configuration>
                    <mainClass>cn.tedu.sp07.Sp07HystrixDashboardApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3. 配置yml

server:
  port: 4001
#允许抓取日志的服务器列表
hystrix:
  dashboard:
    proxy-stream-allow-list: localhost

4. 启动类添加注解

@EnableHystrixDashboard

package cn.tedu.sp07;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard
public class Sp07HystrixDashboardApplication {

    public static void main(String[] args) {
        SpringApplication.run(Sp07HystrixDashboardApplication.class, args);
    }

}

5. 启动访问

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

相关文章