基於Dubbo的Hessian協議實現遠程調用

Dubbo基於Hessian實現了自己Hessian協議,可以直接通過配置的Dubbo內置的其他協議,在服務消費方進行遠程調用,也就是說,服務調用方需要使用Java語言來基於Dubbo調用提供方服務,限制了服務調用方。同時,使用Dubbo的Hessian協議實現提供方服務,而調用方可以使用標準的Hessian接口來調用,原生的Hessian協議已經支持多語言客戶端調用,支持語言如下所示:

下面,我們的思路是,先基於Dubbo封裝的Hessian協議,實現提供方服務和消費方調用服務,雙方必須都使用Dubbo來開發;然後,基於Dubbo封裝的Hessian協議實現提供方服務,然後服務消費方使用標準的Hessian接口來進行遠程調用,分別使用Java和Python語言來實現。而且,我們實現的提供方服務通過Tomcat發佈到服務註冊中心。
首先,使用Java語言定義一個搜索服務的接口,代碼如下所示:

1 package org.shirdrn.platform.dubbo.service.rpc.api;
2
3 public interface SolrSearchService {
4     String search(String collection, String q, String type, int start, int rows);
5 }

上面接口提供了搜索遠程調用功能。

基於Dubbo的Hessian協議實現提供方服務

提供方實現基於Dubbo封裝的Hessian協議,實現接口SolrSearchService,實現代碼如下所示:

01 package org.shirdrn.platform.dubbo.service.rpc.server;
02
03 import java.io.IOException;
04 import java.util.HashMap;
05 import java.util.Map;
06
07 import org.apache.commons.logging.Log;
08 import org.apache.commons.logging.LogFactory;
09 import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;
10 import org.shirdrn.platform.dubbo.service.rpc.utils.QueryPostClient;
11 import org.springframework.context.support.ClassPathXmlApplicationContext;
12
13 public class SolrSearchServer implements SolrSearchService {
14
15     private static final Log LOG = LogFactory.getLog(SolrSearchServer.class);
16     private String baseUrl;
17     private final QueryPostClient postClient;
18     private static final Map<String, FormatHandler> handlers = new HashMap<String, FormatHandler>(0);
19     static {
20         handlers.put("xml"new FormatHandler() {
21             public String format() {
22                 return "&wt=xml";
23             }
24         });
25         handlers.put("json"new FormatHandler() {
26             public String format() {
27                 return "&wt=json";
28             }
29         });
30     }
31
32     public SolrSearchServer() {
33         super();
34         postClient = QueryPostClient.newIndexingClient(null);
35     }
36
37     public void setBaseUrl(String baseUrl) {
38         this.baseUrl = baseUrl;
39     }
40
41     public String search(String collection, String q, String type, int start, int rows) {
42         StringBuffer url = new StringBuffer();
43         url.append(baseUrl).append(collection).append("/select?").append(q);
44         url.append("&start=").append(start).append("&rows=").append(rows);
45         url.append(handlers.get(type.toLowerCase()).format());
46         LOG.info("[REQ] " + url.toString());
47         return postClient.request(url.toString());
48     }
49
50     interface FormatHandler {
51         String format();
52     }
53 }

因爲考慮到後面要使用標準Hessian接口來調用,這裏接口方法參數全部使用內置標準類型。然後,我們使用Dubbo的配置文件進行配置,文件search-provider.xml的內容如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02
07
08     <dubbo:application name="search-provider" />
09     <dubbo:registry
10         address="zookeeper://slave1:2188?backup=slave3:2188,slave4:2188" />
11     <dubbo:protocol name="hessian" port="8080" server="servlet" />
12     <bean id="searchService"
13         class="org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer">
14         <property name="baseUrl" value="http://nginx-lbserver/solr-cloud/" />
15     </bean>
16     <dubbo:service
17         interface="org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService"
18         ref="searchService" path="http_dubbo/search" />
19
20 </beans>

因爲使用Tomcat發佈提供方服務,所以我們需要實現Spring的org.springframework.web.context.ContextLoader來初始化應用上下文(基於Spring的IoC容器來管理服務對象)。實現類SearchContextLoader代碼如下所示:

01 package org.shirdrn.platform.dubbo.context;
02
03 import javax.servlet.ServletContextEvent;
04 import javax.servlet.ServletContextListener;
05
06 import org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer;
07 import org.springframework.context.support.ClassPathXmlApplicationContext;
08 import org.springframework.web.context.ContextLoader;
09
10 public class SearchContextLoader extends ContextLoader implements ServletContextListener {
11
12     @Override
13     public void contextDestroyed(ServletContextEvent arg0) {
14         // TODO Auto-generated method stub
15
16     }
17
18     @Override
19     public void contextInitialized(ServletContextEvent arg0) {
20         String config = arg0.getServletContext().getInitParameter("contextConfigLocation");
21         ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
22         context.start();
23     }
24
25 }

最後,配置Web應用部署描述符文件,web.xml內容如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02 <web-app id="WebApp_ID" version="2.4"
05     <display-name>http_dubbo</display-name>
06
07     <listener>
08         <listener-class>org.shirdrn.platform.dubbo.context.SearchContextLoader</listener-class>
09     </listener>
10     <context-param>
11         <param-name>contextConfigLocation</param-name>
12         <param-value>classpath:search-provider.xml</param-value>
13     </context-param>
14
15     <servlet>
16         <servlet-name>search</servlet-name>
17         <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
18         <init-param>
19             <param-name>home-class</param-name>
20             <param-value>org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer</param-value>
21         </init-param>
22         <init-param>
23             <param-name>home-api</param-name>
24             <param-value>org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService</param-value>
25         </init-param>
26         <load-on-startup>1</load-on-startup>
27     </servlet>
28     <servlet-mapping>
29         <servlet-name>search</servlet-name>
30         <url-pattern>/search</url-pattern>
31     </servlet-mapping>
32
33     <welcome-file-list>
34         <welcome-file>index.html</welcome-file>
35         <welcome-file>index.htm</welcome-file>
36         <welcome-file>index.jsp</welcome-file>
37         <welcome-file>default.html</welcome-file>
38         <welcome-file>default.htm</welcome-file>
39         <welcome-file>default.jsp</welcome-file>
40     </welcome-file-list>
41 </web-app>

啓動Tomcat以後,就可以將提供方服務發佈到服務註冊中心,這裏服務註冊中心我們使用的是ZooKeeper集羣,可以參考上面Dubbo配置文件search-provider.xml的配置內容。

下面,我們通過兩種方式來調用已經註冊到服務註冊中心的服務。

  • 基於Dubbo的Hessian協議遠程調用

服務消費方,通過Dubbo配置文件來指定註冊到註冊中心的服務,配置文件search-consumer.xml的內容,如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02
07
08     <dubbo:application name="search-consumer" />
09     <dubbo:registry
10         address="zookeeper://slave1:2188?backup=slave3:2188,slave4:2188" />
11     <dubbo:reference id="searchService"
12         interface="org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" />
13
14 </beans>

然後,使用Java實現遠程調用,實現代碼如下所示:

01 package org.shirdrn.platform.dubbo.service.rpc.client;
02
03 import java.util.concurrent.Callable;
04 import java.util.concurrent.Future;
05
06 import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;
07 import org.springframework.beans.BeansException;
08 import org.springframework.context.support.AbstractXmlApplicationContext;
09 import org.springframework.context.support.ClassPathXmlApplicationContext;
10
11 import com.alibaba.dubbo.rpc.RpcContext;
12
13 public class SearchConsumer {
14
15     private final String collection;
16     private AbstractXmlApplicationContext context;
17     private SolrSearchService searchService;
18
19     public SearchConsumer(String collection, Callable<AbstractXmlApplicationContext> call) {
20         super();
21         this.collection = collection;
22         try {
23             context = call.call();
24             context.start();
25             searchService = (SolrSearchService) context.getBean("searchService");
26         catch (BeansException e) {
27             e.printStackTrace();
28         catch (Exception e) {
29             e.printStackTrace();
30         }
31     }
32
33     public Future<String> asyncCall(final String q, final String type, final int start, finalint rows) {
34         Future<String> future = RpcContext.getContext().asyncCall(new Callable<String>() {
35             public String call() throws Exception {
36                 return search(q, type, start, rows);
37             }
38         });
39         return future;
40     }
41
42     public String syncCall(final String q, final String type, final int start, final introws) {
43         return search(q, type, start, rows);
44     }
45
46     private String search(final String q, final String type, final int start, final int rows) {
47         return searchService.search(collection, q, type, start, rows);
48     }
49
50     public static void main(String[] args) throws Exception {
51         final String collection = "tinycollection";
52         final String beanXML = "search-consumer.xml";
53         final String config = SearchConsumer.class.getPackage().getName().replace('.''/') +"/" + beanXML;
54         SearchConsumer consumer = new SearchConsumer(collection, newCallable<AbstractXmlApplicationContext>() {
55             public AbstractXmlApplicationContext call() throws Exception {
56                 final AbstractXmlApplicationContext context = newClassPathXmlApplicationContext(config);
57                 return context;
58             }
59         });
60
61         String q = "q=上海&fl=*&fq=building_type:1";
62         int start = 0;
63         int rows = 10;
64         String type = "xml";
65         for (int k = 0; k < 10; k++) {
66             for (int i = 0; i < 10; i++) {
67                 start = 1 10 * i;
68                 if (i % 2 == 0) {
69                     type = "xml";
70                 else {
71                     type = "json";
72                 }
73                 String result = consumer.syncCall(q, type, start, rows);
74                 System.out.println(result);
75                 // Future<String> future = consumer.asyncCall(q, type, start,
76                 // rows);
77                 // System.out.println(future.get());
78             }
79         }
80     }
81
82 }

執行該調用實現,可以遠程調用提供方發佈的服務。
這種方式限制了服務調用方也必須使用Dubbo來開發調用的代碼,也就是限制了編程的語言,而無論是對於內部還是外部,各個團隊之間必然存在語言的多樣性,如果限制了編程語言,那麼開發的服務也只能在內部使用。

  • 基於標準Hessian協議接口的遠程調用

下面,使用標準Hessian接口來實現遠程調用,這時就不需要關心服務提供方的所使用的開發語言,因爲最終是通過HTTP的方式來訪問。我們需要下載Hessian對應語言的調用實現庫,才能更方便地編程。

使用Java語言實現遠程調用
使用Java語言實現,代碼如下所示:

01 package org.shirdrn.rpc.hessian;
02
03 import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;
04
05 import com.caucho.hessian.client.HessianProxyFactory;
06
07 public class HessianConsumer {
08
09     public static void main(String[] args) throws Throwable {
10
11         String serviceUrl = "http://10.95.3.74:8080/http_dubbo/search";
12         HessianProxyFactory factory = new HessianProxyFactory();
13
14         SolrSearchService searchService = (SolrSearchService) factory.create(SolrSearchService.class, serviceUrl);
15
16         String q = "q=上海&fl=*&fq=building_type:1";
17         String collection = "tinycollection";
18         int start = 0;
19         int rows = 10;
20         String type = "xml";
21         String result = searchService.search(collection, q, type, start, rows);
22         System.out.println(result);
23     }
24 }

我們只需要知道提供服務暴露的URL和服務接口即可,這裏URL爲http://10.95.3.74:8080/http_dubbo/search,接口爲org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService。運行上面程序,可以調用提供方發佈的服務。

使用Python語言實現遠程調用
使用Python客戶端來進行遠程調用,我們可以從https://github.com/bgilmore/mustaine下載,然後安裝Hessian的代理客戶端Python實現庫:

2 cd mustaine
3 sudo python setup.py install

然後就可以使用了,使用Python進行遠程調用的實現代碼如下所示:

01 #!/usr/bin/python
02
03 # coding=utf-8
04 from mustaine.client import HessianProxy
05
07 = 'q=*:*&fl=*&fq=building_type:1'
08 start = 0
09 rows = 10
10 resType = 'xml'
11 collection = 'tinycollection'
12
13 if __name__ == '__main__':
14      proxy = HessianProxy(serviceUrl)
15      result = proxy.search(collection, q, resType, start, rows)
16      print result

運行上面程序,就可以看到遠程調用的結果。

參考鏈接

Creative Commons License

本文基於署名-非商業性使用-相同方式共享 4.0許可協議發佈,歡迎轉載、使用、重新發布,但務必保留文章署名時延軍(包含鏈接:http://shiyanjun.cn),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章