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 |
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/j2ee http://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 |
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實現庫:
然後就可以使用了,使用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
本文基於署名-非商業性使用-相同方式共享 4.0許可協議發佈,歡迎轉載、使用、重新發布,但務必保留文章署名時延軍(包含鏈接:http://shiyanjun.cn),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我聯繫。