这是一个系列文章,将会从一个简单的rpc开始,介绍Dubbo的使用和基本原理。
IPC与RPC
RPC全称是远端过程调用,与之相对应的是本地过程调用,即进程间通信。在Linux环境下,进程间通讯可以通过管道,共享内存,信号量,Socket套接字,消息队列或信号量实现。RPC的一般流程是通过组件序列化请求,走网络到服务端,执行真正的服务代码,然后将结果返回给客户端,反序列化数据给调用方法。由于通讯细节被动态代理隐藏,因此整个调用过程就像是在本地调用一样。
通用RPC框架
- Proxy:封装服务端API,代理Client的请求,远程请求真正的方法并返回给Client。从Client的角度看,与调用本地方法没有任何区别。
- processor:通过反射机制定位具体的对象和方法。
- protocol:RPC协议,包括编解码,字节流解析和序列化、反序列化的工作。
- Remote:网络通讯相关功能,可以使用TCP,UDP,HTTP等实现。
简单RPC实现
Client Remote
使用socket进行网络通讯
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Arrays;
public class ClientRemoter {
public static final ClientRemoter client = new ClientRemoter();
public byte[] getDataRemote(byte[] requestData)throws Exception{
try (Socket socket = new Socket()){
socket.connect(new InetSocketAddress("127.0.0.1",9999));
socket.getOutputStream().write(requestData);
socket.getOutputStream().flush();
byte[] data = new byte[1024];
int len = socket.getInputStream().read(data);
return Arrays.copyOfRange(data,0,len);
}
}
}
Server Remote
监听外部请求,接收响应数据
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.*;
public class ServerRemoter {
public static BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(3);
private static final ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(2
, 4
, 60
, TimeUnit.SECONDS
, blockingQueue
);
public void startServer(int port) throws IOException {
final ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
try {
while (true) {
Socket socket = serverSocket.accept();
threadPoolExecutor.execute(new MyRunable(socket));
}
} finally {
serverSocket.close();
}
}
private static class MyRunable implements Runnable {
private Socket socket;
public MyRunable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (InputStream is = socket.getInputStream(); OutputStream out = socket.getOutputStream()) {
byte[] data = new byte[1024];
int len = is.read(data);
ServiceProtocol.ProtocolModel model = (ServiceProtocol.ProtocolModel)ServiceProtocol.protocol.decode(Arrays.copyOfRange(data, 0, 1024)
, ServiceProtocol.ProtocolModel.class);
Object object = ServiceProcessor.processor.process(model);
} catch (Exception e) {
}
}
}
}
Protocol
使用Hessian2进行序列化和反序列化
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import lombok.Data;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@Data
public class ServiceProtocol {
public static final ServiceProtocol protocol = new ServiceProtocol();
private Hessian2Output hessian2Output;
private Hessian2Input hessian2Input;
public byte[] encode(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
hessian2Output = new Hessian2Output(baos);
hessian2Output.writeObject(o);
hessian2Output.close();
return baos.toByteArray();
}
public Object decode(byte[] data, Class clazz) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
hessian2Input = new Hessian2Input(bais);
Object o = hessian2Input.readObject(clazz);
hessian2Input.close();
return o;
}
@Data
public static class ProtocolModel {
private String clazz;
private String method;
private String[] argTypes;
private Object[] args;
}
}
Proxy
代理服务, 将请求的类名和方法名打包
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Proxy {
public static<T> T getInstance(Class<T> clazz){
return (T) Proxy.newProxyInstance(clazz.getClassLoader()
,new Class[]{clazz},new ServiceProxy(clazz));
}
public static class ServiceProxy implements InvocationHandler{
private Class clazz;
public ServiceProxy(Class clazz) {
this.clazz = clazz;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ServiceProtocol.ProtocolModel model= new ServiceProtocol.ProtocolModel();
model.setClazz(clazz.getName());
model.setMethod(method.getName());
model.setArgs(args);
String[] argType = new String[method.getParameterTypes().length];
for (int i = 0; i < argType.length; i++) {
argType[i] = method.getParameterTypes()[i].getName();
}
model.setArgTypes(argType);
byte[] req = ServiceProtocol.protocol.encode(model);
byte[] rsp = ClientRemoter.client.getDataRemote(req);
return ServiceProtocol.protocol.decode(rsp,method.getReturnType());
}
}
}
Processor
通过反射执行对应的方法
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ServiceProcessor {
public static final ServiceProcessor processor = new ServiceProcessor();
private static final ConcurrentMap<String, Object> PROCESSOR_INSTANCE_MAP = new ConcurrentHashMap<String, Object>();
public boolean publish(Class clazz, Object obj) {
return PROCESSOR_INSTANCE_MAP.putIfAbsent(clazz.getName(), obj) != null;
}
public Object process(ServiceProtocol.ProtocolModel model) throws Exception {
try {
Class clazz = Class.forName(model.getClazz());
Class[] types = new Class[model.getArgTypes().length];
for(int i = 0;i < types.length;i++){
types[i] = Class.forName(model.getArgTypes()[i]);
}
Method method = clazz.getMethod(model.getMethod(),types);
Object obj = PROCESSOR_INSTANCE_MAP.get(model.getClazz());
return method.invoke(obj,model.getArgs());
}catch (Exception e){
return null;
}
}
}
Client
客户端
public class Client {
public static void main(String[] args) {
RpcService rpcService = ServiceProxyClient.getInstance(RpcService.class);
rpcService.sayHello("content");
}
}
Server
服务端
public interface RpcService {
String sayHi(String name);
}
public class RpcServiceImpl implements RpcService {
public String sayHi(String name) {
return "Hello," + name;
}
}
测试
/**
* 服务端测试main执行代码
*/
public class ServerDemo {
public static void main(String[] args) throws Exception {
// 发布接口
ServiceProcessor.processor.publish(RpcService.class,new RpcServiceImpl());
// 启动server
ServerRemoter remoter = new ServerRemoter();
remoter.startServer(9999);
}
}