仿照dubbo手寫一個RPC框架
dubbo介紹:
dubbo: Dubbo是一款高效能、輕量級的開源 Java RPC框架,它提供了三大核心能力:面向介面的遠端方法呼叫,智慧容錯和負載均衡,以及服務自動註冊和發現。
目的: 實現呼叫遠端服務像呼叫本地服務一樣,將呼叫過程進行封裝。在消費者端只需要一個要呼叫服務的介面,不需要實現,dubbo對該介面進行動態代理。並且支援多種呼叫協議/伺服器。
框架實現:
- 動態代理介面(核心):動態代理要呼叫服務的介面,在這裡完成了對方法的遠端呼叫,傳遞一個inovocation。
public static <T> T getProxy(final Class interfaceClass){
/*動態代理,代理介面*/
return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String mock = System.getProperty("mock");
if(mock != null && mock.startsWith("return:")){
return mock.replace("return",""); // 測試用,不呼叫遠端服務
}
Invocation invocation = new Invocation(interfaceClass.getName(), method.getName(),method.getParameterTypes(), args);
List<URL> urlList = RemoteMapRegister.get(interfaceClass.getName()); // 從註冊中心獲取到對應的URLs(可能有多臺伺服器提供服務)
/*負載均衡*/
URL url = LoadBalance.random(urlList);
Protocol protocol = ProtocolFactory.getProtocol();
return protocol.send(url,invocation);
}
});
}
- invocation類(通訊的資訊載體):包含要呼叫的介面名,方法名,引數型別,引數。
public class Invocation implements Serializable {
/*呼叫介面名*/
private String interfaceName;
/*呼叫方法名*/
private String methodName;
/*引數型別*/
private Class[] paramType;
/*引數*/
private Object[] params;
- 伺服器與通訊協議介面(通訊的方式):包含兩個方法,啟動伺服器與傳送訊息
/**
* 將多個協議抽象為一個Protocol
*/
public interface Protocol {
/*啟動伺服器*/
void start(URL url);
/**
* 傳送資料
* @param url 傳送的url
* @param invocation 傳送的資料,包裝為要給invocation
* @return
*/
String send(URL url, Invocation invocation);
}
- 使用工廠模式來獲取需要的Protocol:
public class ProtocolFactory {
public static Protocol getProtocol() throws ProtocolNotFoundException {
/*通過命令列引數來決定通訊使用dubbo還是http*/
String name = System.getProperty("protocolName");
/*預設為http請求*/
if(name == null || name.equals("")) name = "http";
switch(name) {
case "http":
return new HttpProtocol();
case "dubbo":
return new DubboProtocol();
default:
throw new ProtocolNotFoundException("此協議暫不支援");
}
}
}
-
註冊中心:
記錄:<介面名,對應提供者的URI>
-
使用一個本地檔案模擬
提供者註冊時寫入檔案,消費者使用時讀取檔案即可
-
使用zookeeper儲存
-
使用curator操作zookeeper:
client = CuratorFrameworkFactory.newClient("localhost:2181", new RetryNTimes(3, 1000)); client.start();
-
註冊方法:
String result = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(String.format("/dubbo/service/%s/%s", interfaceName, JSONObject.toJSONString(url)), null); System.out.println(result);
-
獲取方法:
public static List<URL> get(String interfaceName) { List<URL> urlList = new ArrayList<>(); try { List<String> result = client.getChildren().forPath(String.format("/dubbo/service/%s", interfaceName)); for (String urlstr : result) { urlList.add(JSONObject.parseObject(urlstr, URL.class)); } REGISTER.put(interfaceName, urlList); } catch (Exception e) { e.printStackTrace(); } return urlList; }
-
-
執行流程:
提供者:
-
暴露服務
URL url = new URL("localhost",8080); // 這個url是自己封裝的一個物件,只包括ip和埠 /* 將<對應介面,url>註冊到遠端(redis/zookeeper)*/ RemoteMapRegister.regist(IHelloService.class.getName(),url); /* 伺服器本地註冊:<介面,對應實現類> */ LocalRegister.regist(IHelloService.class.getName(), HelloServiceImpl.class);
-
開啟伺服器,接受請求
// 開啟伺服器,使用工廠獲得伺服器 Protocol protocol = ProtocolFactory.getProtocol(); protocol.start(url); // 開啟伺服器,並且將伺服器阻塞接受請求
-
處理接受到的請求( 展示使用 tomcat & Http協議 )
// java 13:獲取req中內容,將InputStream轉換為Invocation物件 // Invocation invocation = JSONObject.parseObject(req.getInputStream(),Invocation.class); // java8:使用URL傳送請求,獲得InputStream,轉換為ObjectInputStream,讀取物件 InputStream inStream = req.getInputStream(); ObjectInputStream objectInputStream = new ObjectInputStream(inStream); Invocation invocation = (Invocation) objectInputStream.readObject(); String interfaceName = invocation.getInterfaceName(); /*通過介面獲得對應實現類*/ Class implClass = LocalRegister.get(interfaceName); /*通過方法名和引數型別獲得唯一的方法*/ Method method = implClass.getMethod(invocation.getMethodName(),invocation.getParamType()); /*反射機制method.invoke進行方法呼叫*/ String result = (String) method.invoke(implClass.newInstance(),invocation.getParams()); // 列印結果,並且將結果寫入到輸出流 System.out.println("tomcat:" + result); IOUtils.write(result,resp.getOutputStream());
消費者:
-
獲取代理物件,使用代理物件直接呼叫方法
public static void main(String[] args) { IHelloService helloService = ProxyFactory.getProxy(IHelloService.class); // 獲取要呼叫介面的代理物件 String alan = helloService.sayHello("alan"); // 直接呼叫方法,底層進行方法呼叫 System.out.println(alan); }
-
代理物件開啟伺服器,並且傳送請求( 展示使用 tomcat & Http 協議 ):
// 使用JDK13的Java.net傳送請求 // var request = HttpRequest.newBuilder() // .uri(new URI("http", null, hostname, port, "/", null, null)) // .POST(HttpRequest.BodyPublishers.ofString(JSONObject.toJSONString(invocation))) // .build(); // var client = java.net.http.HttpClient.newHttpClient(); // // HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); // // String result = response.body(); // 使用 JDK1.8的url進行傳送網路請求 /*建立一個URL物件*/ URL url = new URL("http", hostname, port, "/"); /*建立連線*/ HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); /*設定連線引數*/ httpURLConnection.setRequestMethod("POST"); httpURLConnection.setDoOutput(true); /*輸出流*/ OutputStream outputStream = httpURLConnection.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(outputStream); oos.writeObject(invocation); oos.flush(); oos.close(); /*傳送請求,獲得結果*/ InputStream inputStream = httpURLConnection.getInputStream(); String result = IOUtils.toString(inputStream); return result;
執行驗證:
首先啟動服務提供者,然後啟動服務消費者:
-
提供者列印:
-
消費者列印:
這裡測試的只是一個消費者與一個提供者。
當使用到提供者叢集時,也是可以的,本框架消費端提供了負載均衡功能(最簡單的 隨機負載均衡 )
一個建議的dubbo框架就這樣完成了,雖然簡單,但是五臟俱全!
相關文章
- 手寫RPC框架RPC框架
- Dubbo原始碼淺析(一)—RPC框架與Dubbo原始碼RPC框架
- 基於netty手寫RPC框架NettyRPC框架
- 手寫RPC框架(六)整合NettyRPC框架Netty
- SpringCloud Alibaba (四):Dubbo RPC框架SpringGCCloudRPC框架
- 如何從0到1設計一個類Dubbo的RPC框架RPC框架
- 使用Netty三分鐘手寫一個RPCNettyRPC
- 寫一個RPC服務RPC
- 看了這篇你就會手寫RPC框架了RPC框架
- 自己動手寫一個持久層框架框架
- 自己手寫一個SpringMVC框架(簡化)SpringMVC框架
- 帶你手寫基於 Spring 的可插拔式 RPC 框架(一)介紹SpringRPC框架
- 設計一個分散式RPC框架分散式RPC框架
- 在spring boot中3分鐘上手RPC框架DubboSpring BootRPC框架
- 徒手擼一個簡單的RPC框架RPC框架
- 仿照 geeorm 編寫的 sormORM
- 手寫簡單的RPCRPC
- 7天用Go動手寫/從零實現RPC框架GeeRPCGoRPC框架
- 動手實現一個簡單的 rpc 框架到入門 grpc (下)RPC框架
- 動手實現一個簡單的 rpc 框架到入門 grpc(上)RPC框架
- 動手實現一個簡單的 rpc 框架到入門 grpc (上)RPC框架
- 從零開始實現一個RPC框架(一)RPC框架
- 顫抖吧!一起手寫一個redux框架!Redux框架
- 【like-react】手寫一個類似 react 的框架React框架
- 7 款仿照 Sinatra 思路的 .NET 框架框架
- 從零開始實現一個RPC框架(四)RPC框架
- 從零開始實現一個RPC框架(零)RPC框架
- 從零開始實現一個RPC框架(二)RPC框架
- 從零開始實現一個RPC框架(五)RPC框架
- 從零開始實現一個RPC框架(三)RPC框架
- 帶你手把手實操一個RPC框架RPC框架
- 從零實現一個RPC框架系列文章(二):11個類實現簡單RPCRPC框架
- 寫了一個 WebSocket 框架Web框架
- 手寫一個HTTP框架:兩個類實現基本的IoC功能HTTP框架
- 從零開始手寫一個微前端框架-渲染篇前端框架
- 手寫一個PromisePromise
- 從零開始實現一個IDL+RPC框架RPC框架
- 從零開始實現一個分散式RPC框架分散式RPC框架