遠程調用——webservice協議
目標:介紹webservice協議的設計和實現,介紹dubbo-rpc-webservice的源碼。
前言
dubbo集成webservice協議,基於 Apache CXF 的 frontend-simple
和 transports-http
實現 ,CXF 是 Apache 開源的一個 RPC 框架,由 Xfire 和 Celtix 合併而來。關於webservice協議的優勢以及介紹可以查看官方文檔,我就不多贅述。
源碼分析
(一)WebServiceProtocol
該類繼承了AbstractProxyProtocol,是webservice協議的關鍵邏輯實現。
1.屬性
/**
* 默認端口
*/
public static final int DEFAULT_PORT = 80;
/**
* 服務集合
*/
private final Map<String, HttpServer> serverMap = new ConcurrentHashMap<String, HttpServer>();
/**
* 總線,該總線使用CXF內置的擴展管理器來加載組件(而不是使用Spring總線實現)。雖然加載速度更快,但它不允許像Spring總線那樣進行大量配置和定製。
*/
private final ExtensionManagerBus bus = new ExtensionManagerBus();
/**
* http通信工廠對象
*/
private final HTTPTransportFactory transportFactory = new HTTPTransportFactory();
/**
* http綁定者
*/
private HttpBinder httpBinder;
2.doExport
@Override
protected <T> Runnable doExport(T impl, Class<T> type, URL url) throws RpcException {
// 獲得地址
String addr = getAddr(url);
// 獲得http服務
HttpServer httpServer = serverMap.get(addr);
// 如果服務爲空,則重新創建服務器。並且加入集合
if (httpServer == null) {
httpServer = httpBinder.bind(url, new WebServiceHandler());
serverMap.put(addr, httpServer);
}
// 服務加載器
final ServerFactoryBean serverFactoryBean = new ServerFactoryBean();
// 設置地址
serverFactoryBean.setAddress(url.getAbsolutePath());
// 設置服務類型
serverFactoryBean.setServiceClass(type);
// 設置實現類
serverFactoryBean.setServiceBean(impl);
// 設置總線
serverFactoryBean.setBus(bus);
// 設置通信工廠
serverFactoryBean.setDestinationFactory(transportFactory);
// 創建
serverFactoryBean.create();
return new Runnable() {
@Override
public void run() {
if(serverFactoryBean.getServer()!= null) {
serverFactoryBean.getServer().destroy();
}
if(serverFactoryBean.getBus()!=null) {
serverFactoryBean.getBus().shutdown(true);
}
}
};
}
該方法是服務暴露的邏輯實現,基於cxf一些類。
3.doRefer
@Override
@SuppressWarnings("unchecked")
protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
// 創建代理工廠
ClientProxyFactoryBean proxyFactoryBean = new ClientProxyFactoryBean();
// 設置地址
proxyFactoryBean.setAddress(url.setProtocol("http").toIdentityString());
// 設置服務類型
proxyFactoryBean.setServiceClass(serviceType);
// 設置總線
proxyFactoryBean.setBus(bus);
// 創建
T ref = (T) proxyFactoryBean.create();
// 獲得代理
Client proxy = ClientProxy.getClient(ref);
// 獲得HTTPConduit 處理“http”和“https”傳輸協議。實例由顯式設置或配置的策略控制
HTTPConduit conduit = (HTTPConduit) proxy.getConduit();
// 用於配置客戶端HTTP端口的屬性
HTTPClientPolicy policy = new HTTPClientPolicy();
// 配置連接超時時間
policy.setConnectionTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
// 配置調用超時時間
policy.setReceiveTimeout(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
conduit.setClient(policy);
return ref;
}
該方法是服務引用的邏輯實現。
4.WebServiceHandler
private class WebServiceHandler implements HttpHandler {
private volatile ServletController servletController;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// 如果servletController爲空,則重新加載一個
if (servletController == null) {
HttpServlet httpServlet = DispatcherServlet.getInstance();
if (httpServlet == null) {
response.sendError(500, "No such DispatcherServlet instance.");
return;
}
// 創建servletController
synchronized (this) {
if (servletController == null) {
servletController = new ServletController(transportFactory.getRegistry(), httpServlet.getServletConfig(), httpServlet);
}
}
}
// 設置遠程地址
RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
// 調用方法
servletController.invoke(request, response);
}
}
該內部類實現了HttpHandler接口,是WebService協議的請求的處理類。
後記
該部分相關的源碼解析地址:https://github.com/CrazyHZM/i...
該文章講解了遠程調用中關於webservice協議實現的部分,到這裏關於rpc遠程調用的部分就結束了,關於遠程調用核心的幾個內容就是代理、協議,再加上不同功能增強的過濾器等,關鍵是要把api中關於接口設計方面的內容看清楚,後面各類協議因爲很多都是基於第三方的框架去實現,雖然方法邏輯有所區別,但是整體的思路和框架一定順着api設計的去實現。接下來我將開始對cluster集羣模塊進行講解。