上文描述了服务端的实现,下面进行客户端的实现。
客户端由于不能直接通过new进行服务端调用,需要使用服务端的代理类,而代理对象为服务端的接口,在demo中将服务端的接口类IHgHello拷贝一份到客户端中。
上文中,已将服务的接口地址进行暴露,即localhost:8080端口,客户端实现服务端的代理类为RpcClientProxy。 RpcClientProxy是一个代理类,通过中动态代理实现对服务端接口的代理,它是对服务传输过程的封装。
//这是调用方法
RpcClientProxy rpcClientProxy = new RpcClientProxy();
IHgHello hgHello = rpcClientProxy.clientProxy(IHgHello.class,"localhost",8080);
public class RpcClientProxy {
public <T> T clientProxy(Class<T> interfaceCls,String host,int port){
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(),new Class<?>[]{interfaceCls},new RemoteInvocationHandler(host,port));
}
}
在代理方法中,真正处理业务的类为RemoteInvocationHandler,其实现了InvocationHandler接口:
public class RemoteInvocationHandler implements InvocationHandler {
String host;
int port;
public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RPCRequest request = new RPCRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParams(args);
RPCNetTransport rpcNetTransport = new RPCNetTransport(host,port);
return rpcNetTransport.send(request);
}
}
不同JVM中进行数据的调用,需要将数据进行序列化和反序列化。在实现过程中,对调用方法进行了封装成RPCRequest类,通过RPCNetTransport进行数据的传输。
public class RPCRequest implements Serializable{
private static final long serialVersionUID = -8532503063900121027L;
private String className;
private String methodName;
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 Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
}
public class RPCNetTransport {
String host;
int port;
public RPCNetTransport(String host, int port) {
this.host = host;
this.port = port;
}
private Socket newSocket(){
System.out.println("begin create connect ");
Socket socket = null;
try{
socket = new Socket(host,port);
}catch (Exception e){
throw new RuntimeException("build connect failed");
}
return socket;
}
public Object send (RPCRequest request){
Socket socket = null;
try{
socket = newSocket();
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(request);
outputStream.flush();
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
Object result = inputStream.readObject(); // 反序列化得到结果
inputStream.close();
outputStream.close();
return result;
}catch (Exception e){
throw new RuntimeException("send request exception:" + e);
}finally {
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
最后在启动server端服务后,调用上面的调用方法,即可实现RPC的调用。
通过这个demo的实现,让我了解了RPC的实现方式以及调用过程,不过在此过程中也有几点需要改进:
1.服务端的接口层通过maven进行打包发布,方便版本升级和客户端引用;
2.服务端的服务发布可以采用注册中心的方式,对发布中心地址进行配置化;
3.服务端在接收客户端连接可以采用netty为通信框架,防止IO阻塞;
demo加强了理解,后续在改进后继续实现,应用于项目实战。这个demo的下载地址为: