RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Http invoker等。另外,RPC是与语言无关的。
下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理与反射与Java原生的序列化。
RPC架构分为三部分:
1)服务提供者,运行在服务器端,提供服务接口定义与服务实现类。
2)服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。
3)服务消费者,运行在客户端,通过远程代理对象调用远程服务。
3、 具体实现
服务接口定义类
HelloProvider.java
package org.rpc.provider;
/**
* @ClassName: HelloProvider
* @Description:TODO(对外提供接口)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:12:59
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
public interface HelloProvider {
String sayHello(String name);
Person getPerson(String name);
}
公共网络通信类
NetModel.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: NetModel.java
* @Package org.rpc.provider
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:14:05
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.provider;
import java.io.Serializable;
import java.util.Arrays;
/**
* @ClassName: NetModel
* @Description:TODO(公共网络通信类,通过序列化该类,将客户端调用接口、方法、参数、参数类型封装,然后服务端反序列化,再通过反射,调取相应实现类的方法。)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:14:05
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
public class NetModel implements Serializable{
private static final long serialVersionUID = 1L;
private String className; //接口名
private String method; //方法名
private Object[] args ; //参数
private String[] types; //参数类型
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
public String[] getTypes() {
return types;
}
public void setTypes(String[] types) {
this.types = types;
}
@Override
public String toString() {
return "NetModel [className=" + className + ", method=" + method + ", args=" + Arrays.toString(args)
+ ", types=" + Arrays.toString(types) + "]";
}
}
序列化工具类
SerializeUtils.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: SerializeUtils.java
* @Package org.rpc.provider
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:14:22
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.provider;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @ClassName: SerializeUtils
* @Description:TODO(序列化工具类)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:14:22
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
public class SerializeUtils {
/**
* 序列化
* @param object
* @return
* @throws IOException
*/
public static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(object);
outputStream.flush();
byte[] byteArray = os.toByteArray();
outputStream.close();
os.close();
return byteArray;
}
/**
* 反序列化
* @param buf
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Object deSerialize(byte[] buf) throws IOException, ClassNotFoundException {
ByteArrayInputStream is = new ByteArrayInputStream(buf);
ObjectInputStream inputStream = new ObjectInputStream(is);
Object object = inputStream.readObject();
inputStream.close();
is.close();
return object;
}
}
测试实体类
Person.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: Person.java
* @Package org.rpc.provider
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:13:51
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.provider;
import java.io.Serializable;
/**
* @ClassName: Person
* @Description:TODO(测试实体类)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:13:51
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
//普通公共bean
public class Person implements Serializable{
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
服务中心
HelloServiceImpl.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: HelloServiceImpl.java
* @Package org.rpc.server
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:16:02
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.server;
import org.rpc.provider.HelloProvider;
import org.rpc.provider.Person;
/**
* @ClassName: HelloServiceImpl
* @Description:TODO(接口实现类)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:16:02
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
public class HelloServiceImpl implements HelloProvider{
/**
* Title: sayHello
* Description:
* @param name
* @return
* @see org.rpc.provider.HelloProvider#sayHello(java.lang.String)
*/
public String sayHello(String name) {
return "hello,"+name;
}
/**
* Title: getPerson
* Description:
* @param name
* @return
* @see org.rpc.provider.HelloProvider#getPerson(java.lang.String)
*/
public Person getPerson(String name) {
Person person = new Person();
person.setName(name);
person.setAge(25);
return person;
}
}
Server服务端 rpc-server
RPCServer.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: RPCServer.java
* @Package org.rpc.server
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:20:42
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.server;
import java.io.FileInputStream;
/**
* @ClassName: RPCServer
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:20:42
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.rpc.provider.NetModel;
import org.rpc.provider.SerializeUtils;
//Server服务端,这里使用JDK自带的ServerSocket来进行进行,服务端收到数据后,对数据进行处理,处理完把结构返回给客户端
public class RPCServer {
public static void main(String[] args)
{
try {
openServer();
} catch (Exception e) {
e.printStackTrace();
}
}
//这个方法用来启动服务端,然后接受数据,返回处理完的结果
public static void openServer() throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
try {
System.out.println("服务开启");
while(true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress()+"-connected");
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
in.read(buf);
byte[] formatDate = formatData(buf);
OutputStream out = socket.getOutputStream();
out.write(formatDate);
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
serverSocket.close();
}
}
/*这个方法用来处理接收到的数据,通过反序列化得到传过来的通信类NetModel,然后得到接口名,方法名,参数,参数类型,
最后通过JDK反射,来调取实现类的方法,并将调取结果,序列化为byte数组,然后返回
*/
public static byte[] formatData(byte[] bs){
try {
//将收到的byte数组反序列化为NetModel类型,然后通过反射调用HelloServiceImpl实现类的方法
NetModel netModel = (NetModel)SerializeUtils.deSerialize(bs);
String className = netModel.getClassName();
String[] types = netModel.getTypes();
Object[] args = netModel.getArgs();
/*这里简单通过Map来做接口映射到实现类,从map中取
Map map = new HashMap();
map.put("org.Simple.API.HelloService", "org.Simple.S.HelloServiceImpl");
Class> clazz = Class.forName(map.get(className));
*/
//也可以把这个键值放到配置文件下,通过配置文件读取
Class> clazz = Class.forName(getPropertyValue(className));
Class> [] typeClazzs = null;
if(types!=null) {
typeClazzs = new Class[types.length];
for (int i = 0; i < typeClazzs.length; i++) {
typeClazzs[i] = Class.forName(types[i]);
}
}
Method method = clazz.getMethod(netModel.getMethod(),typeClazzs);
Object object = method.invoke(clazz.newInstance(), args);
byte[] byteArray = SerializeUtils.serialize(object);
return byteArray;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//通过key去properties文件中取值
public static String getPropertyValue(String key) throws IOException {
Properties properties = new Properties();
FileInputStream in = new FileInputStream("src/main/resources/config.properties");
properties.load(in);
in.close();
return properties.getProperty(key);
}
}
config.properties
org.rpc.provider.HelloProvider=org.rpc.server.HelloServiceImpl
RPC客户端rpc-client
RPCClient.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: RPCClient.java
* @Package org.rpc.client
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:22:14
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.client;
/**
* @ClassName: RPCClient
* @Description:TODO(这里用一句话描述这个类的作用)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:22:14
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import org.rpc.provider.HelloProvider;
import org.rpc.provider.SerializeUtils;
/*RPC客户端,也是用Socket与服务端进行通信*/
public class RPCClient {
//Socket发送消息给服务端,并反序列化服务端返回的数据,返回给方法调用者
public static Object send(byte[] bs) {
try {
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream outputStream = socket.getOutputStream();
outputStream.write(bs);
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
in.read(buf);
Object formatDate = SerializeUtils.deSerialize(buf);
socket.close();
return formatDate;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//main方法,运行客户端
public static void main(String[] args) {
HelloProvider helloService = ProxyFactory.getInstance(HelloProvider.class);
System.out.println("say:"+helloService.sayHello("zhangsan"));
System.out.println("Person:"+helloService.getPerson("zhangsan"));
}
}
ProxyFactory.java
/**
* All rights Reserved, Designed By www.tydic.com
* @Title: ProxyFactory.java
* @Package org.rpc.client
* @Description: TODO(用一句话描述该文件做什么)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:23:50
* @version V1.0
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*/
package org.rpc.client;
/**
* @ClassName: ProxyFactory
* @Description:TODO(代理工厂类)
* @author: Jimu
* @email: [email protected]
* @date: 2019年3月25日 下午2:23:50
*
* @Copyright: 2019 www.maker-win.net Inc. All rights reserved.
*
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.rpc.provider.NetModel;
import org.rpc.provider.SerializeUtils;
public class ProxyFactory {
private static InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
NetModel netModel = new NetModel();
Class>[] classes = proxy.getClass().getInterfaces();
String className = classes[0].getName();
netModel.setClassName(className);
netModel.setArgs(args);
netModel.setMethod(method.getName());
String [] types = null;
if(args!=null) {
types = new String [args.length];
for (int i = 0; i < types.length; i++) {
types[i] = args[i].getClass().getName();
}
}
netModel.setTypes(types);
byte[] byteArray = SerializeUtils.serialize(netModel);
Object send = RPCClient.send(byteArray);
return send;
}
};
@SuppressWarnings("unchecked")
public static T getInstance(Class clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(),
new Class[]{clazz}, handler );
}
}
启动Server服务端
参考链接
https://www.cnblogs.com/codingexperience/p/5930752.html
https://blog.csdn.net/u013034378/article/details/80686233