转载自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" />
|
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) {
|
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应用部署描述符文件,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" />
|
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 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());
|
执行该调用实现,可以远程调用提供方发布的服务。
这种方式限制了服务调用方也必须使用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)
|
运行上面程序,就可以看到远程调用的结果。
参考链接