RPC入門(一):RPC java代碼簡單實現

1.RPC概念

     全程 Remote Procedure Call Protocol 即遠程調用協議。它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通信程序之間攜帶信息數據。在OSI網絡通信模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。(來自百度百科)
(圖片來源:互聯網)

  •      服務消費方(client)調用以本地調用方式調用服務;
  •     client stub接收到調用後負責將方法、參數等組裝成能夠進行網絡傳輸的消息體;
  •     client stub找到服務地址,並將消息發送到服務端;
  •     server stub收到消息後進行解碼;
  •     server stub根據解碼結果調用本地的服務;
  •     本地服務執行並將結果返回給server stub;
  •     server stub將返回結果打包成消息併發送至消費方;
  •     client stub接收到消息,並進行解碼;
  •     服務消費方得到最終結果。
如上圖:如果client調用sayHello()方法,給人的感覺就像在調用本地方法一樣,執行方法---返回數據。RPC屏蔽了底層通信、協議處理,讓調用者不用關心具體實現的細節。

2.RPC框架組成

     從上圖中我們知道RPC框架需要從下面幾方面考慮:
  •      通信模型:服務消費方消費服務提供方提供的服務時,需要進行通信?通信一般採用TCP/UDP 或者 HTTP。
  •      服務定位:客戶端該怎麼找到服務提供方的服務?一般通過:IP、端口、具體方法名。
  •      遠程對象代理:消費方如何調用RPC服務?實際上是通過遠程方法的本地代理實現調用的。
  •      序列化:調用時如何傳遞數據?我們知道底層通信都是傳輸的字節流,因此我們需要將傳遞的對象序列化進行傳輸。


3.RPC框架的JAVA的簡單實現

  •      通信模型:我們可以採用java的BIO或者NIO實現tcp連接。
  •      服務定位:我們只是簡單實現直接寫死 端口 和 ip,不去實現發現服務。
  •      遠程對象代理:java中可以實現字節碼或者動態代理實現。
  •      序列化:java原生序列化實現。

rpc-interface:服務接口定義。
rpc-provider:服務提供者,實現服務接口,依賴interface。
rpc-client: 服務消費方,依賴interface。

3.1 接口定義

package cc.hu.test.rpc.facade;

public interface HelloRPCService {
	String sayHello(String name);
}

3.2:服務提供

服務接口實現:
package cc.hu.test.rpc.provider.impl;

import cc.hu.test.rpc.facade.HelloRPCService;

public class HelloRPCServiceImpl implements HelloRPCService {

	@Override
	public String sayHello(String name) {
		return "Hi, RPC  I  want " + name;
	}

}

服務server接口定義:
package cc.hu.test.rpc.provider;

public interface RPCProvider {
	/**停止服務*/
	void stop();
	/**啓動RPC服務*/
	void start();
	/**獲得服務端口*/
	int getPort();
	/**註冊服務*/
	void regist(Class<?> serviceInterface, Class<?> serviceImpl);
	
	boolean isRunning();
}

服務server接口實現:

package cc.hu.test.rpc.provider;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RPCProviderImpl implements RPCProvider {
	
//	private static ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//使用線程池
	
	private static Map<String, Class<?>> SERVICE_MAP = new HashMap<String, Class<?>>();
	
	private boolean isStop = false;
	
	private static int port = 28080;
	
	public static Class<?> getServiceClass(String serviceName) {
		return SERVICE_MAP.get(serviceName);
	}

	@Override
	public void stop() {
		this.isStop = true;
	}

	@Override
	public void start() {
		ServerSocket serverSocket = null;
		try {
			serverSocket = new ServerSocket(port);
			System.out.println("開啓RPC測試服務@" + String.valueOf(port));
			while (!isStop) {
				Thread t = new Thread(new RPCProviderHandler(serverSocket.accept()));//每一次調用新建一個線程
				t.start();
				//exec.execute(new RPCProviderHandler(serverSocket.accept()) );//使用線程池
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != serverSocket)
				try {
					serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		
	}

	@Override
	public int getPort() {
		return port;
	}

	@Override
	public void regist(Class<?> serviceInterface, Class<?> serviceImpl) {
		SERVICE_MAP.put(serviceInterface.getName(), serviceImpl);
	}

	@Override
	public boolean isRunning() {
		return !isStop;
	}

}

服務server調用處理:
package cc.hu.test.rpc.provider;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;

public class RPCProviderHandler implements Runnable {
	
	Socket socket;
	
	public RPCProviderHandler(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		ObjectInputStream input = null;
		ObjectOutputStream output = null;
		
		try {
			input = new ObjectInputStream(socket.getInputStream());
			String serviceName = input.readUTF();
			String methodName = input.readUTF();
			Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
			Object[] parameters = (Object[]) input.readObject(); 
			
			Class<?> serviceImpl = RPCProviderImpl.getServiceClass(serviceName);//獲取實現類
			if (null == serviceImpl)
				throw new ClassNotFoundException(serviceName);
			Method method = serviceImpl.getMethod(methodName, parameterTypes);//獲取調用方法
			String result = (String) method.invoke(serviceImpl.newInstance(), parameters);
			//將執行調用後的結果輸出
			output = new ObjectOutputStream(socket.getOutputStream());
			output.writeObject(result);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} finally {
				try {
					if (output != null) {
						output.flush();
						output.close();
					}
					if (input != null)
						input.close();
					if (socket != null)
						socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		
	}

}
服務Main:
package cc.hu.test.rpc.provider;

import cc.hu.test.rpc.facade.HelloRPCService;
import cc.hu.test.rpc.provider.impl.HelloRPCServiceImpl;

/**
 * RPC
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    	RPCProvider provider = new RPCProviderImpl();
    	provider.regist(HelloRPCService.class, HelloRPCServiceImpl.class);//註冊rpc服務
    	provider.start();//啓動服務端
    }
}

3.3 消費方:

消費方代理實現:
package cc.hu.test.rpc.client;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class RPCClient<T> {
	
	@SuppressWarnings("unchecked")
	public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final int port, final String address) {
		
		return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() {
			
			@SuppressWarnings("resource")
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Socket socket = null;
				ObjectInputStream input = null;
				ObjectOutputStream output = null;
				socket = new Socket(address, port);
				output = new ObjectOutputStream(socket.getOutputStream());
				output.writeUTF(serviceInterface.getName());
				output.writeUTF(method.getName());
				output.writeObject(method.getParameterTypes());
				output.writeObject(args);
				input = new ObjectInputStream(socket.getInputStream());
				String callRet = (String) input.readObject();
				return callRet;
			}
		});
		
	}
}
消費方main
package cc.hu.test.rpc.client;

import java.io.IOException;

import cc.hu.test.rpc.facade.HelloRPCService;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        HelloRPCService service = RPCClient.getRemoteProxyObj(HelloRPCService.class, 28080, "192.168.2.62");
        byte[] buffer = new byte[512];
        try {
        	System.out.print("請輸入任意字符:");
			System.in.read(buffer);
			String name = new String(buffer);
			System.out.println(service.sayHello(name));
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
}

只是簡單模擬RPC調用,沒有考慮重連、性能、服務發現、註冊中心等機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章