一、RPC
RPC(Remote Procedure Call)——远程过程调用。两台服务器A、B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能之间调用,需要通过网络来表达调用语义和传达调用的数据。
实现RPC步骤:1.客户端生成动态代理类;2.代理类发送请求;3.服务端接受请求;4.服务端处理请求;5.服务器发送处理结果给客户端。
二、项目结构图
有三个包:api、clien、server
三、api包下的IHell、NetModel、Person
IHello是接口,有方法sayHello(),getPerson()。IHello的实现类在服务端。
package api;
public interface IHello {
String sayHello(String name);
Person getPerson(String name);
}
NetModel类用于记录请求的类名,方法名,参数。需要实现序列化,把客户端请求的信息包装到NetModel中。
package api;
import java.io.Serializable;
public class NetModel implements Serializable {
private static final long serialVersionUID = -4712967162106730325L; //序列化UID
private String className; //类名
private String methodName; //方法名
private Object[] args; //参数
// 无参构造器
public NetModel() {
}
public NetModel(String className, String methodName, Object[] args) {
this.className = className;
this.methodName = methodName;
this.args = args;
}
// set、get方法
public static long getSerialVersionUID() {
return serialVersionUID;
}
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 Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
}
Person需要序列化,用于网络传输。
package api;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = -9057543404870272869L; //生成序列化UID
private int age; //年龄
private String name; //姓名
// 无参构造器
public Person(){
}
// 有参构造器
public Person(int age, String name){
this.age = age;
this.name = name;
}
// set、get方法
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// toString方法
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
四、client包下的ProxyFactory、RPCClient
ProxyFactory生成动态代理类。动态代理类每次调用方法,都会走invoke方法,此时把动态代理类父接口名称(IHello)、方法名、参数,传递给服务端。
package client;
import api.NetModel;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private static InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 传入代理类对象接口名。动态代理类会动态生成Proxy0类,此类是实现IHello接口的,需要把IHello接口名传到服务端
NetModel netModel = new NetModel(proxy.getClass().getInterfaces()[0].getName(), method.getName(), args);
Object resObj = RPCClient.send(netModel);
return resObj;
}
};
// 生成动态代理对象
public static <T> T getInstance (Class<T> clazz){
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler);
}
}
PRCClient
package client;
import api.IHello;
import api.NetModel;
import api.Person;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class RPCClient {
public static Object send(NetModel netModel) throws IOException, ClassNotFoundException {
Socket socket = new Socket("127.0.0.1", 8888);
// 对象发送给服务端
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(netModel);
out.flush();
// 接受服务端处理后的结果
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object obj = in.readObject();
// 关闭流及Socket
out.close();
in.close();
socket.close();
return obj;
}
public static void main(String[] args){
// hello指向动态代理对象
IHello hello = ProxyFactory.getInstance(IHello.class);
String str = hello.sayHello("zz");
Person person = hello.getPerson("zz");
System.out.println(str);
System.out.println(person.toString());
}
}
五、server包下的Hello、RPCServer
Hello实现IHello
package server;
import api.IHello;
import api.Person;
public class Hello implements IHello {
@Override
public String sayHello(String name) {
return "Hello," + name;
}
@Override
public Person getPerson(String name) {
Person person = new Person(12, name);
return person;
}
}
RPCServer接受请求内容,处理,发送结果给客户端。
package server;
import api.NetModel;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
public class RPCServer {
private static void openServer() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务开启...");
while (true){
Socket socket = serverSocket.accept();
System.out.println("连接成功:"+socket.getLocalAddress());
// 接受对象
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
NetModel netModel = (NetModel) in.readObject();
String className = netModel.getClassName();
String methodName = netModel.getMethodName();
Object[] args = netModel.getArgs(); //参数值
Class[] types = new Class[args.length]; //参数类型
for (int i = 0; i < types.length; i++){
types[i] = args[i].getClass();
}
// 处理
String classNameServer = getPropertyValue(netModel.getClassName());
Class clazz = Class.forName(classNameServer);
Method method = clazz.getMethod(netModel.getMethodName(), types);
// 调用方法
Object resObj = method.invoke(clazz.newInstance(), args);
// 发送对象
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(resObj);
out.flush();
// 关闭流及Socket
out.close();
in.close();
socket.close();
}
}
/**
* 读取配置文件,通过key获得value,即通过IHello接口名,得到实现类Hello名称
* @param key
* @return
* @throws IOException
*/
private static String getPropertyValue(String key) throws IOException {
Properties pro = new Properties();
FileInputStream in = new FileInputStream("javaRPC/src/config.properties");
pro.load(in);
in.close();
return pro.getProperty(key);
}
public static void main(String[] args){
try {
openServer();
} catch (Exception e) {
e.printStackTrace();
}
}
}
配置文件config.properties
api.IHello=server.Hello
六、测试
先运行服务端,再运行客户端。
服务端:
客户端: