Dubbo基于Hessian實(shí)現(xiàn)了自己Hessian協(xié)議,可以直接通過(guò)配置的Dubbo內(nèi)置的其他協(xié)議,在服務(wù)消費(fèi)方進(jìn)行遠(yuǎn)程調(diào)用,也就是說(shuō),服務(wù)調(diào)用方需要使用Java語(yǔ)言來(lái)基于Dubbo調(diào)用提供方服務(wù),限制了服務(wù)調(diào)用方。同時(shí),使用Dubbo的Hessian協(xié)議實(shí)現(xiàn)提供方服務(wù),而調(diào)用方可以使用標(biāo)準(zhǔn)的Hessian接口來(lái)調(diào)用,原生的Hessian協(xié)議已經(jīng)支持多語(yǔ)言客戶端調(diào)用,支持語(yǔ)言如下所示:
下面,我們的思路是,先基于Dubbo封裝的Hessian協(xié)議,實(shí)現(xiàn)提供方服務(wù)和消費(fèi)方調(diào)用服務(wù),雙方必須都使用Dubbo來(lái)開(kāi)發(fā);然后,基于Dubbo封裝的Hessian協(xié)議實(shí)現(xiàn)提供方服務(wù),然后服務(wù)消費(fèi)方使用標(biāo)準(zhǔn)的Hessian接口來(lái)進(jìn)行遠(yuǎn)程調(diào)用,分別使用Java和Python語(yǔ)言來(lái)實(shí)現(xiàn)。而且,我們實(shí)現(xiàn)的提供方服務(wù)通過(guò)Tomcat發(fā)布到服務(wù)注冊(cè)中心。
首先,使用Java語(yǔ)言定義一個(gè)搜索服務(wù)的接口,代碼如下所示:
1 | package org.shirdrn.platform.dubbo.service.rpc.api; |
3 | public interface SolrSearchService { |
4 | String search(String collection, String q, String type, int start, int rows); |
上面接口提供了搜索遠(yuǎn)程調(diào)用功能。
基于Dubbo的Hessian協(xié)議實(shí)現(xiàn)提供方服務(wù)
提供方實(shí)現(xiàn)基于Dubbo封裝的Hessian協(xié)議,實(shí)現(xiàn)接口SolrSearchService,實(shí)現(xiàn)代碼如下所示:
01 | package org.shirdrn.platform.dubbo.service.rpc.server; |
03 | import java.io.IOException; |
04 | import java.util.HashMap; |
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; |
13 | public class SolrSearchServer implements SolrSearchService { |
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 ); |
20 | handlers.put( "xml" , new FormatHandler() { |
21 | public String format() { |
25 | handlers.put( "json" , new FormatHandler() { |
26 | public String format() { |
32 | public SolrSearchServer() { |
34 | postClient = QueryPostClient.newIndexingClient( null ); |
37 | public void setBaseUrl(String baseUrl) { |
38 | this .baseUrl = baseUrl; |
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()); |
50 | interface FormatHandler { |
因?yàn)榭紤]到后面要使用標(biāo)準(zhǔn)Hessian接口來(lái)調(diào)用,這里接口方法參數(shù)全部使用內(nèi)置標(biāo)準(zhǔn)類(lèi)型。然后,我們使用Dubbo的配置文件進(jìn)行配置,文件search-provider.xml的內(nèi)容如下所示:
01 | <? xml version = "1.0" encoding = "UTF-8" ?> |
08 | < dubbo:application name = "search-provider" /> |
11 | < dubbo:protocol name = "hessian" port = "8080" server = "servlet" /> |
12 | < bean id = "searchService" |
13 | class = "org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer" > |
17 | interface = "org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" |
18 | ref = "searchService" path = "http_dubbo/search" /> |
因?yàn)槭褂肨omcat發(fā)布提供方服務(wù),所以我們需要實(shí)現(xiàn)Spring的org.springframework.web.context.ContextLoader來(lái)初始化應(yīng)用上下文(基于Spring的IoC容器來(lái)管理服務(wù)對(duì)象)。實(shí)現(xiàn)類(lèi)SearchContextLoader代碼如下所示:
01 | package org.shirdrn.platform.dubbo.context; |
03 | import javax.servlet.ServletContextEvent; |
04 | import javax.servlet.ServletContextListener; |
06 | import org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer; |
07 | import org.springframework.context.support.ClassPathXmlApplicationContext; |
08 | import org.springframework.web.context.ContextLoader; |
10 | public class SearchContextLoader extends ContextLoader implements ServletContextListener { |
13 | public void contextDestroyed(ServletContextEvent arg0) { |
14 | // TODO Auto-generated method stub |
19 | public void contextInitialized(ServletContextEvent arg0) { |
20 | String config = arg0.getServletContext().getInitParameter( "contextConfigLocation" ); |
21 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); |
最后,配置Web應(yīng)用部署描述符文件,web.xml內(nèi)容如下所示:
01 | <? xml version = "1.0" encoding = "UTF-8" ?> |
02 | < web-app id = "WebApp_ID" version = "2.4" |
05 | < display-name >http_dubbo</ display-name > |
08 | < listener-class >org.shirdrn.platform.dubbo.context.SearchContextLoader</ listener-class > |
11 | < param-name >contextConfigLocation</ param-name > |
12 | < param-value >classpath:search-provider.xml</ param-value > |
16 | < servlet-name >search</ servlet-name > |
17 | < servlet-class >com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</ servlet-class > |
19 | < param-name >home-class</ param-name > |
20 | < param-value >org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer</ param-value > |
23 | < param-name >home-api</ param-name > |
24 | < param-value >org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService</ param-value > |
26 | < load-on-startup >1</ load-on-startup > |
29 | < servlet-name >search</ servlet-name > |
30 | < url-pattern >/search</ url-pattern > |
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 > |
啟動(dòng)Tomcat以后,就可以將提供方服務(wù)發(fā)布到服務(wù)注冊(cè)中心,這里服務(wù)注冊(cè)中心我們使用的是ZooKeeper集群,可以參考上面Dubbo配置文件search-provider.xml的配置內(nèi)容。
下面,我們通過(guò)兩種方式來(lái)調(diào)用已經(jīng)注冊(cè)到服務(wù)注冊(cè)中心的服務(wù)。
- 基于Dubbo的Hessian協(xié)議遠(yuǎn)程調(diào)用
服務(wù)消費(fèi)方,通過(guò)Dubbo配置文件來(lái)指定注冊(cè)到注冊(cè)中心的服務(wù),配置文件search-consumer.xml的內(nèi)容,如下所示:
01 | <? xml version = "1.0" encoding = "UTF-8" ?> |
08 | < dubbo:application name = "search-consumer" /> |
11 | < dubbo:reference id = "searchService" |
12 | interface = "org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" /> |
然后,使用Java實(shí)現(xiàn)遠(yuǎn)程調(diào)用,實(shí)現(xiàn)代碼如下所示:
01 | package org.shirdrn.platform.dubbo.service.rpc.client; |
03 | import java.util.concurrent.Callable; |
04 | import java.util.concurrent.Future; |
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; |
11 | import com.alibaba.dubbo.rpc.RpcContext; |
13 | public class SearchConsumer { |
15 | private final String collection; |
16 | private AbstractXmlApplicationContext context; |
17 | private SolrSearchService searchService; |
19 | public SearchConsumer(String collection, Callable<AbstractXmlApplicationContext> call) { |
21 | this .collection = collection; |
23 | context = call.call(); |
25 | searchService = (SolrSearchService) context.getBean( "searchService" ); |
26 | } catch (BeansException e) { |
28 | } catch (Exception e) { |
33 | public Future<String> asyncCall( final String q, final String type, final int start, final int rows) { |
34 | Future<String> future = RpcContext.getContext().asyncCall( new Callable<String>() { |
35 | public String call() throws Exception { |
36 | return search(q, type, start, rows); |
42 | public String syncCall( final String q, final String type, final int start, final int rows) { |
43 | return search(q, type, start, rows); |
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); |
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, new Callable<AbstractXmlApplicationContext>() { |
55 | public AbstractXmlApplicationContext call() throws Exception { |
56 | final AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext(config); |
61 | String q = "q=上海&fl=*&fq=building_type:1" ; |
65 | for ( int k = 0 ; k < 10 ; k++) { |
66 | for ( int i = 0 ; i < 10 ; i++) { |
73 | String result = consumer.syncCall(q, type, start, rows); |
74 | System.out.println(result); |
75 | // Future<String> future = consumer.asyncCall(q, type, start, |
77 | // System.out.println(future.get()); |
執(zhí)行該調(diào)用實(shí)現(xiàn),可以遠(yuǎn)程調(diào)用提供方發(fā)布的服務(wù)。
這種方式限制了服務(wù)調(diào)用方也必須使用Dubbo來(lái)開(kāi)發(fā)調(diào)用的代碼,也就是限制了編程的語(yǔ)言,而無(wú)論是對(duì)于內(nèi)部還是外部,各個(gè)團(tuán)隊(duì)之間必然存在語(yǔ)言的多樣性,如果限制了編程語(yǔ)言,那么開(kāi)發(fā)的服務(wù)也只能在內(nèi)部使用。
- 基于標(biāo)準(zhǔn)Hessian協(xié)議接口的遠(yuǎn)程調(diào)用
下面,使用標(biāo)準(zhǔn)Hessian接口來(lái)實(shí)現(xiàn)遠(yuǎn)程調(diào)用,這時(shí)就不需要關(guān)心服務(wù)提供方的所使用的開(kāi)發(fā)語(yǔ)言,因?yàn)樽罱K是通過(guò)HTTP的方式來(lái)訪問(wèn)。我們需要下載Hessian對(duì)應(yīng)語(yǔ)言的調(diào)用實(shí)現(xiàn)庫(kù),才能更方便地編程。
使用Java語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程調(diào)用
使用Java語(yǔ)言實(shí)現(xiàn),代碼如下所示:
01 | package org.shirdrn.rpc.hessian; |
03 | import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService; |
05 | import com.caucho.hessian.client.HessianProxyFactory; |
07 | public class HessianConsumer { |
09 | public static void main(String[] args) throws Throwable { |
11 | String serviceUrl = "; |
12 | HessianProxyFactory factory = new HessianProxyFactory(); |
14 | SolrSearchService searchService = (SolrSearchService) factory.create(SolrSearchService. class , serviceUrl); |
16 | String q = "q=上海&fl=*&fq=building_type:1" ; |
17 | String collection = "tinycollection" ; |
21 | String result = searchService.search(collection, q, type, start, rows); |
22 | System.out.println(result); |
我們只需要知道提供服務(wù)暴露的URL和服務(wù)接口即可,這里URL為http://10.95.3.74:8080/http_dubbo/search,接口為org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService。運(yùn)行上面程序,可以調(diào)用提供方發(fā)布的服務(wù)。
使用Python語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程調(diào)用
使用Python客戶端來(lái)進(jìn)行遠(yuǎn)程調(diào)用,我們可以從https://github.com/bgilmore/mustaine下載,然后安裝Hessian的代理客戶端Python實(shí)現(xiàn)庫(kù):
3 | sudo python setup.py install |
然后就可以使用了,使用Python進(jìn)行遠(yuǎn)程調(diào)用的實(shí)現(xiàn)代碼如下所示:
04 | from mustaine.client import HessianProxy |
07 | q = 'q=*:*&fl=*&fq=building_type:1' |
11 | collection = 'tinycollection' |
13 | if __name__ = = '__main__' : |
14 | proxy = HessianProxy(serviceUrl) |
15 | result = proxy.search(collection, q, resType, start, rows) |
運(yùn)行上面程序,就可以看到遠(yuǎn)程調(diào)用的結(jié)果。
參考鏈接