Zuul网关
网关
完成一个复杂的业务,可能需要多个微服务合作来完成,比如下单,需要用户服务,支付服务,地图服务,订单服务
一般是我们对外服务的窗口,进行服务内外隔离
一般微服务都在内网,不做安全验证
微服务没有网关,会有下面的问题
- 客户端请求多个微服务,增加了客户端复杂性,每个微服务都要做用户认证,限流等,避免和多个微服务打交道的复杂性
- 有跨域问题,不在同一个域
- 认证复杂,每个服务都要独立认证,服务要求的权限不一致
- 难以重构。因为微服务被客户端调用着,重构难以实施
网关是介于客户端(外部调用方比如app,h5)和微服务的中间层
Zuul是Netflix开源的微服务网关,核心是一系列过滤器
- 是所有微服务入口,进行分发
- 身份认证与安全,识别合法的请求,拦截不合法的请求
- 监控,在入口处监控,更全面
- 动态路由,动态将请求分发到不同的后端集群
- 压力测试,可以逐渐增加对后端服务的流量,进行测试
- 负载均衡,也是用ribbon
- 限流,比如每秒只要1000次,10001次就不让访问
zuul默认集成了:ribbon和hystrix
Zuul使用
官方地址:https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-zuul
引入依赖
<!--zuul --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency>
启动类添加注解
该注解声明这是一个zuul代理,该代理使用Ribbon来定位注册到eureka server上的微服务,同时,整合了hystrix,实现了容错。
@EnableZuulProxy
yml配置
普通配置,端口,应用名,eureka地址
server: port: 9000 spring: application: name: online-taxi-zuul
注册中心
eureka:
client:
#设置服务注册中心的URL
service-url:
defaultZone: http://root:root@eureka-7901:7901/eureka/
instance:
hostname: localhost
instance-id: online-taxi-zuul
网关会将服务名转换成具体服务的ip和端口,实际进行访问。
网关一般命名
https://域名/v1/sms/路径
看高德开放平台:https://lbs.amap.com/api/webservice/guide/api/geofence_service#t6
路由端点
zuul yml中
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
##默认是never
show-details: ALWAYS
enabled: true
routes:
enabled: true
结果中是,eureka中有的实例的网关,和自己配置的映射
如果eureka中没有实例,则只有自己配置的映射。
过滤器端点
- error: [],
- post: [],
- pre: [],
- route: []
配置指定微服务的访问路径
通过服务名配置(虚拟主机名)
zuul:
routes:
api-driver: /zuul-api-driver/**
配置前先访问,然后做对比
这样访问
http://localhost:9100/zuul-api-driver/test/hello
自定义命名配置
zuul:
routes:
custom-zuul-name: #此处名字随便取
path: /zuul-custom-name/**
url: http://localhost:9002/
访问前 看结果,做对比。
访问:http://localhost:9100/zuul-custom-name/test/hello
这样 ribbon和hystrix就都失效了
基于上述操作,恢复ribbon+hystrix
zuul:
routes:
#此处名字随便取
custom-zuul-name:
path: /zuul-custom-name/**
service-id: no-eureka-api-driver
no-eureka-api-driver:
ribbon:
listOfServers: localhost:9003,localhost:9002
ribbon:
eureka:
enabled: false
访问:http://localhost:9100/zuul-custom-name/test/hello
指定serviceId
zuul:
routes:
#此处名字随便取
custom-zuul-name:
path: /zuul-custom-name/**
service-id: api-driver
访问:http://localhost:9100/zuul-custom-name/test/hello
忽略微服务
原来访问:
http://localhost:9100/api-driver/test/hello
http://localhost:9100/zuul-api-driver/test/hello
忽略微服务数组
增加如下配置
zuul:
routes:
api-driver: /zuul-api-driver/**
ignored-services:
- api-driver
再访问:
http://localhost:9100/api-driver/test/hello , 不好使
http://localhost:9100/zuul-api-driver/test/hello, 好使
忽略正则
# 忽略正则,不能通过 zuul-api-driver 和 api-driver访问。
# ignored-patterns:
# - /*-driver/**
可以测试一下
http://localhost:9100/zuul-api-driver/test/hello,不好使
http://localhost:9100/api-driver/test/hello ,不好使
前缀
接口一般命名:/api/v1/xxxx
zuul:
prefix: /api
# 是否移除前缀
strip-prefix: true
访问时带上前缀,实际请求会将前缀去掉
比如访问:http://localhost:9100/api/zuul-api-driver/test/hello
实际:http://localhost:9002/test/hello
注意全局的移除,和自定义名字下面的移除。
即zuul下的移除,和custom-zuul-name2: 下面的移除
查看路由日志
关键点找 200,最后几行。看到路由成功,找到了目的地。
2020-02-19 15:36:41.269 DEBUG [online-taxi-zuul,,,] 21456 --- [imer-api-driver] c.netflix.loadbalancer.BaseLoadBalancer : LoadBalancer: PingTask executing [1] servers configured 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.web.servlet.DispatcherServlet : GET "/zuul-api-driver/test/hello", parameters={} 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : route matched=ZuulRoute{id='gate-way/sms', path='/gate-way/sms/**', serviceId='service-sms', url='null', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, } 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : route matched=ZuulRoute{id='zuul-api-driver', path='/zuul-api-driver/**', serviceId='api-driver', url='null', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, } 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : route matched=ZuulRoute{id='api-passenger', path='/api-passenger/**', serviceId='api-passenger', url='null', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, } 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : route matched=ZuulRoute{id='api-driver', path='/api-driver/**', serviceId='api-driver', url='null', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, } 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped to org.springframework.cloud.netflix.zuul.web.ZuulController@4dcf34e0 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] c.n.zuul.http.HttpServletRequestWrapper : Path = null 2020-02-19 15:36:41.590 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] c.n.zuul.http.HttpServletRequestWrapper : Transfer-Encoding = null 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] c.n.zuul.http.HttpServletRequestWrapper : Content-Encoding = null 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] c.n.zuul.http.HttpServletRequestWrapper : Content-Length header = -1 来源uri:/zuul-api-driver/test/hello 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : Finding route for path: /zuul-api-driver/test/hello 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : servletPath=/ 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : zuulServletPath=/zuul 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : RequestUtils.isDispatcherServletRequest()=true 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : RequestUtils.isZuulServletRequest()=false 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : adjustedPath=/zuul-api-driver/test/hello 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : Matching pattern:/gate-way/sms/** 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : Matching pattern:/zuul-api-driver/** 2020-02-19 15:36:41.591 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.c.n.zuul.filters.SimpleRouteLocator : route matched=ZuulRoute{id='zuul-api-driver', path='/zuul-api-driver/**', serviceId='api-driver', url='null', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, } 2020-02-19 15:36:41.592 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] c.n.loadbalancer.ZoneAwareLoadBalancer : Zone aware logic disabled or there is only one zone 2020-02-19 15:36:41.592 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] c.n.loadbalancer.LoadBalancerContext : api-driver using LB returned Server: localhost:9002 for request /test/hello 2020-02-19 15:36:41.602 DEBUG [online-taxi-zuul,c121eba852592312,c121eba852592312,true] 21456 --- [nio-9100-exec-9] o.s.web.servlet.DispatcherServlet : Completed 200 OK
敏感Header
敏感的header不会传播到下游去,也就是说此处的token不会传播的其它的微服务中去。
zuul:
# 一下配置,表示忽略下面的值向微服务传播,以下配置为空表示:所有请求头都透传到后面微服务。
sensitive-headers: token
访问网关token为null。
过滤器
Zuul的大部分功能都是有过滤器实现的
4种过滤器
- PRE: 在请求被路由之前调用,可利用这种过滤器实现身份验证,选择微服务,记录日志
- ROUTING:在将请求路由到微服务调用,用于构建发送给微服务的请求,并用http clinet(或者ribbon)请求微服务
- POST:在调用微服务执行后,可用于添加header,记录日志,将响应发给客户端
- ERROR:在其他阶段发生错误时,走此过滤器
自定义过滤器
- PreFilter看代码,注意下面4点。
- filterType: pre,routing, post, error
- filterOrder: 执行顺序,在谁前,在谁后,可以+1,-1
- shouldFilter: 此过滤器是否执行,true false,可以写过滤器是否执行的判断条件。
- run: 具体执行逻辑
访问: yapi中 网关token
pre来源uri:/api-driver/test/token
pre拦截
pre 业务逻辑 token:msb coolie
说一下AuthFilter。利用filter实现了鉴权, 看代码
接口容错
@Component public class MsbFallback implements FallbackProvider{ /** * 表明为哪个微服务提供回退 * 服务Id ,若需要所有服务调用都支持回退,返回null 或者 * 即可 */ @Override public String getRoute() { // TODO Auto-generated method stub return "*"; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { if (cause instanceof HystrixTimeoutException) { return response(HttpStatus.GATEWAY_TIMEOUT); } else { return response(HttpStatus.INTERNAL_SERVER_ERROR); } } private ClientHttpResponse response(final HttpStatus status) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { //return status; return HttpStatus.BAD_REQUEST; } @Override public int getRawStatusCode() throws IOException { //return status.value(); return HttpStatus.BAD_REQUEST.value(); } @Override public String getStatusText() throws IOException { //return status.getReasonPhrase(); //return HttpStatus.BAD_REQUEST.name(); return HttpStatus.BAD_REQUEST.getReasonPhrase(); } @Override public void close() { } @Override public InputStream getBody() throws IOException { String msg = "{\"msg\":\"服务故障\"}"; return new ByteArrayInputStream(msg.getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
选用基础yml,启动eureka,api-driver, online-taxi-zuul
正常启动,正常访问yapi 网关token,正常;停止api-driver,则走了容错方法
将fallback的改成
@Override public String getRoute() { // TODO Auto-generated method stub // return "*"; return "api-passenger"; }
在访问上面 yapi 中 zuul,中网关token,则报500
再改成
@Override public String getRoute() { // TODO Auto-generated method stub // return "*"; // return "api-passenger"; return "api-driver"; }
重写访问,熔断生效
限流
保护自己,用ratelimit
令牌桶
- 假设进入高速公路的车辆需要在入口处领取到通行卡才能进入高速公路,为了节约人力成本,入口处放置自动出卡机
- 按照国家高速公路交通安全法的规定,在高速公路上行驶的车辆,车速超过100km/h时,应与同车道前车保持100米以上距离
- 为了保持最小安全行车距离100米,按车速100km/h计算,需要间隔至少3.6秒才能放行一辆车,因此出卡机每隔3.6秒出一张通行卡
- 在自动出卡机下放置一个盒子,自动出卡机按照3.6秒的间隔向盒子中投放通行卡
- 每辆进入高速公路的车辆,从盒子中领取通行卡之后才可以进入高速公路
令牌桶可以看作是一个存放一定数量令牌的容器
系统按设定的速度向桶中放置令牌
当桶中令牌满时,多出的令牌溢出,桶中令牌不再增加
在使用令牌桶对流量规格进行评估时,是以令牌桶中的令牌数量是否足够满足报文的转发为依据的
每个需要被转发的报文,都要从令牌桶中领取一定数量的令牌(具体数量视报文大小而定),才可以被正常转发
如果桶中存在足够的令牌可以用来转发报文,称流量遵守或符合约定值,否则称为不符合或超标。
单独限流
高可用,一般做法,前面架上nginx
zuul作为普通的服务,对外访问,前面加一层(nginx+keepalived)
睁开眼,书在面前
闭上眼,书在心里