1.RPC概念
- 服務消費方(client)調用以本地調用方式調用服務;
- client stub接收到調用後負責將方法、參數等組裝成能夠進行網絡傳輸的消息體;
- client stub找到服務地址,並將消息發送到服務端;
- server stub收到消息後進行解碼;
- server stub根據解碼結果調用本地的服務;
- 本地服務執行並將結果返回給server stub;
- server stub將返回結果打包成消息併發送至消費方;
- client stub接收到消息,並進行解碼;
- 服務消費方得到最終結果。
2.RPC框架組成
- 通信模型:服務消費方消費服務提供方提供的服務時,需要進行通信?通信一般採用TCP/UDP 或者 HTTP。
- 服務定位:客戶端該怎麼找到服務提供方的服務?一般通過:IP、端口、具體方法名。
- 遠程對象代理:消費方如何調用RPC服務?實際上是通過遠程方法的本地代理實現調用的。
- 序列化:調用時如何傳遞數據?我們知道底層通信都是傳輸的字節流,因此我們需要將傳遞的對象序列化進行傳輸。
3.RPC框架的JAVA的簡單實現
- 通信模型:我們可以採用java的BIO或者NIO實現tcp連接。
- 服務定位:我們只是簡單實現直接寫死 端口 和 ip,不去實現發現服務。
- 遠程對象代理:java中可以實現字節碼或者動態代理實現。
- 序列化:java原生序列化實現。
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;
}
}
package cc.hu.test.rpc.provider;
public interface RPCProvider {
/**停止服務*/
void stop();
/**啓動RPC服務*/
void start();
/**獲得服務端口*/
int getPort();
/**註冊服務*/
void regist(Class<?> serviceInterface, Class<?> serviceImpl);
boolean isRunning();
}
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;
}
}
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;
}
});
}
}
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調用,沒有考慮重連、性能、服務發現、註冊中心等機制。