转载自http://shiyanjun.cn/archives/349.html
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; |
3 | public interface SolrSearchService { |
4 | String search(String collection, String q, String type, int start, int rows); |
上面接口提供了搜索远程调用功能。
基于Dubbo的Hessian协议实现提供方服务
提供方实现基于Dubbo封装的Hessian协议,实现接口SolrSearchService,实现代码如下所示:
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 { |
因为考虑到后面要使用标准Hessian接口来调用,这里接口方法参数全部使用内置标准类型。然后,我们使用Dubbo的配置文件进行配置,文件search-provider.xml的内容如下所示:
01 | <?xml version="1.0" encoding="UTF-8"?> |
08 | <dubbo:application name="search-provider" /> |
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"> |
17 | interface="org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" |
18 | ref="searchService" path="http_dubbo/search" /> |
因为使用Tomcat发布提供方服务,所以我们需要实现Spring的org.springframework.web.context.ContextLoader来初始化应用上下文(基于Spring的IoC容器来管理服务对象)。实现类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) { |
19 | public void contextInitialized(ServletContextEvent arg0) { |
20 | String config = arg0.getServletContext().getInitParameter("contextConfigLocation"); |
21 | ClassPathXmlApplicationContext context = newClassPathXmlApplicationContext(config); |
最后,配置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> |
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> |
启动Tomcat以后,就可以将提供方服务发布到服务注册中心,这里服务注册中心我们使用的是ZooKeeper集群,可以参考上面Dubbo配置文件search-provider.xml的配置内容。
下面,我们通过两种方式来调用已经注册到服务注册中心的服务。
服务消费方,通过Dubbo配置文件来指定注册到注册中心的服务,配置文件search-consumer.xml的内容,如下所示:
01 | <?xml version="1.0" encoding="UTF-8"?> |
08 | <dubbo:application name="search-consumer" /> |
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" /> |
然后,使用Java实现远程调用,实现代码如下所示:
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 introws) { |
43 | return search(q, type, start, rows); |
46 | private String search(final String q, final String type, final int start, final introws) { |
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, newCallable<AbstractXmlApplicationContext>() { |
55 | public AbstractXmlApplicationContext call() throws Exception { |
56 | final AbstractXmlApplicationContext context = newClassPathXmlApplicationContext(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); |
执行该调用实现,可以远程调用提供方发布的服务。
这种方式限制了服务调用方也必须使用Dubbo来开发调用的代码,也就是限制了编程的语言,而无论是对于内部还是外部,各个团队之间必然存在语言的多样性,如果限制了编程语言,那么开发的服务也只能在内部使用。
下面,使用标准Hessian接口来实现远程调用,这时就不需要关心服务提供方的所使用的开发语言,因为最终是通过HTTP的方式来访问。我们需要下载Hessian对应语言的调用实现库,才能更方便地编程。
使用Java语言实现远程调用
使用Java语言实现,代码如下所示:
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 { |
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); |
我们只需要知道提供服务暴露的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实现库:
3 | sudo python setup.py install |
然后就可以使用了,使用Python进行远程调用的实现代码如下所示:
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) |
运行上面程序,就可以看到远程调用的结果。
参考链接