- zuul-API网关
- 统一调用入口
- 创建项目-zuul
- 导入依赖
- application.yml配置
- 主程序
- 启动测试
- 统一权限校验-zuul 请求过滤
- 集成Ribbon
- zuul + ribbon 负载均衡
- zuul + ribbon 重试
- pom.xml 添加 spring-retry 依赖
- 配置 zuul 开启重试,并配置 ribbon 重试参数
- 集成Hystrix
- zuul + hystrix 降级
- 创建降级类
- ItemFallBack
- OrderFallBack
- 降低 hystrix 超时时间
- 启动服务,测试降级
- zuul + hystrix 数据监控
- 暴露 hystrix.stream 监控端点
- 测试
- 开启监控
- zuul + turbine 聚合监控
- 熔断测试
- zuul和feign的比较
- 统一调用入口
zuul-API网关
zuul API 网关,为微服务应用提供统一的对外访问接口。
zuul 还提供过滤器,对所有微服务提供统一的请求校验。
统一调用入口
创建项目-zuul
导入依赖
Eureka Client
,zuul
导入基本commons依赖
<?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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>sp11-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp11-zuul</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.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>cn.tedu</groupId>
<artifactId>sp01-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.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
注解,启动zuul
package cn.tedu.sp11;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class Sp11ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(Sp11ZuulApplication.class, args);
}
}
启动测试
- http://eureka1:2001
- http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
使用postman,POST发送以下格式数据:[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
- http://localhost:3001/user-service/7
- http://localhost:3001/user-service/7/score?score=100
- http://localhost:3001/order-service/123abc
- http://localhost:3001/order-service/
统一权限校验-zuul 请求过滤
添加过滤器,判断用户权限
- 自定义过滤器,继承过滤器父类
ZuulFilter
- 只需要添加 @Component 注解,zuul会完成自动配置
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.lang3.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 {
/*
filterType 指过滤器类型 pre post routing error
*/
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
/*
filterOrder 顺序号 指定过滤器插入的位置
在默认过滤器中,第5个过滤器在上下文对象中添加了service-id,
在第5个过滤器之后,层能从上下文对象访问到service-id
*/
@Override
public int filterOrder() {
return 6;
}
/*
对当前请求来说,是否要进行过滤,
如果返回true,则要进行过滤,会执行过滤的run()方法
如果返回false,跳过过滤代码,继续执行后面的流程
*/
@Override
public boolean shouldFilter() {
//判断用户调用是否是商品服务
//如果是商品服务,则进行过滤
//如果不是,则不过滤
//当前请求的上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
//从上下文对象中获取客户端调用的service id
String serviceId =(String) ctx.get(FilterConstants.SERVICE_ID_KEY);
return "item-service".equals(serviceId);
}
/*
过滤代码
他的返回值,在当前版本中,没有启用,返回任何数据都无效
*/
@Override
public Object run() throws ZuulException {
//
RequestContext ctx = RequestContext.getCurrentContext();
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 login")
.toString());
}
return null;
}
}
集成Ribbon
zuul + ribbon 负载均衡
zuul 已经集成了 ribbon,默认已经实现了负载均衡
zuul + ribbon 重试
pom.xml 添加 spring-retry 依赖
- 需要 spring-retry 依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
1234
配置 zuul 开启重试,并配置 ribbon 重试参数
- 需要开启重试,默认不开启
spring:
application:
name: zuul
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
zuul:
retryable: true
# routes:
# item-service: /item-service/**
# user-service: /user-service/**
# order-service: /order-service/**
ribbon:
ConnectTimeout: 1000
ReadTimeout: 1000
MaxAutoRetriesNextServer: 1
MaxAutoRetries: 1
集成Hystrix
默认已经启用了hystrix,不用做任何基础配置
降级代码:
实现 FallbackProvider 接口,按接口规则来实现降级代码,在实现类上添加 @Component 注解
zuul + hystrix 降级
创建降级类
getRoute() 方法中指定应用此降级类的服务id,**号或null值可以通配所有服务
ItemFallBack
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 {
/*
返回一个servicid 表示针对哪个服务使用当前的降级类
返回* 或者 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.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 {
String josn = JsonResult.err().msg("调用远程商品服务失败").toString();
ByteArrayInputStream inputStream = new ByteArrayInputStream(josn.getBytes("UTF-8"));
return inputStream;
}
@Override
public HttpHeaders getHeaders() {
//Content-Type:application/json
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
OrderFallBack
package cn.tedu.sp11.fallback;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class OrderFallBack implements FallbackProvider {
@Override
public String getRoute() {
return "order-service"; //"*"; //null;
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return response();
}
private ClientHttpResponse response() {
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 {
log.info("fallback body");
String s = JsonResult.err().msg("后台服务错误").toString();
return new ByteArrayInputStream(s.getBytes("UTF-8"));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
降低 hystrix 超时时间
在application.yml文件中添加超时配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
启动服务,测试降级
http://localhost:3001/item-service/35
zuul + hystrix 数据监控
暴露 hystrix.stream 监控端点
- zuul 已经包含 actuator 依赖
management:
endpoints:
web:
exposure:
include: hystrix.stream
测试
- 启动服务,查看暴露的监控端点
http://localhost:3001/actuator
http://localhost:3001/actuator/hystrix.stream
开启监控
启动 sp08-hystrix-dashboard,填入 zuul 的监控端点路径,开启监控
http://localhost:4001/hystrix
填入监控端点:
http://localhost:3001/actuator/hystrix.stream
必须通过zuul网关访问后台服务才会生成监控数据
- http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
使用postman,POST发送以下格式数据:[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
- http://localhost:3001/user-service/7
- http://localhost:3001/user-service/7/score?score=100
- http://localhost:3001/order-service/123abc
- http://localhost:3001/order-service/
zuul + turbine 聚合监控
修改 turbine 项目,聚合 zuul 服务实例
添加zuul服务
在turbine的application.yml配置文件中配置
spring:
application:
name: turbin
server:
port: 5001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
turbine:
app-config: order-service, zuul
cluster-name-expression: new String("default")
使用hystrix仪表盘, 对 turbine 监控端点进行监控, 此端点聚合了订单服务和zull网关服务的监控数据
http://localhost:5001/turbine.stream
熔断测试
ab -n 20000 -c 50 http://localhost:3001/order-service/123abc
zuul和feign的比较
- 都能做远程调用,都可以集成ribbon 和 hystrix
- zuul 在最前面使用,做统一调用入口,而feign用在服务之间
- zuul不推荐开启重试,feign不推荐开启hystrix(会造成程序的混乱)