springcloud学习记录-zuul
Zuul的主要功能是作为网关进行路由转发和过滤。
核心功能包括:
(1)身份认证和安全:识别每一个资源的验证要求,审查和监控
(2)动态路由:动态将请求路由到不同后端集群
(3)压力测试:增加指定集群流量,检测性能
(4)负载分配:为不同负载类型分配对应容量,超过容量时自动丢弃
Zuul和Eureka结合,将Zuul自身注册为Eureka服务治理下的客户端应用,同时将从Eureka中获得其他微服务的关键信息,即微服务的访问都是通过Zuul跳转后获得。
项目搭建
1.新建module:service_zuul,选择zuul依赖。生成的pom.xml依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<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>
</dependency>
<!--zuul路由重试功能,在yml文件中进行配置:retryable: true-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
</dependencies>
2.在启动类上添加:@EnableZuulProxy
@SpringBootApplication
@EnableZuulProxy
public class ServiceZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceZuulApplication.class, args);
}
3.yml配置:注意是boostrap.yml
server:
port: 9000
spring:
application:
name: service-zuul
eureka:
client:
serviceUrl:
defaultZone: http://eureka1:8761/eureka/
# defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/,http://eureka3:8763/eureka/
zuul:
# ignored-services: service-ribbon,service-feign #忽略该服务,不代理
ignored-services: "*" #写*就是不忽略
# ignored-patterns: # 禁止路由,防止外部调用,只能服务间调用
# - /**/haaaa #这样子外部就不能访问了
# - /**/yyyyyy #可以写多个
prefix: /test # 统一公共前缀
# stripPrefix: false # 取消zuul初始化的路径代理,使用routes自定义方式,自己测试失败,不会用!!!!
routes:
api-aaaaaa: #1.自定义规则的名字.随意的
path: /myconsumer/** #2.自定义的url路径 ,可以通过:进行访问了http://localhost:9000/myconsumer/hi?message=1就会转发到http://localhost:9000/eurekaconsumer1/hi?message=1
serviceId: eurekaconsumer1 #3.访问的服务的名称:原来访问路径http://localhost:9000/eurekaconsumer1/hi?message=1(现在也可以)
# url: http://eurekaconsumer1/users_service #后端服务的位置既可以使用serviceId也可以使用url(物理位置)指定
sensitiveHeaders: #默认是过滤了cookie的,在sensitiveHeaders后面不写为不过滤cookie
# eurekaconsumer1: /myconsumer/** #自定义路径的简介写法,作用和上面1,2,3行加起来的作用的一样的
# api-bbbbbb: #可同时配置多个
# path: /myconsumer/**
# serviceId: eurekaconsumer1
# sensitiveHeaders:
retryable: true # zuul重试一次 #是否开启重试功能
# #对当前服务的重试次数
# ribbon.MaxAutoRetries=2
# #切换相同Server的次数
# ribbon.MaxAutoRetriesNextServer=0
MyPreFilter: #禁用filter:MyPreFilter
pre: #MyPreFilter的类型
disable: true #true为禁用
MyPostFilter:
post:
disable: true
MyRouteFilter:
route:
disable: true
#目前测试的时候发现在服务都成功启动的时候第一次访问会有报错的情况发生,但是之后又恢复正常访问
#主要是Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,
#这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现上面所描述的显现。
ribbon:
eager-load:
enabled: true #Ribbon配置饥饿加载(最佳)
# clients: clients1,clients2,clients3 #指定需要饥饿加载的服务名
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 #超时时间
yml配置中包含了一些其他的相关参数
4.zuul还有过滤的功能
ZuulFilter抽象类:
filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
- pre:请求到达路由之前
- routing:路由请求之时
- post: 最后被调用
- error:处理请求错误时调用
filterOrder:过滤的顺序
shouldFilter:这里可以写逻辑判断,是否要过滤,true表示永远过滤。
run:过滤器的具体逻辑。可以很复杂,包括查sql,nosql去判断该请求到底有没有权限访问等。
禁用过滤器:zuul.<过滤器名>.<过滤器类型>.disable: true
举个例子:
@Component
public class MyPreFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(MyPreFilter.class);
@Override
public String filterType() { //过滤器类型
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() { //过滤器执行的顺序
return 0;
}
@Override
public boolean shouldFilter() { //表示过滤器是否生效
return false;
}
/**
* 过滤的业务逻辑
* 例如:判断是否有token参数
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext(); //获取上下文环境
ctx.set("startTime", System.currentTimeMillis());
HttpServletRequest request = ctx.getRequest(); //获取request对象
log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
String accessToken = request.getParameter("token");
if(!accessToken.equals("123")) {
log.warn("当前请求没有token");
ctx.setSendZuulResponse(false); //不会继续执行,直接响应给客户端
ctx.setResponseStatusCode(401);
// try {
// ctx.getResponse().getWriter().write("token is empty or there is not token");
// }catch (Exception e){}
ctx.setResponseBody("{\"result\":\"token不存在或为空!\"}");
ctx.getResponse().setContentType("text/html;charset=UTF-8");
return null;
}
log.info("请求通过过滤");
return null;
}
}
目录结构如下:
原本访问consumer:http://localhost:8011/hi?message=1进行路由之后:
进行访问了http://localhost:9000/myconsumer/hi?message=1就会转发到http://localhost:9000/eurekaconsumer1/hi?message=1
注意:
(1)cookie:默认情况下,Zuul会过滤HTTP请求信息中的敏感信息,防止传递到下游其他服务器上,但如果使用Security等安全框架,需要获得cookie值做认证,需要设置zuul.sensitiveHeaders;
(2)@EnableZuulProxy可以使用代理模式上传小文件,对于大文件,需要在路径前加上“/zuul/”,绕过Spring的DispatcherServlet,避免multipart过程,经过zuul.servletPath传递原始文件数据,另外,如果打开了Ribbon,则需要增大超时时间(读取时间)。