一、前言
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
二、实现思路
之前看了一下dubbo的底层实现,发现dubbo的服务调用是通过动态代理+socket进行远程调用的。它通过javassit直接根据接口生成相关的代理类的字节码文件并加载到内存中,然后通过NIO非阻塞式的进行socket的服务传输和调用。所以,下面将会利用javassit+socket+io实现一个简单的rpc服务。
三、消费端代码
1,构建实力类,用作消息的传输。
public class MsgBean implements Serializable {
// 调用方的类名称
private String className;
//调用方法名
private String methodName;
//方法的参数类型
private Class[] methodParmType;
//方法参数
private Object[] params;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Class[] getMethodParmType() {
return methodParmType;
}
public void setMethodParmType(Class[] methodParmType) {
this.methodParmType = methodParmType;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
}
2.构建接口代理类的父类,其中rpcRuest()方法用于构建msgBean,并通过socket连接远程的服务获取方法调用结果
public class ProxyBase {
/**
* 通过socker请求获取数据信息
* @param className
* @param paramTypes
* @param returnType
* @param args
* @return
*/
public static Object rpcQuest(String className,String methodName, String paramTypes,String returnType,Object[] args){
Object obj = null;
try{
MsgBean msgBean = new MsgBean();
msgBean.setClassName(className);
msgBean.setMethodName(methodName);
String[] parTypes = paramTypes.split(",");
Class[] classes = new Class[parTypes.length];
for (int i = 0; i < parTypes.length; i++) {
classes[i] = getClass(parTypes[i]);
}
msgBean.setMethodParmType(classes);
msgBean.setParams(args);
Socket socket = new Socket("localhost",10086);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(msgBean);
socket.shutdownOutput();
if(!returnType.equals("void")){
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
obj = in.readObject();
socket.shutdownInput();
in.close();
}
out.close();
socket.close();
}catch (Exception e){
e.printStackTrace();
}
return obj;
}
/**
* 返回数据
* @param obj
* @param T
* @param
* @return
*/
public static T returnVal(Object obj ,Class T){
return (T)obj;
}
/**
* 根据名称获取class对象
* @param className
* @return
* @throws ClassNotFoundException
*/
public static Class getClass(String className) throws ClassNotFoundException {
return Class.forName(className);
}
3、构建生成代理类的工厂
package main.rpc.consumer;
import javassist.*;
import java.util.Map;
public class ProxyFactory {
private Class t;
public ProxyFactory(Class t){
this.t = t;
}
private static final String PROXYFREIX = "$ProxyFactory";
private static final String PROXYSUFFIX = "Impl";
public T getProxyObject(){
T proxyObject = null;
try{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(getPackName()+"."+getProxyObjectName());
//设置代理类的父类
ctClass.setSuperclass(pool.getCtClass(ProxyBase.class.getName()));
//设置代理类继承的接口
CtClass inter = pool.getCtClass(getPackName()+"."+t.getSimpleName());
CtClass[] intes = new CtClass[]{inter};
ctClass.setInterfaces(intes);
//测试新增的方法
CtMethod ctNewMethod = CtNewMethod.make("public void test(){ System.out.println(\"新加的test方法\"); }",ctClass);
ctClass.addMethod(ctNewMethod);
//实现接口的各个方法
CtMethod[] methods = inter.getDeclaredMethods();
for (CtMethod method : methods) {
CtMethod cm = new CtMethod(method.getReturnType(),method.getName(),method.getParameterTypes(),ctClass);
String returnType = method.getReturnType().getName();
String paramsTypes = "";
for (CtClass aClass : method.getParameterTypes()) {
paramsTypes=paramsTypes+aClass.getName()+",";
}
paramsTypes = paramsTypes.substring(0,paramsTypes.lastIndexOf(","));
StringBuilder sb = new StringBuilder("{");
sb.append("String className=\""+t.getName()+"\";");
sb.append("String methodName=\""+method.getName()+"\";");
sb.append("String returnType=\""+returnType+"\";");
sb.append("String paramsTypes=\""+paramsTypes+"\";");
sb.append("Object[] args = new Object[]{");
for(int i=1;i<=method.getParameterTypes().length;i++){
sb.append("$"+i);
if(i!=method.getParameterTypes().length){
sb.append(",");
}
}
sb.append("};");
sb.append("Object obj=quest(className,methodName,paramsTypes,returnType,args");
sb.append(");");
if(!returnType.equals("void")){
sb.append("return ("+returnType+")obj;");
}
sb.append("}");
cm.setBody(sb.toString());
ctClass.addMethod(cm);
}
Class aClass = ctClass.toClass();
proxyObject = (T)aClass.newInstance();
}catch (Exception e){
e.printStackTrace();
}
return proxyObject;
}
/**
* 获取报名
* @return
*/
public String getPackName(){
Package pag = t.getPackage();
return pag.getName();
}
/**999
* 获取代理对象的名称
* @return
*/
public String getProxyObjectName(){
return PROXYFREIX+t.getSimpleName()+PROXYSUFFIX;
}
public static void main(String[] args) throws InterruptedException {
ProxyFactory proxy = new ProxyFactory<>(Student.class);
Student student = proxy.getProxyObject();
Map result = student.handupTask("HEllo 客户端");
System.out.println(result);
}
4、测试的接口
package main.rpc.consumer;
import java.util.Map;
/**
* 调用方接口
*/
public interface Student {
Map handupTask(String str);
}
四、生产端代码
1、接口的实现类
package main.rpc.product;
import java.util.HashMap;
import java.util.Map;
public class StudentIml implements Student {
@Override
public Map handupTask(String str) {
Map result = new HashMap<>();
result.put("你好啊","6666");
result.put("测试通过啦","ouye");
return result;
}
}
2、socket 服务端
package main.rpc.product;
import main.rpc.consumer.MsgBean;
import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class SocketServer {
private static Map propertis = new HashMap<>();
static {
propertis.put("main.rpc.Student","main.rpc.product.StudentIml");
}
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(10086);
System.out.println("等待客户端连接...");
while (true){
Socket socket =serverSocket.accept();
System.out.println("客户端连接成功");
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
//获取接收的方法调用信息
MsgBean msg = (MsgBean) inputStream.readObject();
System.out.println("接收导的消息为:========"+msg);
socket.shutdownInput();
//根据方法的信息利用反射执行方法
Class cl = Class.forName(propertis.get(msg.getClassName()));
Method method = cl.getMethod(msg.getMethodName(),msg.getMethodParmType());
Object obj = method.invoke(cl.newInstance(),msg.getParams());
//如果方法不是void,则将结果回写客户端
if(!method.getReturnType().getName().equals("void")){
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(obj);
socket.shutdownOutput();
out.close();
}
inputStream.close();
}
}
}
五、结束语
基本思路就是消费者和生产者建立一个socket连接,消费方把调用的接口信息,方法名称,还有参数封装为一个对象,通过流传输到生产方,生产方根据方法调用的信息通过反射执行具体的方法,然后将结果回馈给消费者。