2019独角兽企业重金招聘Python工程师标准>>>
引子
在上篇文章中我们简单回顾了我们服务器端架构的演变过程 从而引出了微服务,面向服务的架构体系 从而也带来了新的问题就是服务之间的如何相互调用,服务如何注册与发现的问题
服务调用方式
我们先来了解一下服务之间如何调用,在Java领域通常有几种方式
-
使用Tcp或者Http实现通信 跨平台
-
RMI (Remote Method Invocation) 远程方法调用 只支持Java 客户机与服务器强耦合 不推荐使用
-
WebService
跨平台 通过Http实现通信 复杂度较高 传统SOA使用较多 -
MQ (Message Queue)消息队列 跨平台 完全隔离了客户端和服务提供者 通常异步使用
综合以上几种方案的特点我们会选择使用使用Http或者Tcp的协议来完成RPC(同步) 和MQ(异步)来实现我们的服务调用
对于Java系统的内部调用而言我们可使用Tcp来提高性能, 而对于外面接口统一使用Http的形式,如果对性能要求不高也可以直接使用Http的形式 对于以后系统的异构化更方便和统一
服务发现与注册
我们有了服务间调用的机制,但是 我还需要知道另一个服务的地址我们才能调用, MQ没有这种烦恼直接发送到消息中间件里就可以了, 但是RPC就不行了难道直接在代码里写死吗如果有多个实例呐? 这时候我们最常用的方法是使用集中式的负载均衡来解决
集中式LB方案实现简单,在LB上也容易做集中式的访问控制 集中式LB的主要问题是单点问题,所有服务调用流量都经过LB, 当服务数量和调用量大的时候,LB容易成为瓶颈, 且一旦LB发生故障对整个系统的影响是灾难性的 另外,LB在服务消费方和服务提供方之间增加了跳转,有一定性能开销
这里我们通常是使用nginx代理来实现的, 但是服务实例的网络位置都是动态分配的,而且因为扩展,失效和升级等需求, 服务实例会经常动态改变,因此也需要使用一种更加复杂的服务发现机制。
既然如此我们就引出了我们的服务注册的概念
*服务注册**是服务发现很重要的部分,他是包含服务实例网络地址的数据库 服务注册需要高可用而且随时更新,客户端可以缓存从服务注册表获得的网络地址 然而,这些信息最终会变得过时,客户端也无法发现服务实例 因此,服务注册由若干使用复制协议保持同步的服务器构成 我们使用类似Zookeeper之类具有分布式的强一致性的key/value数据库来实现
服务注册与发现有下面几种模式
- 客户端模式
服务实例的网络地址在启动时注册到服务注册中心中,并且在服务终止时从注册表中删除 服务实例注册信息一般是使用心跳机制来定期刷新
客户端模式的LB方案是一种分布式方案,LB和服务发现能力被分散到每一个服务消费者的进程内部,同时服务消费方和服务提供方之间是直接调用,没有额外开销,性能比较好。但是,该方案以库的方式集成到服务调用方进程里头,如果企业内有多种不同的语言栈,就要配合开发多种不同的客户端,有一定维护成本
- 主机独立LB进程方案
该方案是针对客户端模式的不足而提出的一种折中方案,原理和第二种方案基本类似,不同之处是,他将LB和服务发现功能从进程内移出来,变成主机上的一个独立进程,主机上的一个或者多个服务要访问目标服务时,他们都通过同一主机上的独立LB进程做服务发现和负载均衡 通过这种形式就解决了与服务模块耦合的问题方便升级和维护
总结
目前在微服务中使用更多的是客户端发现的模式 使用的案例有Netflix的开源服务框架,对应的组件分别是:Eureka服务注册表,Karyon服务端框架支持服务自注册和健康检查,Ribbon客户端框架支持服务自发现和软路由 另外,阿里开源的服务框架Dubbo也是采用类似机制