java 认识RPC

什么是远程调用

RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Http invoker等。另外,RPC是与语言无关的。

RPC示意图

 

  1. 服务消费方(client)调用以本地调用方式调用服务;
  2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
  3. client stub找到服务地址,并将消息发送到服务端;
  4. server stub收到消息后进行解码;
  5. server stub根据解码结果调用本地的服务;
  6. 本地服务执行并将结果返回给server stub;
  7. server stub将返回结果打包成消息并发送至消费方;
  8. client stub接收到消息,并进行解码;
  9. 服务消费方得到最终结果。 

 

RPC 调用分类

RPC 调用的分类方式有很多种。

从通信协议层面可以分为:

基于 HTTP 协议的 RPC;

基于二进制协议的 RPC;

基于 TCP 协议的 RPC。

从是否跨平台可分为:

单语言 RPC,如 RMI, Remoting;

跨平台 RPC,如 google protobuffer, restful json,http XML。

从调用过程来看,可以分为同步通信RPC和异步通信RPC:

同步 RPC:指的是客户端发起调用后,必须等待调用执行完成并返回结果;

异步 RPC:指客户方调用后不关心执行结果返回,如果客户端需要结果,可用通过提供异步 callback 回调获取返回信息。大部分 RPC 框架都同时支持这两种方式的调用。

RPC 的实现方式有很多,可以基于常见的 HTTP 协议,也可以在TCP上层封装自定义协议,常见的 Web Service 就是基于 HTTP 协议的 RPC,HTTP 协议的优点是具有良好的跨平台性,特别适合异构系统较多的公司,但是由于 HTTP 报头较为冗长,性能较差,基于 TCP 协议的 RPC 可以建立长连接,速度和效率明显,但是难度和复杂程度很高。

我们所说的远程调用 基于网络通信,目前有两种常用IO通信模型:1)BIO;2)NIO。一般RPC框架需要支持这两种IO模型。

  1. 使用java nio方式自研,这种方式较为复杂,而且很有可能出现隐藏bug,但也见过一些互联网公司使用这种方式;
  2. 基于netty,现在很多RPC框架都直接基于netty这一IO通信框架,省力又省心,比如阿里巴巴的HSF、dubbo,Twitter的finagle等。

RPC调用的原理实现

   上代码:定义一个HelloService的接口及其实现类HelloServiceImpl

  •   
      public interface HelloService {
           public String hello(String name);
        }
    
    
    
    public class HelloServiceImpl implements HelloService{
    	 @Override
    	 public String hello(String name) {
    			return "Hello, " + name;
    	 }
    }

    定义PRCServer,利用到了线程池及其反射

  • public class PRCServer {
    	private ExecutorService threadPool;
    	private static final int DEFAULT_THREAD_NUM = 10;
    	
    	public PRCServer(){
    		threadPool = Executors.newFixedThreadPool(DEFAULT_THREAD_NUM);
    	}
    	
    	public void register(Object service, int port){
    		try {
    			System.out.println("server starts...");
    			ServerSocket server = new ServerSocket(port);
    			Socket socket = null;
    			while((socket = server.accept()) != null){
    				System.out.println("client connected...");
    				threadPool.execute(new Processor(socket, service));
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	class Processor implements Runnable{
    		Socket socket;
    		Object service;
    		
    		public Processor(Socket socket, Object service){
    			this.socket = socket;
    			this.service = service;
    		}
    		public void process(){
    			
    		}
    		@Override
    		public void run() {
    			try {
    				ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
    				String methodName = in.readUTF();
    				Class<?>[] parameterTypes = (Class<?>[]) in.readObject();
    				Object[] parameters = (Object[]) in.readObject();
    				Method method = service.getClass().getMethod(methodName, parameterTypes);
    				try {
    					Object result = method.invoke(service, parameters);
    					ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    					out.writeObject(result);
    				} catch (IllegalAccessException e) {
    					e.printStackTrace();
    				} catch (IllegalArgumentException e) {
    					e.printStackTrace();
    				} catch (InvocationTargetException e) {
    					e.printStackTrace();
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    			} catch (NoSuchMethodException e) {
    				e.printStackTrace();
    			} catch (SecurityException e) {
    				e.printStackTrace();
    			} catch (ClassNotFoundException e1) {
    				e1.printStackTrace();
    			}
    		}
    	}
    }
    

    定义PRCClient

  • public class PRCClient {
    	public static void main(String args[]){
    		HelloService helloService = getClient(HelloService.class, "127.0.0.1", 50002);
    		System.out.println(helloService.hello("jim"));
    	}
    	
    	@SuppressWarnings("unchecked")
    	public static <T> T getClient(Class<T> clazz, String ip, int port){
    		return  (T) Proxy.newProxyInstance(PRCClient.class.getClassLoader(), new Class<?>[]{clazz}, new InvocationHandler() {
    			
    			public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
    				Socket socket = new Socket(ip, port);
    				ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    				out.writeUTF(arg1.getName());
    				out.writeObject(arg1.getParameterTypes());
    				out.writeObject(arg2);
    				ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
    				return in.readObject();
    			}
    		});
    	}
    }

    定有一个启动类

  • public class Main {
    	public static void main(String args[]){
    		HelloService helloService = new HelloServiceImpl();
    		
    		PRCServer server = new PRCServer();
    		server.register(helloService, 50002);
    	}
    }
    

    启动main,再启动PRClient即可。

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值