SpringCloud微服务组件-微服务网关zuul
一 定义
zuul:Zuul可以作为springcloud微服务的网关来使用,他可以实现动态路由、过滤器等功能。
动态路由:动态的把用户请求分配到不同的后端微服务。
过滤器:校验用户请求的合法性,类似拦截器。
二 搭建网关微服务
2.1 创建springboot工程
2.2 引入zuul依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
2.3启动类开启zuul网关注解
@EnableZuulProxy // @EnableZuulProxy可以说是@EnableZuulServer的增强版,当Zuul与Eureka、Ribbon等组件配合使用时,则使用@EnableZuulProxy
//@EnableZuulServer
2.4 配置路由
路由的目的就是把用户端过来的请求分配到对应的不同的微服务中去处理。(熟悉nginx的话,那么这其实就是反向代理)
在yml中配置路由规则
# 路由规则
zuul:
routes:
service-article: # 配置的微服务路由id,微服务实例id
path: /service-article/** # 请求路径(前缀),匹配规则:localhost:6000/article-service/abc
url: http://192.168.1.4:8001 # 转发请求到指定微服务的ip地址
prefix: /api # 前缀
请求网关的规则:
http://[网关地址]:[端口号]/[前缀]/[微服务id]/[请求路径]
三,常用功能
3.1 配置微服务实例路由
目前我们配置的zuul的url为url: http://192.168.1.4:8001,如此去进行访问的话其实不太好,耦合度也大,我要想feign那样,写上服务的实例名就更好了,因为ip会变,而实例名不会变。
zuul可以根据我们填写的微服务实例名获得他的注册ip地址,如此一来我们就需要把zuul结合并注册到eureka中去了
配置步骤:
1.把zuul微服务注册到eureka注册中心去(具体注册方式参考SpringCloud微服务组件-注册中心eureka)
2.修改zuul的转发地址为
# 路由规则
zuul:
routes:
service-article: # 配置的微服务路由id,微服务实例id
path: /service-article/** # 请求路径(前缀),匹配规则:localhost:6000/article-service/abc
service-id: service-article # 微服务实例id
# url: http://192.168.1.4:8001 # 转发请求到指定微服务的ip地址
prefix: /api # 前缀
2.1 简化转发配置
# 路由规则
zuul:
routes:
# 由于路由id和微服务id相同,那么转发配置可以简化
service-article: /service-article/**
3.2 过滤器
当我们使用zuul后,那么所有前端发来的请求必定会经过zuul这个网关,需要注意,微服务之间通信不会经过zuul,还是用的feign。整个架构图可以参考如下:
那么这个时候,如果有一些权限方面的控制,我们完全可以在网关这里控制,这就是zuul的过滤拦截作用。
3.2.1 构建一个最简单的过滤器:
/**
* 构建zuul的自定义过滤器
*/
@Component
public class MyFilter extends ZuulFilter {
/**
* 定义过滤器类型
* pre: 在请求被路由之前执行
* route: 在路由请求的时候执行
* post: 请求被路由以后执行
* error: 处理请求时发生错误执行
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 过滤器的执行顺序,可以配置多个过滤器
* 执行顺序从小到大
* @return
*/
@Override
public int filterOrder() {
return 1;
}
/**
* 是否开启过滤器
* true:使用
* false:禁用
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器业务实现
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
System.out.println("display pre zuul filter...");
return null;
}
}
3.2.2 实例:限制ip黑名单的频繁请求
开发步骤:
1.在yml中设置基本参数:
blackIP:
continueCounts: ${counts:10} # ip连续请求的次数
timeInterval: ${interval:10} # ip判断的时间间隔,单位:秒
limitTimes: ${times:15} # 限制的时间,单位:秒
2.完善过滤器的业务开发:
@Override
public Object run() throws ZuulException {
System.out.println("执行【IP黑名单】Zuul过滤器...");
// 获得上下文对象requestContext
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
// 获得ip
String ip = IPUtil.getRequestIp(request);
/**
* 需求:
* 判断ip在10秒内请求的次数是否超过10次,
* 如果超过,则限制访问15秒,15秒过后再放行
*/
final String ipRedisKey = "zuul-ip:" + ip;
final String ipRedisLimitKey = "zuul-ip-limit:" + ip;
// 获得剩余的限制时间
long limitLeftTime = redis.ttl(ipRedisLimitKey);
// 如果剩余时间还存在,说明这个ip不能访问,继续等待
if (limitLeftTime > 0) {
stopRequest(requestContext);
return null;
}
// 在redis中累加ip的请求访问次数
long requestCounts = redis.increment(ipRedisKey, 1);
// 从0开始计算请求次数,初期访问为1,则设置过期时间,也就是连续请求的间隔时间
if (requestCounts == 1) {
redis.expire(ipRedisKey, timeInterval);
}
// 如果还能取得到请求次数,说明用户连续请求的次数落在10秒内
// 一旦请求次数超过了连续访问的次数,则需要限制这个ip了
if (requestCounts > continueCounts) {
// 限制ip访问一段时间
redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);
stopRequest(requestContext);
}
return null;
}
private void stopRequest(RequestContext requestContext){
// 停止继续向下路由,禁止请求通信
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(200);
String result = JsonUtils.objectToJson(
GraceJSONResult.errorCustom(
ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP));
requestContext.setResponseBody(result);
requestContext.getResponse().setCharacterEncoding("utf-8");
requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_VALUE);
}