Dubbo实现RPC调用使用入门
来源:互联网 发布:假丝绸出口数据 编辑:程序博客网 时间:2024/06/07 05:03
使用Dubbo进行远程调用实现服务交互,它支持多种协议,如Hessian、HTTP、RMI、Memcached、Redis、Thrift等等。由于Dubbo将这些协议的实现进行了封装了,无论是服务端(开发服务)还是客户端(调用服务),都不需要关心协议的细节,只需要在配置中指定使用的协议即可,从而保证了服务提供方与服务消费方之间的透明。
另外,如果我们使用Dubbo的服务注册中心组件,这样服务提供方将服务发布到注册的中心,只是将服务的名称暴露给外部,而服务消费方只需要知道注册中心和服务提供方提供的服务名称,就能够透明地调用服务,后面我们会看到具体提供服务和消费服务的配置内容,使得双方之间交互的透明化。
示例场景
我们给出一个示例的应用场景:
服务方提供一个搜索服务,对服务方来说,它基于SolrCloud构建了搜索服务,包含两个集群,ZooKeeper集群和Solr集群,然后在前端通过Nginx来进行反向代理,达到负载均衡的目的。
服务消费方就是调用服务进行查询,给出查询条件(满足Solr的REST-like接口)。
应用设计
基于上面的示例场景,我们打算使用ZooKeeper集群作为服务注册中心。注册中心会暴露给服务提供方和服务消费方,所以注册服务的时候,服务先提供方只需要提供Nginx的地址给注册中心,但是注册中心并不会把这个地址暴露给服务消费方,如图所示:
我们先定义一下,通信双方需要使用的接口,如下所示:
01
package
org.shirdrn.platform.dubbo.service.rpc.api;
02
03
public
interface
SolrSearchService {
04
05
String search(String collection, String q, ResponseType type,
int
start,
int
rows);
06
07
public
enum
ResponseType {
08
JSON,
09
XML
10
}
11
}
基于上图中的设计,下面我们分别详细说明Provider和Consumer的设计及实现。
- Provider服务设计
Provider所发布的服务组件,包含了一个SolrCloud集群,在SolrCloud集群前端又加了一个反向代理层,使用Nginx来均衡负载。Provider的搜索服务系统,设计如下图所示:
上图中,实际Nginx中将请求直接转发内部的Web Servers上,在这个过程中,使用ZooKeeper来进行协调:从多个分片(Shard)服务器上并行搜索,最后合并结果。我们看一下Nginx配置的内容片段:
01
user nginx;
02
worker_processes 4;
03
04
error_log /var/log/nginx/error.log warn;
05
pid /var/run/nginx.pid;
06
07
08
events {
09
worker_connections 1024;
10
}
11
12
13
http {
14
include /etc/nginx/mime.types;
15
default_type application/octet-stream;
16
17
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
18
'$status $body_bytes_sent "$http_referer" '
19
'"$http_user_agent" "$http_x_forwarded_for"';
20
21
access_log /var/log/nginx/access.log main;
22
23
sendfile on;
24
#tcp_nopush on;
25
26
keepalive_timeout 65;
27
28
#gzip on;
29
30
upstream master {
31
server slave1:8888 weight=1;
32
server slave4:8888 weight=1;
33
server slave6:8888 weight=1;
34
}
35
36
server {
37
listen 80;
38
server_name master;
39
location / {
40
root /usr/share/nginx/html/solr-cloud;
41
index index.html index.htm;
42
proxy_pass http://master;
43
include /home/hadoop/servers/nginx/conf/proxy.conf;
44
}
45
}
46
}
一共配置了3台Solr服务器,因为SolrCloud集群中每一个节点都可以接收搜索请求,然后由整个集群去并行搜索。最后,我们要通过Dubbo服务框架来基于已有的系统来开发搜索服务,并通过Dubbo的注册中心来发布服务。
首先需要实现服务接口,实现代码如下所示:
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<ResponseType, FormatHandler> handlers =
new
HashMap<ResponseType, FormatHandler>(
0
);
19
static
{
20
handlers.put(ResponseType.XML,
new
FormatHandler() {
21
public
String format() {
22
return
"&wt=xml"
;
23
}
24
});
25
handlers.put(ResponseType.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, ResponseType type,
42
int
start,
int
rows) {
43
StringBuffer url =
new
StringBuffer();
44
url.append(baseUrl).append(collection).append(
"/select?"
).append(q);
45
url.append(
"&start="
).append(start).append(
"&rows="
).append(rows);
46
url.append(handlers.get(type).format());
47
LOG.info(
"[REQ] "
+ url.toString());
48
return
postClient.request(url.toString());
49
}
50
51
interface
FormatHandler {
52
String format();
53
}
54
55
public
static
void
main(String[] args)
throws
IOException {
56
String config = SolrSearchServer.
class
.getPackage().getName().replace(
'.'
,
'/'
) +
"/search-provider.xml"
;
57
ClassPathXmlApplicationContext context =
new
ClassPathXmlApplicationContext(config);
58
context.start();
59
System.in.read();
60
}
61
62
}
对应的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
address
=
"zookeeper://slave1:2188?backup=slave3:2188,slave4:2188"
/>
10
<
dubbo:protocol
name
=
"dubbo"
port
=
"20880"
/>
11
<
bean
id
=
"searchService"
class
=
"org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer"
>
12
<
property
name
=
"baseUrl"
value
=
"http://nginx-lbserver/solr-cloud/"
/>
13
</
bean
>
14
<
dubbo:service
interface
=
"org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService"
ref
=
"searchService"
/>
15
16
</
beans
>
上面,Dubbo服务注册中心指定ZooKeeper的地址:zookeeper://slave1:2188?backup=slave3:2188,slave4:2188,使用Dubbo协议。配置服务接口的时候,可以按照Spring的Bean的配置方式来配置,注入需要的内容,我们这里指定了搜索集群的Nginx反向代理地址http://nginx-lbserver/solr-cloud/。
- Consumer调用服务设计
这个就比较简单了,拷贝服务接口,同时要配置一下Dubbo的配置文件,写个简单的客户端调用就可以实现。客户端实现的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.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService.ResponseType;
08
import
org.springframework.beans.BeansException;
09
import
org.springframework.context.support.AbstractXmlApplicationContext;
10
import
org.springframework.context.support.ClassPathXmlApplicationContext;
11
12
import
com.alibaba.dubbo.rpc.RpcContext;
13
14
public
class
SearchConsumer {
15
16
private
final
String collection;
17
private
AbstractXmlApplicationContext context;
18
private
SolrSearchService searchService;
19
20
public
SearchConsumer(String collection, Callable<AbstractXmlApplicationContext> call) {
21
super
();
22
this
.collection = collection;
23
try
{
24
context = call.call();
25
context.start();
26
searchService = (SolrSearchService) context.getBean(
"searchService"
);
27
}
catch
(BeansException e) {
28
e.printStackTrace();
29
}
catch
(Exception e) {
30
e.printStackTrace();
31
}
32
}
33
34
public
Future<String> asyncCall(
final
String q,
final
ResponseType type,
final
int
start,
final
int
rows) {
35
Future<String> future = RpcContext.getContext().asyncCall(
new
Callable<String>() {
36
public
String call()
throws
Exception {
37
return
search(q, type, start, rows);
38
}
39
});
40
return
future;
41
}
42
43
public
String syncCall(
final
String q,
final
ResponseType type,
final
int
start,
final
int
rows) {
44
return
search(q, type, start, rows);
45
}
46
47
private
String search(
final
String q,
final
ResponseType type,
final
int
start,
final
int
rows) {
48
return
searchService.search(collection, q, type, start, rows);
49
}
50
51
public
static
void
main(String[] args)
throws
Exception {
52
final
String collection =
"tinycollection"
;
53
final
String beanXML =
"search-consumer.xml"
;
54
final
String config = SearchConsumer.
class
.getPackage().getName().replace(
'.'
,
'/'
) +
"/"
+ beanXML;
55
SearchConsumer consumer =
new
SearchConsumer(collection,
new
Callable<AbstractXmlApplicationContext>() {
56
public
AbstractXmlApplicationContext call()
throws
Exception {
57
final
AbstractXmlApplicationContext context =
new
ClassPathXmlApplicationContext(config);
58
return
context;
59
}
60
});
61
62
String q =
"q=上海&fl=*&fq=building_type:1"
;
63
int
start =
0
;
64
int
rows =
10
;
65
ResponseType type = ResponseType.XML;
66
for
(
int
k =
0
; k <
10
; k++) {
67
for
(
int
i =
0
; i <
10
; i++) {
68
start =
1
*
10
* i;
69
if
(i %
2
==
0
) {
70
type = ResponseType.XML;
71
}
else
{
72
type = ResponseType.JSON;
73
}
74
// String result = consumer.syncCall(q, type, start, rows);
75
// System.out.println(result);
76
Future<String> future = consumer.asyncCall(q, type, start, rows);
77
// System.out.println(future.get());
78
}
79
}
80
}
81
}
查询的时候,需要提供查询字符串,符合Solr语法,例如“q=上海&fl=*&fq=building_type:1”。配置文件,我们使用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
address
=
"zookeeper://slave1:2188?backup=slave3:2188,slave4:2188"
/>
10
<
dubbo:reference
id
=
"searchService"
interface
=
"org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService"
/>
11
12
</
beans
>
运行说明
首先保证服务注册中心的ZooKeeper集群正常运行,然后启动SolrSearchServer,启动的时候直接将服务注册到ZooKeeper集群存储中,可以通过ZooKeeper的客户端脚本来查看注册的服务数据。一切正常以后,可以启动运行客户端SearchConsumer,调用SolrSearchServer所实现的远程搜索服务。
参考链接
- 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
- Dubbo实现RPC调用使用入门
- Dubbo实现RPC调用使用入门
- Dubbo实现RPC调用使用入门
- Dubbo实现RPC调用使用入门
- Dubbo实现RPC调用使用入门
- 使用hadoop RPC实现RPC调用
- 使用Mina实现RPC调用
- RPC入门总结(九)Dubbo框架实现细节
- dubbo rpc服务使用
- dubbo rpc 调用过程解析
- Dubbo的RPC调用过程
- Dubbo 简单RPC调用示例
- dubbo调用 RPC异常 IllegalStateException异常 解决方案
- dubbo核心技术原理:RPC调用协议
- dubbo核心技术原理:RPC调用协议
- RPC框架之-DUBBO(一)入门
- 关于RPC框架Dubbo学习-入门
- Axis2入门小项目-使用RPC方式调用WebService
- 用JS实现省市区三级联动 (数据从数据库查出)
- JMS + ActiveMQ 简单的demo
- [leetcode] 390. Elimination Game
- 安卓中GridView填充简单的文字和图片,笔记!
- python的取整函数:向上取整,向下取整,四舍五入取整
- Dubbo实现RPC调用使用入门
- 导出有索引大表的步骤(sql方法)。
- Java多线程综述
- Python 多层装饰器
- python实现log日志
- MFC库参考 典型HTTP客户端应用程序中的步骤
- 嵌入式操作系统与物联网演进之路
- 阿里云配置JAVA开发环境并部署WAR包
- php5.2 、5.3、5.4、5.5、5.6 各个版本升级不兼容点