可以通过java原生的序列化,socket通信,动态代理和反射机制,实现最简单的RPC框架。
服务发布者创建服务端的serverSocket:
我之前想象调用应该是这个样子:
客户端将类名,方法名,参数名和值序列化后发给服务端,服务端获取字节信息后反序列化成java对象并对类名,方法名和参数名及值进行手动遍历做case匹配然后去调用。反射调用解决了手动匹配的问题。
本地手动序列化用动态代理解决了。
客户端:
package rpcLocal;
/**
* @description:1.将本地接口调用转换成JDK的动态代理,动态代理中实现接口的远程调用(其实就是做了个序列化而已) 2.创建socket客户端,根据指定地址连接远程服务提供者
* 3.将远程服务调用所需的接口类,方法名,参数列表等编码后发送给服务提供者
* 4.同步阻塞等待服务端返回应答,获取应答之后返回
*/
public class RpcImporter<S> {
public S importer(final Class<?> serviceClass, final InetSocketAddress addr) {
return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{serviceClass.getInterfaces()[0]}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
socket = new Socket();
socket.connect(addr);
output = new ObjectOutputStream(socket.getOutputStream());
/*序列化*/
output.writeUTF(serviceClass.getName());
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
input = new ObjectInputStream(socket.getInputStream());
return input.readObject();
} finally {
if (socket != null) {
socket.close();
}
if (output != null) {
output.close();
}
if (input != null) {
input.close();
}
}
}
});
}
}
客户端main方法:
package rpcLocal;
public class MainTest {
public static void main(String[] args) throws Exception{
new Thread(new Runnable(){
public void run(){
try{//启动服务端的发布类
RPCexporter.exporter("localhost",8002);
}catch(Exception e){
e.printStackTrace();
}
}
}).start();
RpcImporter<EchoService> importer = new RpcImporter<EchoService>();
EchoService echo = importer.importer(EchoServiceImpl.class,new InetSocketAddress("localhost",8002));
echo.echo("我是ping");
}
}
服务端发布类:
package rpc.publisher;
/**
* @description:职责:1.监听客户端的TCP连接,将其封装成Task,由线程池执行
* 2.将客户端的码流反序列化成对象,反射调用服务实现者,获取执行结果
* 3.将执行结果对象反序列化,通过socket发送给客户端
* 4.远程服务调用完成之后,释放socket等资源链接,防止句柄泄露
*/
public class RPCexporter {
private final static Logger logger = LoggerFactory.getLogger(RPCexporter.class);
static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static void exporter(String hostName,int port)throws Exception{
ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress(hostName,port));
try{
System.out.println("开始向线程池提交任务");
while(true){
//提交任务线程至线程池
executor.execute(new ExporterTask(server.accept()));
}
}
finally{
server.close();
}
}
/*内部类*/
public static class ExporterTask implements Runnable{
Socket client = null;
public ExporterTask(Socket client){
this.client = client;
}
@Override
public void run(){
ObjectInputStream input = null;
ObjectOutputStream output = null;
try{
//获取客户端输入流
input = new ObjectInputStream(client.getInputStream());
String interfaceName = input.readUTF();
System.out.println("服务端获取到的输入流——"+interfaceName);
Class<?> service = Class.forName(interfaceName);
String methodName = input.readUTF();
System.out.println("服务端获取到的输入流——"+methodName);
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
Object[] arguments = (Object[])input.readObject();
logger.info("参数类型:{},参数值:{}", JSON.toJSON(parameterTypes),JSON.toJSON(arguments));
Method method = service.getMethod(methodName,parameterTypes);
Object result = method.invoke(service.newInstance(),arguments);
output = new ObjectOutputStream(client.getOutputStream());
output.writeObject(result);
}catch(Exception e){
e.printStackTrace();
}finally{
if(output!=null){
try{
output.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(input!=null){
try{
input.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(client!=null){
try{
client.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
}
服务端服务接口:
package rpc;
public interface EchoService {
void echo(String ping);
}
服务端服务实现:
package rpc;
public class EchoServiceImpl implements EchoService{
public void echo(String ping){
System.out.println("打印ping执行:"+ping!=null?ping+"--im ok":"ping is null");
}
}