Spring Cloud Zuul-“网关“与上层负载均衡器搭配及Zuul调优建议**工作原理**核心源码解析

> 在Spring Cloud微服务架构体系中,所有请求的前门的网关Zuul承担着请求转发的主要功能。
> 'Nginx均分请求到 + Zuul 负载层,还是比较乐观的组合策略,但是它俩者没有什么直接关联性,Zuul层服务挂掉了,'
> 'Nginx依然还是会把请求分过来,给Zuul处理,在Nginx没有采取相对措施情况下,这样造成大量请求失败情况。'
> '解决这个痛点,OpenResty(一个基于Nginx与 Lua 的高性能 Web 平台),利用它,使用它的Lua脚本模块与注册'
> '中心构建一个服务动态增减的机制,通过Lua获取注册中心状态为UP的服务,动态地加入到Nginx的均衡列表中去,'
> '这种方式架构模式涉及不止一个负载均衡器,称其为“多层负载”'
>Nginx+Zuul 痛点场景图-1:

Nginx均分请求到 + Zuul 负载层 痛点场景

>Nginx+Zuul 利用OpenRestry 弹性解决方案图-2:

在这里插入图片描述

Lua插件源码,GitHub地址:https://github.com/SpringCloud/nginx-zuul-dynamic-lb

<Nginx相关经典配置如下:>
http {
	#sharing cache area (缓存区大小)
	lua_shared_dict dynamic_eureka_balancer 128m;

	init_worker_by_lua_block { --引入Lua插件文件
		-- init eureka balancer
		local file = require "resty.dynamic_eureka_balancer"
		local balancer = file:new({dict_name="dynamic_eureka_balancer"})
		
		--eureka server list --Eureka地址列表
		balancer.set_eureka_service_url({"127.0.0.1:8888", "127.0.0.1:9999"})
		
		--eureka basic authentication
		--use this setting if eureka has enabled basic authentication. 
		--note: basic authentication must use BASE64 encryption in `user:password` format
        --balancer.set_eureka_service_basic_authentication("")
		-- 配置初始化被监听的服务名
		--The service name that needs to be monitored
		balancer.watch_service({"zuul", "client"})
	}
	
	upstream springcloud_cn {
		server 127.0.0.1:666; # Required, because empty upstream block is rejected by nginx (nginx+ can use 'zone' instead)
		
		balancer_by_lua_block {    
			--配置需要动态变化的服务名
			--The zuul name that needs to be monitored
			local service_name = "zuul"
			
			local file = require "resty.dynamic_eureka_balancer"
			local balancer = file:new({dict_name="dynamic_eureka_balancer"}) 
			
			--balancer.ip_hash(service_name) --IP Hash LB
			balancer.round_robin(service_name) --Round Robin LB
		}
	}

    server {
        listen       80;
        server_name  localhost;
		
		location / {
			proxy_pass  http://springcloud_cn/;
			proxy_set_header Host $http_host;
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header X-Forwarded-Proto  $scheme;
		}

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
	}
}
> <实现原理是使用Lua脚本定时根据配置的服务名与Eureka地址,去拉去该服务的信息,>
> < 在Eureka里面提供/eureka/apps{serviceId}端口,返回服务的注册信息,所以我们只需要取用状态为“UP”的服务,>
> <将它的地址加入Nginx负载列表即可。此项目使得Nginx与Zuul之间拥有一个动态感知能力,>
> < 不用手动配置Nginx负载与Zuul负载,这样对于应用弹性扩展是极其友好的。>
> Zuul的优化分为以下几个类型:
> "Zuul 是建立在Servlet上的同步阻塞架构,所以在处理逻辑上面是和线程密不可分的,每一次请求都是需要从"
> "线程池获取线程来维持连接,这就导致一个组件占用两个线程资源的情况。所以,在Zuul使用中,对这部分优化:"
> "Zuul的优化分为以下几个类型:"
>1.容器优化:内置容器Tomcat与Undertow的比较与参数设置。’
>2.组件优化:内部集成的组件优化,如Hystrix线程隔离、Ribbon、HttpClient与OKHttp选择。’
>3.JVM参数优化:适用与网关应用的JVM参数建议。’
>4.内部优化:一些内部原生参数,或者内部源码,以一种更恰当的方式重写它们。’
> Zuul的容器优化:

容器优化:默认的内嵌容器Tomcat替换成Undertow1。要使用Undertow只需要在配置文件中移除Tomcat,添加Undertow的依赖即可。

<dependencys>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
		<exclusions>
			<exclusion>
				<groupId>org.springframework,boot</groupId>
				<artifactId>spring-boot-starter-tomcat</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-undertow</artifactId>
	</dependency>
</dependencys>
> Undertow参数说明:

在这里插入图片描述
在这里插入图片描述

>> '功能最强大的组件--Zuul网关。'
>> 'Zuul网关主要用于智能路由,同时也支持认证、区域和内容感知路由,'
>> '将多个底层服务聚合成统一的对外API。'
>> '所以要更好的使用Zuul,免不了要对它集成组件进行优化:'
> Zuul的组件优化-Hystrix:
> Hystrix.
	<Zuul中默认集成了Hystrix熔断器>
<常见问题: '启动Zuul应用之后,第一次请求会失败,原因:Zuul首次要初始化很多类信息耗时长,超时默认1秒后,就会返回错误信息 '>
> <解决方式有两种:>
> 第一种,加大超时时间:hystrix.cmmand.default.execution.isolation.thread.timeouthMillisecods=5000
> 第二种,直接禁用掉Hystrix的超时:hystrix.command.default.execution.timeout.enabled=false
> Hystrix两种线程隔离方式对比:

Hystrix两种线程隔离方式对比

切换隔离模式的配置方式:hystrix.command.default.execution.strategy=Thread | Semaphore
总结一下:当应用需要与外网交互,由于网络开销比较大与请求比较耗时,
> 这时选用线程隔离策略,可以保证有剩余的容器(Tomact&Undertow&Jetty)线程可用,
> 而不会由于外部原因使得线程,一直处于阻塞或等待状态,可以快速失败返回。
但当应用只在内网交互,并且体量较大,这时使用信号量隔离策略就比较好,
>因为这类应用的响应通常会非常快(由于在内网),不会占用容器线程太长时间,
>使用信号量线程上下文就会成为一个瓶颈,可以减少线程切换的开销,提高应用运转的效率,
>也可以起到对请求进行全局限流的作用。

> Zuul的组件优化-Ribbon:
ribbon:
	ConnectTimeout: 3000
	ReadTimeout: 60000
	MaxAutoRetries: 1 #对第一请求的服务的重试次数
	MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包括第一个服务)
	OkToRetryOnAllOperationbs: true
	
<!--配置当中的ConnectTimeout:!--ReadTimeout:  !-是当HTT客户端使用`HttpClient`的时候生效的,>
<!--这个超时时间最终会被设置到’HttpClent‘中去。在设置的时候要结合Hystrix的超时时间来综合考虑,>
<  !--针对应用场景: !设置太小会导致很多请求失败,设置太大导致熔断功能控制性太差,需要经过压力测试得来。>
> JVM参数优化 -对于Zuul网关:
--'对于Zuul来说,它起到的作用是“网关”,网关最需要的就是吞吐量,所以优化应以此为切入点来综合考虑💭;'
--'这里推荐使用' -Parallel Scavenge(并行清理) 收集器-'注意它有一个
参数 -XX:+UseAdaptiveSizePolicy(使用自适应大小策略),如果打开,JVM会自动选择年轻区大小和相应Survivor(幸存)区比例'
--'以达到目标系统规定的最低响应时间或者YGC(对新生代堆进行gc)频率,官方建议在使用并行收集器的时候一直打开,'
--'但 经过 奕小生 压力测试中观察到效果并不理想,这里建议是将它关闭,改为-XX:-+UseAdaptiveSizePolicy(使用自适应大小策略)'
--'再根据实际情况调整Eden(新生代Eden 伊甸园)区与Survivor(新生代Survivor 幸存者)区的比例。'
--'这里还有一个参数比较管用-XX:TargetSurvivorRatiob,即Survivor区的对象利用率,默认是50%,'
--'建议稍微加大,加大后YGC会看到比较明显的效果。另外,让垃圾对象尽量在新生代被回收掉,'
--'以免进入老年代触发FGC(这也是一个技巧).'
=='这里使用的策略是在有限的堆空间下使用一个较大的新生代,并且Eden区也要比Survivor区大。'
--'老年代使用 Parallel Old 收集器,让网关应用彻底面向吞吐量。'
--'参数-XX:+ ScavengeBeforeFullGC,即FGC前先进行一次YGC,推荐使用这个参数。'
> JVM参数优化,没有一个固定的值,在不同的压力环境下,不同JDK版本下的选择也不一样,上文比较适合的是JDK1.7
> 与JDK1.8版本,如果是1.9版本之后,建议使用'G1'。JVM优化是一个繁琐的过程,慢慢反复折腾.喽!。
> > --<最后,说一下Spring Boot以jar包模式启动时的JVM参数设置方法: java-JVM参数-jar appliction.jar >--
> JVM垃圾回收器搭配-GC收集器搭配(有连线的表示可以搭配使用)如图下:

在这里插入图片描述

> -对于Zuul网关内部优化:
> '在官方文档中,Zuul部分开篇讲了 zuul.max.host.connections属性拆分成了:'
> 'zuul.host.maxTitalConnections(服务HTTP客户端最大连接数)'
> '与 zuul.host.maxPerRouteConnections(每个路由规则HTTP客户端最大连接数)默认值分别是200与20,'
> '如果使用HttpClient的时候则有效,如果使用OKHttp则无效(替换方式上一篇写过)'
> '之前,说过对于Zuul中有些Filter的设计并不是很好用的,使用者可以选择自实现替换它或者禁用:'
> <!-- zull.<SimpleClassNamne>.<filterType>.disable=true -->
> '在Zuul中还有一个超时时间,使用serviceId映射与url映射的设置是不一样的,'
> '如果使用serviceId映射,应该设置 zuul,host.connect-timeout-millis '
> '与 zuul.host.socket-timeout-millis 参数。'
> -Zuul生命周期图:

Zuul生命周期图

> '图示: ZuulServlet通过RequestContext统管这由许多Fliter(过滤器)组成的核心部件'
> '所以操作都与Fliter(过滤器)息息相关. 请求、ZuulServlet与Filter共同建起了Zuul的运行时生命周期,下图如示:'
> -Zuul生命周期类图:

Zuul生命周期类图

	Zuul的请求引入还是来自于 'DispatcherServlet', 然后交由 'ZuulHandlerMapping'
'(由AbstractUrlHandLer-Mapping继承得来)' 它是'Spring MVC对request'进行分发的一个重要的抽象类,
处理由处理化得来的'路由定位器RouteLocater',为后续的请求分发做好准备,
--同时整合类基于事件从服务中心拉去
--服务列表的机制;'进入ZuulConteroller'
  -它的'主要职责是初始化ZuulServer'以及'继承ServletWrapping-Controller'
	-通过重写 'handleRequest()'方法来将 Zuul-Servlet引入生命周期
	-之后所有的'请求都会经过 ZuulServlet'
	-当请求就如ZuulServlet 之后,第一次调用会初始化 ZuulRunner ,
	-非第一次调用的按 'Filter链Order顺序执行'
	-ZuulRunner 中将请求和响应初始化为 'RequestContext'
	-'包装成 FilterProcessor 转换为调用 preRoute()、route()、postRoute()和error()方法'
	--最后便是我们的各个Filter执行逻辑来。
> -Spring Cloud Zuul @Enable-ZuulServer的底层初始化类:ZuulServerAutoConfiguration的功能是:

ZuulServerAutoConfiguration的功能是:

					>  .1-初始化配置加载器。
					>  .2-初始化路由定位器。
					>  .3-初始化路由映射器。
					>  .4-初始化配置刷新监听器。
					>  .5-初始化ZuulServlet加载器。
					>  .6-初始化ZuulController。
					>  .7-初始化Filter执行解析器。
					>  .8-初始化一些Filter。
					>  .9-初始化Metrix监控。
> -Spring Cloud Zuul @Enable-ZuulProxy的底层初始化类:ZuulProxyAutoConfiguration的功能是:

ZuulProxyAutoConfiguration的功能是:

					>  .1-初始化服务注册、发现监听器。
					>  .2-初始化服务列表监听器。
					>  .3-初始化Zuul自定义Endpoint。
					>  .4-初始化一些ZuulServerAutoConfiguration中没有的Filter。
					>  .5-引入HTTP客户端的两种方式:HttpClient与OkHttp。
>>>":装载完成这些组件之后,Zuul便工作了起来,形成一个完整的生命周期。"
> -Spring Cloud Zuul 核心路由实现RouteLocator(路由定位器):
> 路由部分有一个顶级接口叫作: RouteLocator(路由定位器).
> 所有关于路由的功能都是由它实现而来,其中定义了三个基本方法:
> 1.'获取忽略的path集合。'2.'获取路由列表。'3.'根据path获取路由信息。'
> -Zuul-RouteLocator(路由定位器)实现类图:

-Zuul-RouteLocator(路由定位器)实现类图


  1. Undertow翻译为“暗流”,即平静的湖面下暗藏这波涛汹涌,所以JBoss公司取其意,为它的轻量级高性能容器命名。Undertow提供阻塞或基于XNIO的非阻塞机制,它的包大小不足1MB,内嵌模式云心时的堆内存占只有4MB左右。 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值