天天看點

Dubbo源碼學習--Http協定(三)

基于 HTTP 表單的遠端調用協定,采用 Spring 的 HttpInvoker 實作 ​​1​​

特性

  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:HTTP
  • 傳輸方式:同步傳輸
  • 序列化:表單序列化
  • 适用範圍:傳入傳出參數資料包大小混合,提供者比消費者個數多,可用浏覽器檢視,可用表單或URL傳入參數,暫不支援傳檔案。
  • 适用場景:需同時給應用程式和浏覽器 JS 使用的服務。

限制

  • 參數及傳回值需符合 Bean 規範

配置

配置協定:

<dubbo:protocol name="http" port="8080"      

配置 Jetty Server (預設):

<dubbo:protocol ... server="jetty"      

配置 Servlet Bridge Server (推薦使用):

<dubbo:protocol ... server="servlet"      

配置 DispatcherServlet:

<servlet>
         <servlet-name>dubbo</servlet-name>
         <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
         <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
         <servlet-name>dubbo</servlet-name>
         <url-pattern>/*</url-pattern>
</servlet-mapping>      

注意,如果使用 servlet 派發請求:

  • 協定的端口

​<dubbo:protocol port="8080" />​

  • 協定的上下文路徑

​<dubbo:protocol contextpath="foo" />​

接下來我們看看在HttpProtocol中dubbo具體做了什麼邏輯處理。

HttpProtocol同樣也是提供了兩個接口doExport和doRefer

protected <T> Runnable doExport(final T impl, Class<T> type, URL url) throws RpcException {
        //擷取暴露服務的位址
        String addr = getAddr(url);
        HttpServer server = serverMap.get(addr);
        if (server == null) {
            //将接口資訊綁定到容器中
            server = httpBinder.bind(url, new InternalHandler());
            serverMap.put(addr, server);
        }
        final HttpInvokerServiceExporter httpServiceExporter = new HttpInvokerServiceExporter();
        httpServiceExporter.setServiceInterface(type);
        httpServiceExporter.setService(impl);
        try {
            httpServiceExporter.afterPropertiesSet();
        } catch (Exception e) {
            throw new RpcException(e.getMessage(), e);
        }
        final String path = url.getAbsolutePath();
        skeletonMap.put(path, httpServiceExporter);
        //建立線程,作為銷毀服務時使用
        return new Runnable() {
            public void run() {
                skeletonMap.remove(path);
            }
        };
    }      
@SuppressWarnings("unchecked")
    protected <T> T doRefer(final Class<T> serviceType, final URL url) throws RpcException {
        final HttpInvokerProxyFactoryBean httpProxyFactoryBean = new HttpInvokerProxyFactoryBean();
        //建立服務的url資訊
        httpProxyFactoryBean.setServiceUrl(url.toIdentityString());
        //建立服務的接口資訊
        httpProxyFactoryBean.setServiceInterface(serviceType);
        String client = url.getParameter(Constants.CLIENT_KEY);
        if (client == null || client.length() == 0 || "simple".equals(client)) {
            //建立連接配接資訊
            SimpleHttpInvokerRequestExecutor httpInvokerRequestExecutor = new SimpleHttpInvokerRequestExecutor() {
                protected void prepareConnection(HttpURLConnection con,
                                                 int contentLength) throws IOException {
                    super.prepareConnection(con, contentLength);
                    con.setReadTimeout(url.getParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT));
                    con.setConnectTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
                }
            };
            httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
        } else if ("commons".equals(client)) {
            HttpComponentsHttpInvokerRequestExecutor httpInvokerRequestExecutor = new HttpComponentsHttpInvokerRequestExecutor();
            httpInvokerRequestExecutor.setReadTimeout(url.getParameter(Constants.CONNECT_TIMEOUT_KEY, Constants.DEFAULT_CONNECT_TIMEOUT));
            httpProxyFactoryBean.setHttpInvokerRequestExecutor(httpInvokerRequestExecutor);
        } else {
            throw new IllegalStateException("Unsupported http protocol client " + client + ", only supported: simple, commons");
        }
        httpProxyFactoryBean.afterPropertiesSet();
        //建立http連接配接的代理類
        return (T) httpProxyFactoryBean.getObject();
    }