原创 吴就业 192 0 2020-08-22
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/cb5507b385a24a9d99e3d547870c8d51
作者:吴就业
链接:https://wujiuye.com/article/cb5507b385a24a9d99e3d547870c8d51
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
本篇文章写于2020年08月22日,从公众号|掘金|CSDN手工同步过来(博客搬家),本篇为原创文章。
使用API网关可统一流量入口,对客户端屏蔽内部多个微服务的域名,实现负载均衡,并可以统一鉴权、接口访问控制、流量管控。
作为网关,代理了所有流量,对性能要求更高,介于网关核心功能是路由、代理后端服务,主要处理请求转发,如果使用同步阻塞方式,后端接口响应耗时越长,对网关性能的影响就越大。nginx作为反向代理中的佼佼者,基于nginx开发的Kong网关性能更是毋庸置疑。但作为内部网关,我们需要更好的定制,选择Java系的网关对我们来说更容易驾驭。
在选定编程语言后,我们就需要考虑性能问题。而网关的性能损耗无非就是I/O阻塞,只有纯异步才能有更好的性能表现,所以我们选择至少是基于实现Reactive Streams规范的反应式编程库开发的网关。
Spring cloud gateway作为spring cloud生态系统中的网关,基于Spring Boot、Project Reactor开发,不仅实现异步非阻塞,并且更快速与spring cloud生态中的其它组件整合,这是我们选择Spring cloud gateway的原因。
我们基于Spring Cloud Gateway开发内部微服务网关,并结合注册中心实现自动服务发现路由。
就在最近将项目部署测试环境的Kubernetes集群上时,发现路由失败。经调试源码发现是因为没有导入Ribbon的依赖,所以Gateway注册的是非负载均衡路由过滤器(NoLoadBalancerClientFilter),当路由规则配置以”lb://“开头时,该路由过滤器直接响应503(目标服务不可达)。
Gateway使用全局过滤器实现路由功能,其按照全局过滤器指定的排序值顺序调用,每个过滤器都可终止请求的调用。
Gateway允许每个全局过滤器重写路由URL,通过将重写后的URL更新到请求的attribute向下传递,下游过滤器可重新修改路由URL,最初的路由URL从路由规则取得。
// 从ServerWebExchange#getAttribute取得路由url
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
Gateway使用一个链表将每个过滤器替换前的路由URL串起来,如果想获取URL被替换为目标服务URL过程的变化,可获取该链表、遍历该链表。
// 记录修改前的url
addOriginalRequestUrl(exchange, url);
//
public static void addOriginalRequestUrl(ServerWebExchange exchange, URI url) {
exchange.getAttributes().computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR,
s -> new LinkedHashSet<>());
LinkedHashSet<URI> uris = exchange
.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
uris.add(url);
}
Gateway接收到一个请求后,请求从FilteringWebHandler传送到全局过滤器链上(GatewayFilterChain),在过滤器链上向下传递,经过中间过滤器将请求URL替换后,由最后的请求转发过滤器(ForwardRoutingFilter)将请求交给DispatcherHandler向目标服务发起请求并将结果响应给调用端。
在了解路由失效的原因后、在尝试依赖Ribbon的starter包依然路由失败后,笔者决定自己实现路由功能,并使用Ribbon实现的负载均衡算法在网关实现负载均衡。
由于项目是基于Spring Cloud Kubernetes开发的微服务项目,使用Kubernetes云原生服务作为“注册中心”,服务无需注册,可通过kubernete API读取Service或Endpoints资源,并且spring-cloud-kubernete-core提供了读取Service或Endpoints资源的API,因此自己实现路由功能相比于依赖一堆无关紧要的jar包更轻量。
负载均衡功能是次要的,如果不需要在网关实现负载均衡,则可以将服务发现模式改为Service,读取Kubernetes中的Service资源的Cluster IP;如果想要在网关实现负载均衡,而可以将服务发现模式改为Pod,读取Kubernetes的Endpoints资源。
自实现路由功能分为以下几个步骤:
1、使用spring cloud kubernetes core提供访问kubernetes资源的客户端接口定时拉取服务目录缓存到本地;
2、自实现负载均衡算法,或使用Ribbon的ribbon-loadbalancer包提供的负载均衡算法(IRule),每次路由根据算法从服务列表中选择一个节点调用;
3、自定义实现理由功能的全局过滤器(GlobalFilter),指定该过滤器排在Gateway注册的非负载均衡过滤器之前,在它之前完成将url中的“lb://serviceId”替换为”http://ip:port”。
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。
Instrumentation之所以难驾驭,在于需要了解Java类加载机制以及字节码,一不小心就能遇到各种陌生的Exception。笔者在实现Java探针时就踩过不少坑,其中一类就是类加载相关的问题,也是本篇所要跟大家分享的。
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。