您的位置:首页 > 其它

[转载] 基于Dubbo的Hessian协议实现远程调用

2015-07-02 00:21 561 查看
转载自http://shiyanjun.cn/archives/349.html

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
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
03
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
04
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo
=
"http://code.alibabatech.com/schema/dubbo"
05
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
06
 
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
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"
03
xmlns
=
"http://java.sun.com/xml/ns/j2ee"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
04
xsi:schemaLocation
=
"http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
>
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
03
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
04
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo
=
"http://code.alibabatech.com/schema/dubbo"
05
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
06
 
http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
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,
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);
37
}
38
});
39
return
future;
40
}
41
42
public
String syncCall(
final
String q,
final
String type,
final
int
start,
final
int
rows) {
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,
new
Callable<AbstractXmlApplicationContext>() {
55
public
AbstractXmlApplicationContext call()
throws
Exception {
56
final
AbstractXmlApplicationContext context =
new
ClassPathXmlApplicationContext(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实现库:

1
git clone https://github.com/bgilmore/mustaine.git
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
06
serviceUrl
=
'http://10.95.3.74:8080/http_dubbo/search'
07
q
=
'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
运行上面程序,就可以看到远程调用的结果。

参考链接

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: