系统间通信:基于TCP协议的RPC实现范例

系统间通信:基于TCP协议的RPC实现范例

一、RPC名词解释

         RPC的全称是Remote Process Call,即远程过程调用,它应用广泛,实现方式也很多,拥有RMI、WebService等诸多成熟的方案,在业界得到了广泛的使用。单台服务的处理能力受到硬件成本的限制,不可能无限制地提升。RPC将原来的本地调用转变为调用远端的服务器上的方法,给系统的处理能力和吞吐量带来了近似于无限制提升的可能,这是系统发展到一定阶段必然性的变革,也是实现分布式计算的基础。

         RPC的实现包括客户端和服务端,即服务的调用方与服务的提供方。服务调用方发送RPC请求到服务提供方,服务提供方根据调用方提供的参数执行请求方法,将执行结果返回给调用方,一次RPC调用完成。这里涉及到网络通信、序列化与反序列化的操作,如果服务提供者是多台或者服务提供者进行了分组,还会涉及到负载均衡与路由。

二、RPC调用过程

         随着业务的发展,服务调用者的规则发展到一定阶段,对服务提供方的压力也日益增加,因此,服务需要进行扩容。而随着服务提供者的增加与业务的发展,不同的服务之间还需要进行分组,以隔离不同的业务,避免相互影响,在这种情况下,服务的路由和负载均衡成为必须考虑的问题。服务消费者通过获取服务提供者的分组信息和地址信息进行路由,如果服务提供者为一个集群而非单台机器,则需要根据相应的负载均衡策略,选取其中一台进行调研,有关服务的路由和负载均衡,后续章节会进行详细介绍RPC调用过程(不包括负载均衡和路由)如下图所示:

(1)    服务消费方(client)调用以本地调用方式调用服务;;

(2)    clientstub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体,并将消息发送到服务端(对象序列化);

(3)    serverstub收到消息后进行解码(对象反序列化);

(4)    serverstub根据解码结果调用本地的服务;

(5)    本地服务执行并将结果返回给server stub;

(6)    serverstub将返回结果打包成消息并发送至消费方;

(7)    clientstub接收到消息,并进行解码;

(8)    服务消费方得到最终结果。

RPC的目标就是要2~7这些步骤都封装起来,让用户对这些细节透明。(注意:无论是何种类型的数据,最终都需要转换二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流再恢复为对象。目前成熟的方案有多种,比如Protocol Buffers、Java自带方式、Hessian、JSON、XML等)。各种序列化方式性能对比图如下所示:


三、范例代码

        基于Java的Socket API,我实现一个简单的RPC调用的范例,在这个范例中,包括服务的接口、接口的远程实现、服务的提供方以及服务的消费方。为了方便大家下载代码运行,我把代码放到了Github(https://github.com/dreamerkr/RPCDemo),其类图如下所示:


(1)    服务接口SayHelloservice

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.primeton.RPCDemo;  
  2.   
  3. public interface SayHelloService {  
  4.     /** 
  5.      * 问候接口 
  6.      * @param msg 
  7.      * @return 
  8.      */  
  9.     public String sayHello(String msg);  
  10. }  

(2)    接口远端实现

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:14px;">package com.primeton.RPCDemo;  
  2.   
  3. public class SayHelloServiceImpl implements SayHelloService {  
  4.   
  5.     @Override  
  6.     public String sayHello(String msg) {  
  7.         if("hello".equals(msg)){  
  8.             return "hello client";  
  9.         }else{  
  10.             return "bye bye";  
  11.         }  
  12.     }  
  13. }</span>  

(3)   服务消费者

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:14px;">package com.primeton.RPCDemo;  
  2.   
  3. import java.io.ObjectInputStream;  
  4. import java.io.ObjectOutputStream;  
  5. import java.lang.reflect.Method;  
  6. import java.net.Socket;  
  7.   
  8. /** 
  9.  * TODO 服务消费者 
  10.  * 
  11.  * @author wangzhao (mailto:wangzhao@primeton.com) 
  12.  */  
  13. public class Consumer {  
  14.   
  15.     public static void main(String[] args) throws Exception {  
  16.           
  17.         //构造需要调用的方法  
  18.         String interfaceName = SayHelloService.class.getName();  
  19.         Method method = SayHelloService.class.getMethod("sayHello",   
  20.                 java.lang.String.class);  
  21.         Object[] argments = {"hello"};  
  22.           
  23.         //发送调用信息到服务器端,调用相应的服务  
  24.         Socket socket = new Socket("127.0.0.1",1234);  
  25.         ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());  
  26.         outputStream.writeUTF(interfaceName);  
  27.         outputStream.writeUTF(method.getName());  
  28.         outputStream.writeObject(method.getParameterTypes());  
  29.         outputStream.writeObject(argments);  
  30.         System.out.println("发送信息到服务端,发送的信息为:"+argments[0]);  
  31.           
  32.         //服务返回的结果  
  33.         ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());  
  34.         Object object = inputStream.readObject();  
  35.         System.out.println("服务返回的结果为"+object);  
  36.     }  
  37. }</span>  

(4)    服务提供者

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:14px;">package com.primeton.RPCDemo;  
  2.   
  3. import java.io.ObjectInputStream;  
  4. import java.io.ObjectOutputStream;  
  5. import java.lang.reflect.Method;  
  6. import java.net.ServerSocket;  
  7. import java.net.Socket;  
  8. import java.util.HashMap;  
  9. import java.util.Map;  
  10.   
  11. /** 
  12.  * RPC服务提供者 
  13.  * 
  14.  * @author wangzhao (mailto:wangzhao@primeton.com) 
  15.  */  
  16. public class Provider {  
  17.   
  18.     public static void main(String[] args) throws Exception {  
  19.         ServerSocket serverSocket = new ServerSocket(1234);  
  20.         Map<Object,Object> services = new HashMap<Object,Object>();  
  21.         services.put(SayHelloService.classnew SayHelloServiceImpl());  
  22.         while(true){  
  23.             System.out.println("服务提供者启动,等待客户端调用…………");  
  24.             Socket socket = serverSocket.accept();  
  25.               
  26.             //收到消息后进行解码  
  27.             ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());  
  28.             String interfaceName = objectInputStream.readUTF();  
  29.             String methodName = objectInputStream.readUTF();  
  30.             Class<?>[] paramterTypes = (Class<?>[])objectInputStream.readObject();  
  31.             Object[] argments = (Object[])objectInputStream.readObject();  
  32.             System.out.println("客户端调用服务端接口"+interfaceName+"的"+ methodName+"方法");  
  33.               
  34.             //根据解码结果调用本地的服务  
  35.             Class serviceClass = Class.forName(interfaceName);  
  36.             Object serivce = services.get(serviceClass);  
  37.             Method method = serviceClass.getMethod(methodName, paramterTypes);  
  38.             Object result = method.invoke(serivce, argments);  
  39.               
  40.             //服务提供者发送result给服务调用者  
  41.             ObjectOutputStream stream = new ObjectOutputStream(socket.getOutputStream());  
  42.             stream.writeObject(result);  
  43.             System.out.println("服务端返回结果为"+result);  
  44.         }  
  45.     }  
  46. }</span>  

     服务提供者端事先将服务实例化好后放在services这个Map中(此处涉及服务的路由被简化处理了,后面章节会详细讲解),通过一个while循环,不断地接收到来的请求,得到所需的参数,包括接口名称、方法名称、参数类型和参数,通过Java的反射取得接口所需调用的方法,执行后将结果返回给服务的消费者。

        在真实的生产环境中,常常是多个客户端同时发送多个请求到服务端,服务端则需要同时接收和处理多个客户端请求,涉及并发处理、路由、负载均衡、监控、日志记录等功能,这个是无法满足的。下一章我将给大家分享基于HTTP协议的RPC实现的范例。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值