为什么微服务需要网关
在微服务架构中,通常会有多个服务提供者。设想一个电商系统,可能会有商品、订单、支付、用户等多个类型的服务,而每个类型的服务数量也会随着整个系统体量的增大也会随之增长和变更。作为UI端,在展示页面时可能需要从多个微服务中聚合数据,而且服务的划分位置结构可能会有所改变。网关就可以对外暴露聚合API,屏蔽内部微服务的微小变动,保持整个系统的稳定性。
当然这只是网关众多功能中的一部分,它还可以做负载均衡,统一鉴权,协议转换,监控监测等一系列功能。
- 统一安全认证
- 统一限流
- 统一降级
- 统一异常处理
- 统一请求统计
- 统一超时

Zuul
用一张图过一下zuul的核心原理,Zuul是一个责任链模式

过滤器前面的数字,表示执行的时候的顺序
pre过滤器
routing过滤器
post过滤器
error过滤器
基本用法
路由规则配置
简单路由
SimpleHostRoutingFilter 往指定的地址路由
1 2 3 4 5 6 7
| zuul.routes.demo.path=/ServiceB/** zuul.routes.demo.url=http://localhost:9090/ServiceB
zuul.host.maxTotalConnections=200
zuul.host.maxPerRouteConnections=20
|
跳转路由
SendForwardFilter 往自己的接口跳转
1 2 3
| zuul.routes.demo.path=/test/** zuul.routes.demo.url=forward: /gateway/sayHello
|
Ribbon路由
RibbonRoutingFilter 基于Ribbon的服务路由
1 2 3 4 5 6 7
| zuul.routes.ServiceB.path=/demo/** zuul.routes.ServiceB.serviceId=ServiceB
zuul.routes.ServiceB.path=/demo/**
|
自定义路由规则
请求:test/**的路径,转发给zuul-test-service
1 2 3 4 5 6 7
| @Configuration public class MyRouteRuleConfig { @Bean public PatternServiceRouteMapper patternServiceRouteMapper() { return new PatternServiceRouteMapper("(zuul)-(?<test>.+)-(service)", "${test}/**"); } }
|
忽略路由
1
| zuul.ignoredPatterns=/ServiceB/test
|
其他配置
请求头配置
默认情况下,zuul有些敏感的请求头不会转发给下游的服务,比如:Cookie、Set-Cookie、Authorization,也可以自己配置敏感请求头
1 2
| zuul.sensitiveHeaders=accept-language, cookie zuul.routes.demo.sensitiveHeaders=cookie
|
路由映射信息
在Zuul项目中,引入actuator依赖,然后在配置文件中,将management.security.enabled
设置为false,就可以访问/actuator/routes
地址,然后可以看到路由的映射信息
hystrix配置
与Ribbon整合的时候,会使用Hystrix,可以在网关写统一的降级实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class ServiceBFallbackProvider implements ZuulFallbackProvider {
public String getRoute() { return "*"; }
public ClientHttpResponse fallbackResponse() { return new ClientHttpResponse() { } public InputStream getBody() throws IOException { return new ByteArrayInputStream("fallback".getBytes()); }
public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_PLAIN); return headers; }
public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; }
public int getRawStatusCode() throws IOException { return 200; }
public String getStatusText() throws IOException { return "OK"; }
public void close() {
} }
@Configuration public class FallbackConfig {
@Bean public ZuulFallbackProvider fallbackProvider() { return new ServiceBFallbackProvider(); } }
|
ribbon客户端预加载
默认情况下,第一次请求zuul才会初始化ribbon客户端,所以可以配置预加载
1
| zuul.ribbon.eager-load.enabled=true
|
超时配置
Zuul用的是ribbon+Hystrix,所以设置超时得考虑这2个组件的配置,而且hystrix的超时要考虑ribbon的重试次数和单次超时时间。
hystrix的超时时间计算公式如下:
(ribbon.ConnectTimeout + ribbon.ReadTimeout) * (ribbon.MaxAutoRetries + 1) * (ribbon.MaxAutoRetriesNextServer + 1)
1 2 3 4
| ribbon.ReadTimeout=100 ribbon.ConnectTimeout=500 ribbon.MaxAutoRetries=1 ribbon.MaxAutoRetriesNextServer=1
|
如果不配置ribbon的超时时间,默认的hystrix超时时间是4000ms
过滤器
自定义过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class MyFilter extends ZuulFilter { public boolean shouldFilter() { return true; }
publici Object run() { System.out.println("执行过滤器"); return null; }
public String filterType() { return FilterConstants.ROUTE_TYPE; }
public int filterOrder() { return 1; } }
@Configuration public class FilterConfig {
@Bean public MyFilter myFilter() { return new MyFilter(); } }
|
groovy动态加载过滤器
加入依赖
1 2 3 4 5
| <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.4.12</version> </dependency>
|
增加代码配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @PostConstruct public void zuulInit() { FilterLoader.getInstance().setCompiler(new GroovyCompiler()); String scriptRoot = System.getProperty("zuul.filter.root", "groovy/filters"); String refreshInterval = System.getProperty("zuul.filter.refreshInterval", "5"); if(scriptRoot.length() > 0) { scriptRoot = scriptRoot + File.separator; } try { FilterFileManager.setFilenameFilter(new GroovyFileFilter()); FilterFileManager.init(Integer.parseInt(refreshInterval), scriptRoot + "pre", scriptRoot + "route", scriptRoot + "post"); } catch(Exception e) { throw new RuntimeException(e); } }
|
1 2
| zuul.filter.root=groovy/filters zuul.filter.refreshInterval=5
|
然后将groovy编写的过滤器,放在groovy/filters
文件夹下,几秒后就会生效。
禁用过滤器
1
| zuul.SendForwardFilter.route.disable=true
|
@EnableZuulServer
会自动禁用掉PreDecorationFilter、RibbonRoutingFilter、SimpleHostRoutingFilter等过滤器
error过滤器
在自定义的过滤器有异常可以抛一个ZuulException,然后写一个MyErrorController,继承BasicErrorController,统一处理异常。