Zuul服务网关介绍
Zuul 主要提供了对请求的路由与过滤功能。
- 路由:将外部请求转发到具体的微服务实例上,是外部访问微服务的统一入口。
- 过滤:对请求的处理过程进行干预,对请求进行校验、 鉴权等处理。
搭建网关项目
1,创建springboot工程,导入依赖
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.RELEASE</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>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>
2,启动类添加注解
//开启zuul代理模式
@EnableZuulProxy
3,修改配置文件
server:
port: 8090
spring:
application:
name: zuulservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka
到这里,一个网关服务就可以启动了,网关服务从eureka获取服务列表,用户访问所有服务都走网关:http://127.0.0.1:8090/微服务名称/请求路径
比如 http://127.0.0.1:8090/consumer-depart/consumer/depart/list
配置网关路由
zuul:
routes:
#自定义路径来替代微服务名称 比如 http://127.0.0.1:8090/con/consumer/depart/list
#配置路由规则
# /** 匹配任意多级路径 /abc8080/xxx/ooo/jjj
# /* 仅可匹配一级路径 /abc8080/xxx
# /? 仅可匹配一级路径,但该路径只能是一个字符 /abc8080/x
consumer-depart: /con/**
#配置路径前辍 http://127.0.0.1:8090/abc/con/consumer/depart/list
prefix: /abc
#屏蔽通过指定微服务名称访问
# ignored-services: consumer-depart
#屏蔽所有微服务名称访问
ignored-services: "*"
#屏蔽指定路径
ignored-patterns: /**/list/**
#默认情况下,Cookie、Set-Cookie、Authorization 敏感请求头信息会被 zuul 屏蔽掉
#屏蔽指定的请求头,设置值替换原有屏蔽的三个信息,多个以逗号分隔或者["",""]
sensitive-headers: Authorization
负载均衡策略
Zuul内置有ribbon,在网关配置负载均衡策略,可以yml配置或者代码里
// 设置负载均衡算法为"随机算法"
@Bean
public IRule loadBalanceRule() {
return new RandomRule();
}
服务降级
/**
* zuul的服务降级类
*/
@Component
public class ConsumerFallback implements FallbackProvider {
// 指定要进行服务降级的消费者微服务名称
@Override
public String getRoute() {
// 对所有微服务开启降级功能
return "*";
// 仅对指定的微服务进行降级
// return "abcmsc-consumer-depart-8080";
}
/**
* 定义降级响应
* @param route 指当前正在处理的微服务名称
* @param cause
* @return
*/
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
// 对"abcmsc-consumer-depart-8080"不进行服务降级
if("abcmsc-consumer-depart-8080".equals(route)) return null;
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
// 返回状态常量
return HttpStatus.SERVICE_UNAVAILABLE;
}
@Override
public int getRawStatusCode() throws IOException {
// 返回状态码,这里为503
return HttpStatus.SERVICE_UNAVAILABLE.value();
}
@Override
public String getStatusText() throws IOException {
// 返回状态短语,这里为Service Unavailable
return HttpStatus.SERVICE_UNAVAILABLE.getReasonPhrase();
}
// 关闭当前的Response
@Override
public void close() { }
// 定义响应体
@Override
public InputStream getBody() throws IOException {
// 指定降级信息
String msg = "fallback:" + route;
return new ByteArrayInputStream(msg.getBytes());
}
// 定制响应头
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
配置网关过滤
/**
* zuul过滤器,/con 服务必须有token请求头
*/
@Component
@Slf4j
public class RouteFilter extends ZuulFilter {
// 设置过滤类型
@Override
public String filterType() {
// 指定在路由之前进行过滤
return FilterConstants.PRE_TYPE;
}
// 设置当前过滤器在所有过滤器中执行的顺序
// 数字越小优先级越高,可以是负数
@Override
public int filterOrder() {
return -5;
}
// 通过过滤后要执行的业务逻辑
@Override
public Object run() throws ZuulException {
log.info("通过过滤");
return null;
}
// 真正的过滤规则就是定义在这里的
// 返回true,则通过过滤
@Override
public boolean shouldFilter() {
// 获取请求上下文
RequestContext context = RequestContext.getCurrentContext();
// 从请求上下文中获取请求
HttpServletRequest request = context.getRequest();
// 从请求中获取请求参数与uri
String token = request.getHeader("token");
String uri = request.getRequestURI();
// 若/con的访问中没有携带token头信息,则无法通过过滤
if(uri.contains("/con") && StringUtils.isEmpty(token)) {
log.warn("未授权");
// 设置没有通过过滤
context.setSendZuulResponse(false);
// 返回的状态码
context.setResponseStatusCode(HttpStatus.SC_UNAUTHORIZED);
// 返回false,不会调用run()方法了
return false;
}
return true;
}
}
配置网关限流
通过对请求限流的方式避免系统遭受“雪崩之灾”。
令牌桶限流
流量限制参考 https://www.jianshu.com/p/0cbf1a22a6d1
@Component
@Slf4j
public class BucketRouteFilter extends ZuulFilter {
//根据指定的稳定吞吐率创建RateLimiter,这里的吞吐率是指每秒多少许可数(通常是指QPS,每秒多少查询)
//测试用,每秒产生2个令牌,每秒超过两个请求就限流
private static final RateLimiter RATE_LIMITER = RateLimiter.create(2);
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return -5;
}
@Override
public Object run() throws ZuulException {
log.info("通过过滤");
return null;
}
/**
* 过滤、限流的实现
* @return
*/
@Override
public boolean shouldFilter() {
RequestContext context = RequestContext.getCurrentContext();
//tryAcquire尝试获取1个permit,默认超时时间是0,意思是拿不到就立即返回false,
//可设置请求超时时间 RATE_LIMITER.tryAcquire(1, TimeUnit.SECONDS)
if(!RATE_LIMITER.tryAcquire()) {
log.warn("访问量超载");
context.setSendZuulResponse(false);
context.setResponseStatusCode(429);
context.setResponseBody("服务器繁忙");
return false;
}
return true;
}
}
zuul推荐限流方式(spring-cloud-zuul-ratelimit)
源码地址 https://github.com/qiuweili123/spring-cloud-zuul-ratelimit
<!-- spring-cloud-zuul-ratelimit依赖 -->
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
限流策略(限流查验的对象)类型有:
- user:针对用户的限流,即对单位时间窗内经过网关的用户数量进行限制
- origin:针对客户端 IP 的限流,即对单位时间窗内经过网关的 IP 数量进行限制
- url:针对请求 URL 的限流,即对单位时间窗内经过网关的请求数量进行限制
zuul:
routes:
consumer-service: /aaa/**
# 对限流策略进行配置
ratelimit:
# 开启zuul限流
enabled: true
repository: redis
#默认 - 针对所有的路由配置的策略,除非特别配置了policy-list:
default-policy-list:
#可选 - 每个刷新时间窗口对应的请求数量限制
limit: 10
#可选- 每个刷新时间窗口对应的请求时间限制(秒)
# quota: 1000
# 刷新时间窗口的时间,默认值 (秒)
refresh-interval: 60
#可选 限流方式
type:
- user
- origin
- url
#指定特定服务的限流策略,覆盖默认
policy-list:
myServiceId:
limit: 10
quota: 1000
refresh-interval: 60
type:
- user
限流触发的提示页面
在 src/main/resources 目录下再定义新的目录 public/error,必须是这个目录名称。
type:
- user
- origin
- url
#指定特定服务的限流策略,覆盖默认
policy-list:
myServiceId:
limit: 10
quota: 1000
refresh-interval: 60
type:
- user
限流触发的提示页面
在 src/main/resources 目录下再定义新的目录 public/error,必须是这个目录名称。
在该目录中定义一个异常处理页面,名称必须是异常状态码,扩展名必须为 html。