摘要:客戶端對象字節輸出流請求對象響應對象增加協議頭發送請求獲得請求后的狀態碼三該類實現了接口,是創建的工廠類。該類的實現跟類類似,但是是標準的接口調用會采用的工廠類,而是的協議調用。
遠程調用——hessian協議
目標:介紹遠程調用中跟hessian協議相關的設計和實現,介紹dubbo-rpc-hessian的源碼。前言
本文講解多是dubbo集成的第二種協議,hessian協議,Hessian 是 Caucho 開源的一個 RPC 框架,其通訊效率高于 WebService 和 Java 自帶的序列化。dubbo集成hessian所提供的hessian協議相關介紹可以參考官方文檔,我就不再贅述。
文檔地址:http://dubbo.apache.org/zh-cn...源碼分析 (一)DubboHessianURLConnectionFactory
該類繼承了HessianURLConnectionFactory類,是dubbo,用于創建與服務器的連接的內部工廠,重寫了父類中open方法。
public class DubboHessianURLConnectionFactory extends HessianURLConnectionFactory { /** * 打開與HTTP服務器的新連接或循環連接 * @param url * @return * @throws IOException */ @Override public HessianConnection open(URL url) throws IOException { // 獲得一個連接 HessianConnection connection = super.open(url); // 獲得上下文 RpcContext context = RpcContext.getContext(); for (String key : context.getAttachments().keySet()) { // 在http協議頭里面加入dubbo中附加值,key為 header+key value為附加值的value connection.addHeader(Constants.DEFAULT_EXCHANGER + key, context.getAttachment(key)); } return connection; } }
在hessian上加入dubbo自己所需要的附加值,放到協議頭里面進行發送。
(二)HttpClientConnection該類是基于HttpClient封裝來實現HessianConnection接口,其中邏輯比較簡單。
public class HttpClientConnection implements HessianConnection { /** * http客戶端對象 */ private final HttpClient httpClient; /** * 字節輸出流 */ private final ByteArrayOutputStream output; /** * http post請求對象 */ private final HttpPost request; /** * http 響應對象 */ private volatile HttpResponse response; public HttpClientConnection(HttpClient httpClient, URL url) { this.httpClient = httpClient; this.output = new ByteArrayOutputStream(); this.request = new HttpPost(url.toString()); } /** * 增加協議頭 * @param key * @param value */ @Override public void addHeader(String key, String value) { request.addHeader(new BasicHeader(key, value)); } @Override public OutputStream getOutputStream() throws IOException { return output; } /** * 發送請求 * @throws IOException */ @Override public void sendRequest() throws IOException { request.setEntity(new ByteArrayEntity(output.toByteArray())); this.response = httpClient.execute(request); } /** * 獲得請求后的狀態碼 * @return */ @Override public int getStatusCode() { return response == null || response.getStatusLine() == null ? 0 : response.getStatusLine().getStatusCode(); } @Override public String getStatusMessage() { return response == null || response.getStatusLine() == null ? null : response.getStatusLine().getReasonPhrase(); } @Override public String getContentEncoding() { return (response == null || response.getEntity() == null || response.getEntity().getContentEncoding() == null) ? null : response.getEntity().getContentEncoding().getValue(); } @Override public InputStream getInputStream() throws IOException { return response == null || response.getEntity() == null ? null : response.getEntity().getContent(); } @Override public void close() throws IOException { HttpPost request = this.request; if (request != null) { request.abort(); } } @Override public void destroy() throws IOException { }(三)HttpClientConnectionFactory
該類實現了HessianConnectionFactory接口,是創建HttpClientConnection的工廠類。該類的實現跟DubboHessianURLConnectionFactory類類似,但是DubboHessianURLConnectionFactory是標準的Hessian接口調用會采用的工廠類,而HttpClientConnectionFactory是Dubbo 的 Hessian 協議調用。當然Dubbo 的 Hessian 協議也是基于http的。
public class HttpClientConnectionFactory implements HessianConnectionFactory { /** * httpClient對象 */ private final HttpClient httpClient = new DefaultHttpClient(); @Override public void setHessianProxyFactory(HessianProxyFactory factory) { // 設置連接超時時間 HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), (int) factory.getConnectTimeout()); // 設置讀取數據時阻塞鏈路的超時時間 HttpConnectionParams.setSoTimeout(httpClient.getParams(), (int) factory.getReadTimeout()); } @Override public HessianConnection open(URL url) throws IOException { // 創建一個HttpClientConnection實例 HttpClientConnection httpClientConnection = new HttpClientConnection(httpClient, url); // 獲得上下文,用來獲得附加值 RpcContext context = RpcContext.getContext(); // 遍歷附加值,放入到協議頭里面 for (String key : context.getAttachments().keySet()) { httpClientConnection.addHeader(Constants.DEFAULT_EXCHANGER + key, context.getAttachment(key)); } return httpClientConnection; } }
實現了兩個方法,第一個方法是給http連接設置兩個參數配置,第二個方法是創建一個連接。
(四)HessianProtocol該類繼承了AbstractProxyProtocol類,是hessian協議的實現類。其中實現類基于hessian協議的服務引用、服務暴露等方法。
1.屬性/** * http服務器集合 * key為ip:port */ private final Map2.doExportserverMap = new ConcurrentHashMap (); /** * HessianSkeleto 集合 * key為服務名 */ private final Map skeletonMap = new ConcurrentHashMap (); /** * HttpBinder對象,默認是jetty實現 */ private HttpBinder httpBinder;
@Override protectedRunnable doExport(T impl, Class type, URL url) throws RpcException { // 獲得ip地址 String addr = getAddr(url); // 獲得http服務器對象 HttpServer server = serverMap.get(addr); // 如果為空,則重新創建一個server,然后放入集合 if (server == null) { server = httpBinder.bind(url, new HessianHandler()); serverMap.put(addr, server); } // 獲得服務path final String path = url.getAbsolutePath(); // 創建Hessian服務端對象 final HessianSkeleton skeleton = new HessianSkeleton(impl, type); // 加入集合 skeletonMap.put(path, skeleton); // 獲得通用的path final String genericPath = path + "/" + Constants.GENERIC_KEY; // 加入集合 skeletonMap.put(genericPath, new HessianSkeleton(impl, GenericService.class)); // 返回一個線程 return new Runnable() { @Override public void run() { skeletonMap.remove(path); skeletonMap.remove(genericPath); } }; }
該方法是服務暴露的主要邏輯實現。
3.doRefer@Override @SuppressWarnings("unchecked") protectedT doRefer(Class serviceType, URL url) throws RpcException { // 獲得泛化的參數 String generic = url.getParameter(Constants.GENERIC_KEY); // 是否是泛化調用 boolean isGeneric = ProtocolUtils.isGeneric(generic) || serviceType.equals(GenericService.class); // 如果是泛化調用。則設置泛化的path和附加值 if (isGeneric) { RpcContext.getContext().setAttachment(Constants.GENERIC_KEY, generic); url = url.setPath(url.getPath() + "/" + Constants.GENERIC_KEY); } // 創建代理工廠 HessianProxyFactory hessianProxyFactory = new HessianProxyFactory(); // 是否是Hessian2的請求 默認為否 boolean isHessian2Request = url.getParameter(Constants.HESSIAN2_REQUEST_KEY, Constants.DEFAULT_HESSIAN2_REQUEST); // 設置是否應使用Hessian協議的版本2來解析請求 hessianProxyFactory.setHessian2Request(isHessian2Request); // 是否應為遠程調用啟用重載方法,默認為否 boolean isOverloadEnabled = url.getParameter(Constants.HESSIAN_OVERLOAD_METHOD_KEY, Constants.DEFAULT_HESSIAN_OVERLOAD_METHOD); // 設置是否應為遠程調用啟用重載方法。 hessianProxyFactory.setOverloadEnabled(isOverloadEnabled); // 獲得client實現方式,默認為jdk String client = url.getParameter(Constants.CLIENT_KEY, Constants.DEFAULT_HTTP_CLIENT); if ("httpclient".equals(client)) { // 用http來創建 hessianProxyFactory.setConnectionFactory(new HttpClientConnectionFactory()); } else if (client != null && client.length() > 0 && !Constants.DEFAULT_HTTP_CLIENT.equals(client)) { // 拋出不支持的協議異常 throw new IllegalStateException("Unsupported http protocol client="" + client + ""!"); } else { // 創建一個HessianConnectionFactory對象 HessianConnectionFactory factory = new DubboHessianURLConnectionFactory(); // 設置代理工廠 factory.setHessianProxyFactory(hessianProxyFactory); // 設置工廠 hessianProxyFactory.setConnectionFactory(factory); } // 獲得超時時間 int timeout = url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); // 設置超時時間 hessianProxyFactory.setConnectTimeout(timeout); hessianProxyFactory.setReadTimeout(timeout); // 創建代理 return (T) hessianProxyFactory.create(serviceType, url.setProtocol("http").toJavaURL(), Thread.currentThread().getContextClassLoader()); }
該方法是服務引用的主要邏輯實現,根據客戶端配置,來選擇標準 Hessian 接口調用還是Dubbo 的 Hessian 協議調用。
4.getErrorCode@Override protected int getErrorCode(Throwable e) { // 如果屬于HessianConnectionException異常 if (e instanceof HessianConnectionException) { if (e.getCause() != null) { Class> cls = e.getCause().getClass(); // 如果屬于超時異常,則返回超時異常 if (SocketTimeoutException.class.equals(cls)) { return RpcException.TIMEOUT_EXCEPTION; } } // 否則返回網絡異常 return RpcException.NETWORK_EXCEPTION; } else if (e instanceof HessianMethodSerializationException) { // 序列化異常 return RpcException.SERIALIZATION_EXCEPTION; } return super.getErrorCode(e); }
該方法是針對異常的處理。
5.HessianHandlerprivate class HessianHandler implements HttpHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 獲得請求的uri String uri = request.getRequestURI(); // 獲得對應的HessianSkeleton對象 HessianSkeleton skeleton = skeletonMap.get(uri); // 如果如果不是post方法 if (!request.getMethod().equalsIgnoreCase("POST")) { // 返回狀態設置為500 response.setStatus(500); } else { // 設置遠程地址 RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort()); // 獲得請求頭內容 Enumerationenumeration = request.getHeaderNames(); // 遍歷請求頭內容 while (enumeration.hasMoreElements()) { String key = enumeration.nextElement(); // 如果key開頭是deader,則把附加值取出來放入上下文 if (key.startsWith(Constants.DEFAULT_EXCHANGER)) { RpcContext.getContext().setAttachment(key.substring(Constants.DEFAULT_EXCHANGER.length()), request.getHeader(key)); } } try { // 執行下一個 skeleton.invoke(request.getInputStream(), response.getOutputStream()); } catch (Throwable e) { throw new ServletException(e); } } } }
該內部類是Hessian的處理器,用來處理請求中的協議頭內容。
后記該部分相關的源碼解析地址:https://github.com/CrazyHZM/i...
該文章講解了遠程調用中關于hessian協議的部分,內容比較簡單,可以參考著官方文檔了解一下。接下來我將開始對rpc模塊關于hessian協議部分進行講解。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73175.html
摘要:大揭秘異步化改造目標從源碼的角度分析的新特性中對于異步化的改造原理。看源碼解析四十六消費端發送請求過程講到的十四的,在以前的邏輯會直接在方法中根據配置區分同步異步單向調用。改為關于可以參考源碼解析十遠程通信層的六。 2.7大揭秘——異步化改造 目標:從源碼的角度分析2.7的新特性中對于異步化的改造原理。 前言 dubbo中提供了很多類型的協議,關于協議的系列可以查看下面的文章: du...
摘要:前言基于表單的遠程調用協議,采用的實現,關于協議就不用多說了吧。后記該部分相關的源碼解析地址該文章講解了遠程調用中關于協議的部分,內容比較簡單,可以參考著官方文檔了解一下。 遠程調用——http協議 目標:介紹遠程調用中跟http協議相關的設計和實現,介紹dubbo-rpc-http的源碼。 前言 基于HTTP表單的遠程調用協議,采用 Spring 的HttpInvoker實現,關于h...
摘要:可以參考源碼解析二十四遠程調用協議的八。十六的該類也是用了適配器模式,該類主要的作用就是增加了心跳功能,可以參考源碼解析十遠程通信層的四。二十的可以參考源碼解析十七遠程通信的一。 2.7大揭秘——消費端發送請求過程 目標:從源碼的角度分析一個服務方法調用經歷怎么樣的磨難以后到達服務端。 前言 前一篇文章講到的是引用服務的過程,引用服務無非就是創建出一個代理。供消費者調用服務的相關方法。...
摘要:服務引用過程目標從源碼的角度分析服務引用過程。并保留服務提供者的部分配置,比如版本,,時間戳等最后將合并后的配置設置為查詢字符串中。的可以參考源碼解析二十三遠程調用的一的源碼分析。 dubbo服務引用過程 目標:從源碼的角度分析服務引用過程。 前言 前面服務暴露過程的文章講解到,服務引用有兩種方式,一種就是直連,也就是直接指定服務的地址來進行引用,這種方式更多的時候被用來做服務測試,不...
摘要:而存在的意義就是保證請求或響應對象可在線程池中被解碼,解碼完成后,就會分發到的。 2.7大揭秘——服務端處理請求過程 目標:從源碼的角度分析服務端接收到請求后的一系列操作,最終把客戶端需要的值返回。 前言 上一篇講到了消費端發送請求的過程,該篇就要將服務端處理請求的過程。也就是當服務端收到請求數據包后的一系列處理以及如何返回最終結果。我們也知道消費端在發送請求的時候已經做了編碼,所以我...
閱讀 2928·2021-10-14 09:42
閱讀 3694·2021-08-11 11:19
閱讀 3542·2019-08-30 13:57
閱讀 3120·2019-08-30 13:49
閱讀 1534·2019-08-29 18:38
閱讀 898·2019-08-29 13:16
閱讀 1850·2019-08-26 13:25
閱讀 3230·2019-08-26 13:24