网关
Starter阿里云镜像
概念
微服务基本模块已经有了,也可以做微服务了。但完成一个复杂的业务,可能需要多个微服务合作来完成,比如下单,需要用户服务,支付服务,地图服务,订单服务。一般是我们对外服务的窗口,进行服务内外隔离。一般微服务都在内网,不做安全验证,
就好像:很多明星,可以独立开演唱会(独立提供服务)。也可以去春晚(微服务群提供服务)。但一台春晚就不能让 观众一个一个调用了。观众要调用,需要检票啥的,检票就类似于网关,进来之后,界面随便看,不会说你 看个小品,还需要再检票。
微服务没有网关,会有下面的问题:
客户端请求多个微服务,增加了客户端复杂性,每个微服务都要做用户认证,限流等,避免和多个微服务打交道的复杂性。
有跨域问题,不在同一个域。
认证复杂,每个服务都要独立认证,服务要求的权限不一致。
难以重构。因为微服务被客户端调用着,重构难以实施。
网关是介于客户端(外部调用方比如app,h5)和微服务的中间层。
Zuul是Netflix开源的微服务网关,核心是一系列过滤器。这些过滤器可以完成以下功能。
- 是所有微服务入口,进行分发。
- 身份认证与安全。识别合法的请求,拦截不合法的请求。
- 监控。在入口处监控,更全面。
- 动态路由。动态将请求分发到不同的后端集群。
- 压力测试。可以逐渐增加对后端服务的流量,进行测试。
- 负载均衡。也是用ribbon。
- 限流(望京超市)。比如我每秒只要1000次,10001次就不让访问了。
- 服务熔断
网关和服务的关系:演员和剧场检票人员的关系。
zuul默认集成了:ribbon和hystrix。
启用网关
新建项目引入依赖
1 | <dependency> |
配置文件
1 | eureka.client.service-url.defaultZone=http://euk1.com:7001/eureka/ |
启动类
1 | @EnableZuulProxy |
测试访问
网关会将服务名转换成具体服务的ip和端口,实际进行访问
1 | http://localhost/consumer/alive |
负载均衡
启动两个Consumer
轮询访问上面地址,会看到返回结果中,端口一直轮询在变。说明负载均衡生效了,默认是轮询
1 | consumer.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule |
路由端点
调试的时候,看网关请求的地址,以及 映射是否正确。网关请求有误时,可以通过此处排查错误。
配置
1 | management.endpoints.web.exposure.include=* |
配置指定微服务的访问路径
- 通过服务名配置(虚拟主机名)
1 | zuul.routes.consumer=/xxoo/** |
配置前先访问,然后做对比。
2.自定义映射
1 | zuul.routes.xx.path=/xx/** |
- .自定义下的负载均衡
1 | zuul.routes.xx.path=/xx/** |
忽略微服务
配置
1 | zuul.ignored-services=user-provider |
前缀
1 | zuul.prefix=/api/v1 |
带上前缀请求
1 | zuul.strip-prefix=false |
高可用
Nginx + Keepalive
敏感Header
测试点:
停止一个api-driver。访问:yapi:网关token,看返回。
初始请求。返回值中token为msb cookie
加上下面配置
敏感的header不会传播到下游去,也就是说此处的token不会传播的其它的微服务中去。
1 | zuul: |
访问。网关token为null。
上面是网关的路由。
过滤器
Zuul的大部分功能都是有过滤器实现的。
4种过滤器
1 | PRE: 在请求被路由之前调用,可利用这种过滤器实现身份验证。选择微服务,记录日志。 |
自定义过滤器
1 | PreFilter看代码,注意下面4点。 |
访问:yapi中 网关token
1 | pre来源uri:/api-driver/test/token |
说一下AuthFilter。利用filter实现了 鉴权。看代码。(实际用jwt)
测试一下,
1 | // 测试路径 |
接口容错
1 | @Component |
选用基础yml
测试点:启动eureka,api-driver, online-taxi-zuul
正常启动,正常访问yapi 网关token。正常
停止api-driver。
则走了容错 方法。
将fallback的改成:
1 | @Override |
在访问上面 yapi 中 zuul,中网关token。则报500。
再改成:
1 | @Override |
重写访问,熔断生效。
最后改回去*。
限流
保护自己,用ratelimit。
令牌桶
1 | 假设进入高速公路的车辆需要在入口处领取到通行卡才能进入高速公路。为了节约人力成本,入口处放置自动出卡机。按照国家高速公路交通安全法的规定,在高速公路上行驶的车辆,车速超过100km/h时,应与同车道前车保持100米以上距离。为了保持最小安全行车距离100米,按车速100km/h计算,需要间隔至少3.6秒才能放行一辆车,因此出卡机每隔3.6秒出一张通行卡。在自动出卡机下放置一个盒子,自动出卡机按照3.6秒的间隔向盒子中投放通行卡。每辆进入高速公路的车辆,从盒子中领取通行卡之后才可以进入高速公路。 |
启动jmeter,双击:jmeter.bat
右击TestPlan,add ,Threads,Thread Group
右击测试令牌桶线程组,add,sampler, http request。
在线程组:
1、Number of Threads(users):用户个数
2、Ramp-up Period(in seconds):在多长时间内,加载指定的用户个数,单位为s。
假如需加载100个用户,在5s中之内加载完成,那么平均每秒钟加载20个用户。
3、Loop Count(循环次数):用户执行操作的循环次数,如果选择forever,则永远循环下去。
测试点:启动eureka,api-driver,online-taxi-zuul。
令牌桶设置成2,jemter 用10个并发。可以看到控制台输出结果。
单独限流。
高可用
一般做法
前面架上nginx。
zuul作为普通的服务。对外访问。前面加一层(nginx+keepalived)
第8节课完。2020.3.8
maven,
剔除。
原理
《Zuul原理流程图》
让我们做,如何实现?
方案:请求过来->pre(一组,鉴权,限流之类的。)->route(一组,路由到别的服务,具体微服务。)->post(一组,处理响应)。
zuul本质就是filter。
通过filter解析url来决定我们去访问哪个微服务。
发请求访问微服务,也是通过filter实现。
响应数据,也是通过filter实现。
源码
所有断点入口打在:
1 | ZuulServlet中service方法第一行。 |
入口开关(所有启动类上的开关,套路都一样。)
spring-cloud-netflix-zuul-2.1.3.RELEASE.jar中spring.factories
1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
点ZuulProxyAutoConfiguration进去
1 | @ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class) |
知道了@EnableZuulProxy的作用,开关。
1 | @Import(ZuulProxyMarkerConfiguration.class) |
接着:看ZuulProxyAutoConfiguration中
1 | 服务发现 |
PreDecorationFilter:解析决定使用哪种url。
RibbonRoutingFilter:向微服务发请求
SendResponseFilter:接受微服务响应,并向用户响应。
主要filter执行流程
debug 上面3个类的中的 run方法。
PreDecorationFilter
1 | public Object run() { |
RibbonRoutingFilter
1 | public Object run() { |
实际请求走的ribbon。
1 | com.netflix.loadbalancer.LoadBalancerContext |
SendResponseFilter
1 | public Object run() { |
过滤器存储
其父类ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration
1 | 在缺失zuulServlet bean的情况下注入了ZuulServlet |
过滤器执行流程
关键:(请求转发器)ZuulServlet,所有请求先到ZuulServlet
上面类注入了servlet,打开servlet,过滤器执行的关键 为什么 先pre,routing,post
1 | public class ZuulServlet extends HttpServlet |
通过上面方法,可以得出如下结论:
RequestContext贯穿整个请求filter线程。
通过service方法,可以看出整个servlet的处理流程:
pre异常: pre -> error -> post
route异常: pre -> route -> error -> post
post异常: pre -> route -> post -> error
正常: pre -> route -> post
为什么最后都要走post,因为post最后,才能直接给用户响应数据。
pre:表示路由的前置过滤器链,route:表示路由的过滤器链,post:表示路由的后置过滤器链,error:表示路由错误过滤器链。
由此可见,责任链模式是zuul的核心。
处理,增加下一个处理的节点。
Zuul责任链模式的执行顺序由filterType和filterOrder共同决定。不同的类型执行顺序为:pre过滤器 -> route过滤器 -> post过滤器。同一类型的执行顺序为:按filterOrder值大小排序,filterOrder值越小,越先执行。
通过上面,就知道我们的自定义过滤器,应该如何写了。(回忆我们前面自定义过滤器),
1 | //获取当前上下文 |
过滤器排序
自定义过滤器的排序源码
在com.netflix.zuul.http.ZuulServlet中,service方法中,有一行:preRoute();点进去
1 | void preRoute() throws ZuulException { |
点preRoute进去
1 | public void preRoute() throws ZuulException { |
点preRoute进去
1 | public void preRoute() throws ZuulException { |
点runFilters进去
1 | public Object runFilters(String sType) throws Throwable { |
点getFiltersByType进去
1 | public List<ZuulFilter> getFiltersByType(String filterType) { |
看到了,排序方法。
过滤器顺序
过滤器定义order
1 | FilterConstants |
打开:SendResponseFilter
1 | @Override |
RequestContext 贯穿整个请求。
过滤器执行
1 | FilterProcessor |