Apache Thrift是Facebook实现的一种高效的,支持多种编程语言的远程服务调用的框架.在多语言并行于业务之中的公司,其是一个很好的RPC框架选择,但是由于缺少服务发现管理功能,在使用的时候,需要告知业务方现有业务部署的地址,并且调用方需要自己实现服务状态的感知和重试机制.此外,对于互联网公司而言,业务快速变化必然导致机器的增减,这些变化,需要通知到所有调用方来更改调用机器的配置,是非常麻烦的.
显然,对于Thrift来说,一个服务发现管理框架是多么的重要。
那么,服务发现管理框架其实可以做的很重,也可以做的很轻;对于我们,需要满足什么需求:
- 服务调用方自动获取服务提供方地址;
- 服务提供方服务分组;
- 服务调用方负载均衡策略;
- 服务非兼容升级;
具体的需求分析和实现,将在 Ourea服务发现实现原理介绍。
Thrift 接口使用还是比较简单地,对外提供的server和client接口封装了所有的内部实现细节,所以,一般我们只需要告诉Thrift
地址端口信息,然后就可以完成简单地RPC调用。
下面,给出一个简单地示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
public class SimpleThriftServer { private static final Logger logger = LoggerFactory.getLogger(SimpleThriftServer.class); private static final int port = 9999;
public void simple(int port){
try { TServerSocket tServerSocket = new TServerSocket(port); Hello.Processor processor = new Hello.Processor(new HelloService());
TServer server = new TSimpleServer(new TServer.Args(tServerSocket).processor(processor) ); server.serve();
}catch (Exception e){ logger.error("server start error........",e); } }
public static void main(String[] args) throws InterruptedException { SimpleThriftServer server = new SimpleThriftServer(); server.simple(port); } }
public class SimpleThriftClient {
private static final Logger logger = LoggerFactory.getLogger(SimpleThriftClient.class); private static final int port = 9999; private static final String ip = "127.0.0.1";
public static void main() { TTransport transport = null; try { transport = new TSocket(ip, port); TProtocol protocol = new TBinaryProtocol(transport); Hello.Client client = new Hello.Client(protocol); transport.open(); HelloResult result = client.sayHello("hello world"); } catch (Exception e) { logger.error("client invoke fail. ", e); } finally { if (transport != null) { transport.close(); } } } }
|
Note:
- TProtocol 协议和编解码组件
- TTransport 传输通信组件
- TProcessor 服务处理相关组件
- TServer 服务提供组件
- Client 服务调用客户端
Thrift 原生的对外接口已经很简单了,但是为什么还需要去封装呢?上文的代码虽然简单,但是有几个点需要去注意:
- 对于生产环境的服务,在发布新功能,出现故障down机,都会导致服务出现不可用的情况;此外,对外的服务一般都是集群部署,集群机器的增减也是很可能会出现的事情,因此,就会出现最初对外提供的服务IP地址会出现新增(新建服务),减少(缩减服务),暂时停服(机器故障),这些所有变更通知所有业务服务调用方去更改是很难处理的事情。此外,由于服务可能存在大量的机器列表,这些配置在业务代码中,本身也是不可取的。
- 服务调用的时候,可能存在某些服务当时负载过高,或者服务网络问题等导致服务调用策略需要调整。也就是在选择调用集群中某台机器的时候,每个业务都要自己去实现策略,这是不可取的。此外,对于服务的负载情况无法感知,即使是静态的服务提供权重都无法获取,导致了即使客户端自己实现均衡策略,由于缺少必要的数据支持,导致只能采用轮询和随机。
- 业务上,服务调用之间隔离,服务接口的灰度升级等,是比较常见的技术需求。Thrift 对外发布的服务的所有IP,对于调用方来说都是平等的,也就是,如果我需要将集群中某些机器进行接口的非兼容的灰度升级,或者某些机器独立出来给一些非常重要的业务使用。目前,这种场景,只能新加机器来解决了。
- 对于调用方Client的调用,每次都需要去创建连接,然后和server端交互,对于大请求场景下的应用,对性能的影响是很大的。创建connection对象,是很重的,需要进行池化。
- ……
基于以上的一些原因,开发了基于Zookeeper
的Thrift服务发现机制框架。