基于Dubbo的Hessian协议实现远程调用
Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务,限制了服务调用方。同时,使用Dubbo的Hessian协议实现提供方服务,而调用方可以使用标准的Hessian接口来调用,原生的Hessian协议已经支持多语言客户端调用,支持语言如下所示:
- Java:http://hessian.caucho.com/#Java
- Flash/Flex:http://hessian.caucho.com/#FlashFlex
- Python:http://hessian.caucho.com/#Python
- C++:http://hessian.caucho.com/#C
- C#:http://hessian.caucho.com/#NETC
- D:http://hessian.caucho.com/#D
- Erlang:http://hessian.caucho.com/#Erlang
- PHP:http://hessian.caucho.com/#PHP
- Ruby:http://hessian.caucho.com/#Ruby
- Objective-C:http://hessian.caucho.com/#ObjectiveC
下面,我们的思路是,先基于Dubbo封装的Hessian协议,实现提供方服务和消费方调用服务,双方必须都使用Dubbo来开发;然后,基于Dubbo封装的Hessian协议实现提供方服务,然后服务消费方使用标准的Hessian接口来进行远程调用,分别使用Java和Python语言来实现。而且,我们实现的提供方服务通过Tomcat发布到服务注册中心。
首先,使用Java语言定义一个搜索服务的接口,代码如下所示:
1
2
3
4
5
|
package org.shirdrn.platform.dubbo.service.rpc.api; public interface SolrSearchService { String search(String collection, String q, String type, int start, int rows); } |
上面接口提供了搜索远程调用功能。
基于Dubbo的Hessian协议实现提供方服务
提供方实现基于Dubbo封装的Hessian协议,实现接口SolrSearchService,实现代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package org.shirdrn.platform.dubbo.service.rpc.server; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService; import org.shirdrn.platform.dubbo.service.rpc.utils.QueryPostClient; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SolrSearchServer implements SolrSearchService { private static final Log LOG = LogFactory.getLog(SolrSearchServer. class ); private String baseUrl; private final QueryPostClient postClient; private static final Map<String, FormatHandler> handlers = new HashMap<String, FormatHandler>( 0 ); static { handlers.put( "xml" , new FormatHandler() { public String format() { return "&wt=xml" ; } }); handlers.put( "json" , new FormatHandler() { public String format() { return "&wt=json" ; } }); } public SolrSearchServer() { super (); postClient = QueryPostClient.newIndexingClient( null ); } public void setBaseUrl(String baseUrl) { this .baseUrl = baseUrl; } public String search(String collection, String q, String type, int start, int rows) { StringBuffer url = new StringBuffer(); url.append(baseUrl).append(collection).append( "/select?" ).append(q); url.append( "&start=" ).append(start).append( "&rows=" ).append(rows); url.append(handlers.get(type.toLowerCase()).format()); LOG.info( "[REQ] " + url.toString()); return postClient.request(url.toString()); } interface FormatHandler { String format(); } } |
因为考虑到后面要使用标准Hessian接口来调用,这里接口方法参数全部使用内置标准类型。然后,我们使用Dubbo的配置文件进行配置,文件search-provider.xml的内容如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
<? xml version = "1.0" encoding = "UTF-8" ?> xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo = "http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd < dubbo:application name = "search-provider" /> < dubbo:registry < dubbo:protocol name = "hessian" port = "8080" server = "servlet" /> < bean id = "searchService" class = "org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer" > </ bean > < dubbo:service interface = "org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" ref = "searchService" path = "http_dubbo/search" /> </ beans > |
因为使用Tomcat发布提供方服务,所以我们需要实现Spring的org.springframework.web.context.ContextLoader来初始化应用上下文(基于Spring的IoC容器来管理服务对象)。实现类SearchContextLoader代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package org.shirdrn.platform.dubbo.context; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.web.context.ContextLoader; public class SearchContextLoader extends ContextLoader implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub } @Override public void contextInitialized(ServletContextEvent arg0) { String config = arg0.getServletContext().getInitParameter( "contextConfigLocation" ); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); context.start(); } } |
最后,配置Web应用部署描述符文件,web.xml内容如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<? xml version = "1.0" encoding = "UTF-8" ?> < web-app id = "WebApp_ID" version = "2.4" xsi:schemaLocation = "http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" > < display-name >http_dubbo</ display-name > < listener > < listener-class >org.shirdrn.platform.dubbo.context.SearchContextLoader</ listener-class > </ listener > < context-param > < param-name >contextConfigLocation</ param-name > < param-value >classpath:search-provider.xml</ param-value > </ context-param > < servlet > < servlet-name >search</ servlet-name > < servlet-class >com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</ servlet-class > < init-param > < param-name >home-class</ param-name > < param-value >org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer</ param-value > </ init-param > < init-param > < param-name >home-api</ param-name > < param-value >org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService</ param-value > </ init-param > < load-on-startup >1</ load-on-startup > </ servlet > < servlet-mapping > < servlet-name >search</ servlet-name > < url-pattern >/search</ url-pattern > </ servlet-mapping > < welcome-file-list > < welcome-file >index.html</ welcome-file > < welcome-file >index.htm</ welcome-file > < welcome-file >index.jsp</ welcome-file > < welcome-file >default.html</ welcome-file > < welcome-file >default.htm</ welcome-file > < welcome-file >default.jsp</ welcome-file > </ welcome-file-list > </ web-app > |
启动Tomcat以后,就可以将提供方服务发布到服务注册中心,这里服务注册中心我们使用的是ZooKeeper集群,可以参考上面Dubbo配置文件search-provider.xml的配置内容。
下面,我们通过两种方式来调用已经注册到服务注册中心的服务。
- 基于Dubbo的Hessian协议远程调用
服务消费方,通过Dubbo配置文件来指定注册到注册中心的服务,配置文件search-consumer.xml的内容,如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<? xml version = "1.0" encoding = "UTF-8" ?> xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo = "http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd < dubbo:application name = "search-consumer" /> < dubbo:registry < dubbo:reference id = "searchService" interface = "org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" /> </ beans > |
然后,使用Java实现远程调用,实现代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
package org.shirdrn.platform.dubbo.service.rpc.client; import java.util.concurrent.Callable; import java.util.concurrent.Future; import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService; import org.springframework.beans.BeansException; import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.alibaba.dubbo.rpc.RpcContext; public class SearchConsumer { private final String collection; private AbstractXmlApplicationContext context; private SolrSearchService searchService; public SearchConsumer(String collection, Callable<AbstractXmlApplicationContext> call) { super (); this .collection = collection; try { context = call.call(); context.start(); searchService = (SolrSearchService) context.getBean( "searchService" ); } catch (BeansException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public Future<String> asyncCall( final String q, final String type, final int start, final int rows) { Future<String> future = RpcContext.getContext().asyncCall( new Callable<String>() { public String call() throws Exception { return search(q, type, start, rows); } }); return future; } public String syncCall( final String q, final String type, final int start, final int rows) { return search(q, type, start, rows); } private String search( final String q, final String type, final int start, final int rows) { return searchService.search(collection, q, type, start, rows); } public static void main(String[] args) throws Exception { final String collection = "tinycollection" ; final String beanXML = "search-consumer.xml" ; final String config = SearchConsumer. class .getPackage().getName().replace( '.' , '/' ) + "/" + beanXML; SearchConsumer consumer = new SearchConsumer(collection, new Callable<AbstractXmlApplicationContext>() { public AbstractXmlApplicationContext call() throws Exception { final AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext(config); return context; } }); String q = "q=上海&fl=*&fq=building_type:1" ; int start = 0 ; int rows = 10 ; String type = "xml" ; for ( int k = 0 ; k < 10 ; k++) { for ( int i = 0 ; i < 10 ; i++) { start = 1 * 10 * i; if (i % 2 == 0 ) { type = "xml" ; } else { type = "json" ; } String result = consumer.syncCall(q, type, start, rows); System.out.println(result); // Future<String> future = consumer.asyncCall(q, type, start, // rows); // System.out.println(future.get()); } } } } |
执行该调用实现,可以远程调用提供方发布的服务。
这种方式限制了服务调用方也必须使用Dubbo来开发调用的代码,也就是限制了编程的语言,而无论是对于内部还是外部,各个团队之间必然存在语言的多样性,如果限制了编程语言,那么开发的服务也只能在内部使用。
- 基于标准Hessian协议接口的远程调用
下面,使用标准Hessian接口来实现远程调用,这时就不需要关心服务提供方的所使用的开发语言,因为最终是通过HTTP的方式来访问。我们需要下载Hessian对应语言的调用实现库,才能更方便地编程。
使用Java语言实现远程调用
使用Java语言实现,代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package org.shirdrn.rpc.hessian; import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService; import com.caucho.hessian.client.HessianProxyFactory; public class HessianConsumer { public static void main(String[] args) throws Throwable { HessianProxyFactory factory = new HessianProxyFactory(); SolrSearchService searchService = (SolrSearchService) factory.create(SolrSearchService. class , serviceUrl); String q = "q=上海&fl=*&fq=building_type:1" ; String collection = "tinycollection" ; int start = 0 ; int rows = 10 ; String type = "xml" ; String result = searchService.search(collection, q, type, start, rows); System.out.println(result); } } |
我们只需要知道提供服务暴露的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实现库:
1
2
3
|
git clone https: //github .com /bgilmore/mustaine .git cd mustaine sudo python setup.py install |
然后就可以使用了,使用Python进行远程调用的实现代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
#!/usr/bin/python # coding=utf-8 from mustaine.client import HessianProxy q = 'q=*:*&fl=*&fq=building_type:1' start = 0 rows = 10 resType = 'xml' collection = 'tinycollection' if __name__ = = '__main__' : proxy = HessianProxy(serviceUrl) result = proxy.search(collection, q, resType, start, rows) print result |
运行上面程序,就可以看到远程调用的结果。
参考链接
- https://github.com/alibaba/dubbo
- http://alibaba.github.io/dubbo-doc-static/Home-zh.htm
- http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm
- http://alibaba.github.io/dubbo-doc-static/Developer+Guide-zh.htm
- http://alibaba.github.io/dubbo-doc-static/Administrator+Guide-zh.htm
- http://alibaba.github.io/dubbo-doc-static/FAQ-zh.htm
- http://hessian.caucho.com/#HessianImplementationsDownload
- https://github.com/bgilmore/mustaine