RPC框架稱為遠端呼叫框架,其實現的核心原理就是消費者端使用動態代理來代理一個介面的方法(基於JDK的動態代理,當然如果使用CGLib可以直接使用無介面類別的方法),透過加入網路傳輸編程,傳輸呼叫介面方法名稱,方法參數來給提供者獲取,再透過反射,來執行該介面的方法,再將反射執行的結果透過網路程式傳回消費者端。
現在我們來依序實作這些概念。這裡我們做最簡單的實作,網路程式設計使用的是BIO,大家可以使用Reactor模式的Netty來改寫效能更好的方式。而網路傳輸所使用的序列化和反序列化也是Java自帶的,當然這樣的傳輸位元組比較大,可以用google的protoBuffer或是kryo來處理。 這裡只為了方便說明原理。
pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.guanjian</groupId> <artifactId>rpc-framework</artifactId> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>
首先當然是我們要進行遠端呼叫的介面以及介面的方法。
public interface HelloService { String sayHello(String content);}
介面實作類別
public class HelloServiceImpl implements HelloService { public String sayHello(String content) { return "hello," + content; } }
消費者端的動態代理,如果你是把提供者和消費者寫在兩個工程中,則提供者端需要上面的接口和實作類,而消費者端只需要上面的介面。
public class ConsumerProxy { /** * 消费者端的动态代理 * @param interfaceClass 代理的接口类 * @param host 远程主机IP * @param port 远程主机端口 * @param <T> * @return */ @SuppressWarnings("unchecked") public static <T> T consume(final Class<T> interfaceClass,final String host,final int port) { return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, (proxy,method,args) -> { //创建一个客户端套接字 Socket socket = new Socket(host, port); try { //创建一个对外传输的对象流,绑定套接字 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); try { //将动态代理的方法名写入对外传输的对象流中 output.writeUTF(method.getName()); //将动态代理的方法的参数写入对外传输的对象流中 output.writeObject(args); //创建一个对内传输的对象流,绑定套接字 //这里是为了获取提供者端传回的结果 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); try { //从对内传输的对象流中获取结果 Object result = input.readObject(); if (result instanceof Throwable) { throw (Throwable) result; } return result; } finally { input.close(); } } finally { output.close(); } } finally { socket.close(); } } ); } }
有關JDK動態代理的內容可以參考AOP原理與自實現,BIO的部分可以參考傳統IO與NIO比較
提供者端的網路傳輸與遠端方式呼叫服務
public class ProviderReflect { private static final ExecutorService executorService = Executors.newCachedThreadPool(); /** * RPC监听和远程方法调用 * @param service RPC远程方法调用的接口实例 * @param port 监听的端口 * @throws Exception */ public static void provider(final Object service,int port) throws Exception { //创建服务端的套接字,绑定端口port ServerSocket serverSocket = new ServerSocket(port); while (true) { //开始接收客户端的消息,并以此创建套接字 final Socket socket = serverSocket.accept(); //多线程执行,这里的问题是连接数过大,线程池的线程数会耗尽 executorService.execute(() -> { try { //创建呢一个对内传输的对象流,并绑定套接字 ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); try { try { //从对象流中读取接口方法的方法名 String methodName = input.readUTF(); //从对象流中读取接口方法的所有参数 Object[] args = (Object[]) input.readObject(); Class[] argsTypes = new Class[args.length]; for (int i = 0;i < args.length;i++) { argsTypes[i] = args[i].getClass(); } //创建一个对外传输的对象流,并绑定套接字 //这里是为了将反射执行结果传递回消费者端 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); try { Class<?>[] interfaces = service.getClass().getInterfaces(); Method method = null; for (int i = 0;i < interfaces.length;i++) { method = interfaces[i].getDeclaredMethod(methodName,argsTypes); if (method != null) { break; } } Object result = method.invoke(service, args); //将反射执行结果写入对外传输的对象流中 output.writeObject(result); } catch (Throwable t) { output.writeObject(t); } finally { output.close(); } } catch (Exception e) { e.printStackTrace(); } finally { input.close(); } } finally { socket.close(); } } catch (Exception e) { e.printStackTrace(); } }); } } }
啟動提供者端的網路偵聽和遠端呼叫
public class RPCProviderMain { public static void main(String[] args) throws Exception { HelloService service = new HelloServiceImpl(); ProviderReflect.provider(service,8083); } }
啟動消費者的動態代理呼叫
public class RPCConsumerMain { public static void main(String[] args) throws InterruptedException { HelloService service = ConsumerProxy.consume(HelloService.class,"127.0.0.1",8083); for (int i = 0;i < 1000;i++) { String hello = service.sayHello("你好_" + i); System.out.println(hello); Thread.sleep(1000); } } }
運行結果
hello,你好_0
hello,你好_1
hello,你好_2
hello,你好_3
hello,你好_4
hello,你好_5
.....
如果你要擴充成一個Netty ProtoBuffer的高效能RPC框架可以參考Netty整合Protobuffer 的相關寫法。
推薦教學:《PHP》
以上是Java 手寫一個RPC框架的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

禪工作室 13.0.1
強大的PHP整合開發環境

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中