使用netty构建API网关实践之路
随着互联网的快速发展,当前以步入移动互联、物联网时代。用户访问系统入口也变得多种方式,由原来单一的PC客户端,变化到PC客户端、各种厅派浏览器、手机移动端及智能终端等。同时系统之间大部分都不是单独运行,经常会涉及与其他系统对接、共享数据的需求。所以系统需要升级框架满足日新月异需求变化,支持业务发展,并将框架升级为微服务架构。“API网关”核心组件是架构用于满足此些需求
很多互联网平台已基于网关的设计思路,构建自身平台的API网关,国内主要有京东、携程、唯品会等,国外主要有Netflix、Amazon等。
业界为了满足这些需求,已有相关的网关框架。
1、基于nginx平台实现的网关有:kong、umbrella等
2、自研发的网关有:zuul1、zuul2等
但是以上网关框架只能是满足部分需求,不能满足企业的所有要求,就我而言,我认为最大的问题是没有协议转换及OPS管理控制平台
另外:对于微服务架构下,如果基于HTTP REST传输协议,API网关还承担了一个内外API甄别的功能,只有在API网关上注册了的API还能是真正的堆外API
整个网关系统由扮兆三个子系统组成:
说明:
1) 整个网关基于Netty NIO来实现同步非阻塞是HTTP服务,网关是外部API请求的HTTP服务端,同时是内部服务的客户端,所以有Netty Server Handler和Netty Client Handler的出现;
2)对于Netty Server Handler来说,当一个HTTP请求进来时,他会把当前连接转化为ClientToProxyConnection,它是线程安全的,伴随当前此HTTP请求的生命周期结束,它也负责ClientToProxyConnection的生命周期的维护;
3)对于Netty Client Handler来说,当ClientToProxyConnection需要传递请求到内部服务时,会新建(或者获取扮缺贺原来已建)的ProxyToServerConnection来进行内部的请求,它也是线程安全的;
4)对于Filter来说,他运行在ClientToProxyConnection上,插入请求进来及收到后端请求之间;
从以上分析,网关选择同步非阻塞方式是一个合适的选择
其中转化的过程如下:
2:根据FileDescriptorSet获取gRPC的入参和出参描述符,然后再创建gRPC所需要的MethodDescriptor方法描述对象
2) HTTP ----> dubbo
在dubbo的框架设计中,其中已经包含了泛化调用的设计,所以在这块,基本上就延用了dubbo的泛化调用来实现http转dubbo的协议,而关于dubbo的参数部分,可以指定参数映射规范,利用参数裁剪的技术对http请求参数进行抽取,如果dubbo的接口是java类型,则直接抽取,如果是pojo,按照dubbo的用户文档,把他组成一个Map的数据结构即可,而操作这一步需要映射规则
整个网关目前基本完成并且也开源到GitHub上,欢迎拍砖及使用
tesla
Dubbo——服务调用、服务暴露、服务引用过程
1、InvokerInvocationHandler jdk动态代理
5、RegistryDirector返回Invokers
Router分为:Script 脚本路由、Condition 条件路由
6、通过MockInvokersSelector的route方法(getNormalInvokers)拿到能正常执行的invokers
8、当回到AbstractClusterInvoker后,执行首纳(默认FailoverClusterInvoker,根据配置的是,Failfast Cluster(快速失败) , Failsafe Cluster(失败安全) , Failback Cluster(失败自动恢复) , Forking Cluster(并行调用多个服务器,只要一个成功即返回) , Broadcast Cluster(广播调用所有提供者,逐个调用,任意一台报错则报错))doInvoker方法
9、FailoverClusterInvoker调用AbstractClusterInvoker的select方法
10、执行doSelect方法
11、调用AbstractLoadbalance的select方法
12、根据配置的负载均衡策略调用对应的(如RoundRobinLoadBalance)类的doSelect方法
13、返回invokers.get()方法
14、调用FailoverClusterInvoker的invoke方法
均继承自抽象类AbstractDirectory
Directory 获取 invoker 是从 methodInvokerMap 中获取的,主要都是读操作,那它的写操作是在什么时候写的呢?就是在回调方法 notify 的时候操作的,也就是注册中心有变化,则更新 methodInvokerMap 和 urlInvokerMap 的值
根据dubbo-admin配置的路由规则来过滤相关的invoker,当我们对路由规则点击启用,就会触发 RegistryDirectory 类的 notify 方法。
notify方法调用refreshInvoker方法。
route方法的实现类为ConditionRoute 根据条件进行过滤
1、调用mathThen方法
2、调用matchCondition方法
3、调用isMatch判断
4、调用isMatchGlobPattern方法
集群模块是服务提供者和服务消费者的中间层,为服务消费者屏蔽了服务提供者的情况,这样服务消费者就可以专心处理远程调用相关事宜。比如发请求,接受服务提供者返回的数据等。这就是Dubbo Cluster集群的作用。
通过cluster来指定集群容错方式
其实就是应对出错情况采取的策略
用于芹磨有状态服务,尽可能让客户端总是向同一提供者发起调用,除非提供者挂了,再连另一台,自动开启延迟链接,以减少长接数
启动时服务提供者将当前进程启动时间注册到ZK;服务消费者发现该节点后计算服务启动时间(相对当前时间),在默认预热时间的前20%时间内,该节点权重始终固定为2,这样客户端的负载均衡器只会分发极少的请求至节点。
在预热时间之后的80%时间内,该节点权重将随着时间的推移而线性增长;待预热时间到期后,权重自动恢复为默认值100;负载均衡器的内核是一个标准的WLC算法模块,即加权最少连接算法;
如果某个节点Hang住或宕机,其权重会迅速自动调节降低,避免持续性影响;当节点下线时,服务端提前触发权重调节,重载默认权重至1并发布到注册中心,服务消费者将迅速感知到该事件;
服务提供者优雅下线步骤(注意这套逻辑仅在服务端执行)在ok.htm?down=true对应的controller中加入下列逻辑,注意嫌芹斗要判断down是否为true,因为正常来说false表示启动验证而不是关机
服务者消费者配置
dubbo服务支持参数动态调整,例如动态调整权重,但dubbo实现方式较为特殊,并不是常规思路。
ServiceConfig类拿到对外提供服务的实际类ref,然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转换(javassistProxyFacory、JdkProxyFactory),接着要做Invoker转换到Export的过程
服务发布:本地暴露、远程暴露
为什么会有 本地暴露 和 远程暴露 呢?不从场景考虑讨论技术的没有意义是.在dubbo中我们一个服务可能既是 Provider ,又是 Consumer ,因此就存在他自己调用自己服务的情况,如果再通过网络去访问,那自然是舍近求远,因此他是有 本地暴露 服务的这个设计.从这里我们就知道这个两者的区别
1、spring启动,解析配置文件
2、创建dubbo标签解析器
3、解析dubbo标签
4、ServiceBean解析
5、容器创建完成,触发ContextRefrestEvent
6、export暴露服务
7、duExportUrls
8、doExportUrlsFor1Protocol
9、getInvoker
10、protocol.export
11、开启服务器 openServer()如nettyServer
12、注册服务到注册中心 registerProvider
Filter 在服务暴露前,做拦截器初始化,在加载所有拦截器时会过滤支队provider生效的数据。
可以。zookeeper的信息会缓存到本地作为一个缓存文件,并且转换成 properties 对象方便使用。建立线程池,定时检测并连接注册中心,失败了就重连。
注册服务到zk其实就是在zk上创建临时节点,当节点下线或者down掉时,即会删除临时节点,从而使服务从可用列表中剔除。
持久节点
临时节点
1、export的时候进行zk订阅
2、设置监听回调的地址,回调给FailbackRegistry的notify
3、创建持久节点
4、设置对该节点的监听
5、更新新的服务信息,服务启动和节点更新回调,都会调用到这里
6、更新缓存文件
7、对比新旧信息是否有变化,有则重新暴露服务
高并发大业务量情况下,暂时屏蔽边缘业务
MockClusterInvoker
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。接下来,我们先来了解一下 Java SPI 与 Dubbo SPI 的用法,然后再来分析 Dubbo SPI 的源码。