Dubbo实现RPC调用使用入门

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介:

使用Dubbo进行远程调用实现服务交互,它支持多种协议,如Hessian、HTTP、RMI、Memcached、Redis、Thrift等等。由于Dubbo将这些协议的实现进行了封装了,无论是服务端(开发服务)还是客户端(调用服务),都不需要关心协议的细节,只需要在配置中指定使用的协议即可,从而保证了服务提供方与服务消费方之间的透明。
另外,如果我们使用Dubbo的服务注册中心组件,这样服务提供方将服务发布到注册的中心,只是将服务的名称暴露给外部,而服务消费方只需要知道注册中心和服务提供方提供的服务名称,就能够透明地调用服务,后面我们会看到具体提供服务和消费服务的配置内容,使得双方之间交互的透明化。

示例场景

我们给出一个示例的应用场景:
服务方提供一个搜索服务,对服务方来说,它基于SolrCloud构建了搜索服务,包含两个集群,ZooKeeper集群和Solr集群,然后在前端通过Nginx来进行反向代理,达到负载均衡的目的。
服务消费方就是调用服务进行查询,给出查询条件(满足Solr的REST-like接口)。

应用设计

基于上面的示例场景,我们打算使用ZooKeeper集群作为服务注册中心。注册中心会暴露给服务提供方和服务消费方,所以注册服务的时候,服务先提供方只需要提供Nginx的地址给注册中心,但是注册中心并不会把这个地址暴露给服务消费方,如图所示:

4850b105087f1af56e76851837f9805912088ef3

我们先定义一下,通信双方需要使用的接口,如下所示:

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, introws);
06
07 public enum ResponseType {
08 JSON,
09 XML
10 }
11 }

基于上图中的设计,下面我们分别详细说明Provider和Consumer的设计及实现。

  • Provider服务设计

Provider所发布的服务组件,包含了一个SolrCloud集群,在SolrCloud集群前端又加了一个反向代理层,使用Nginx来均衡负载。Provider的搜索服务系统,设计如下图所示:

5cf44fce20b8a24c2a6d705ca18d037cd2a8c6d8

上图中,实际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 = newHashMap<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 = newClassPathXmlApplicationContext(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:serviceinterface="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 intstart, 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, newCallable<AbstractXmlApplicationContext>() {
56 public AbstractXmlApplicationContext call() throws Exception {
57 final AbstractXmlApplicationContext context = newClassPathXmlApplicationContext(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所实现的远程搜索服务。

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
16天前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
3月前
|
负载均衡 Dubbo Java
Dubbo 3.x:探索阿里巴巴的开源RPC框架新技术
随着微服务架构的兴起,远程过程调用(RPC)框架成为了关键组件。Dubbo,作为阿里巴巴的开源RPC框架,已经演进到了3.x版本,带来了许多新特性和技术改进。本文将探讨Dubbo 3.x中的一些最新技术,包括服务注册与发现、负载均衡、服务治理等,并通过代码示例展示其使用方式。
85 9
|
2月前
|
Dubbo Cloud Native 网络协议
【Dubbo3技术专题】「服务架构体系」第一章之Dubbo3新特性要点之RPC协议分析介绍
【Dubbo3技术专题】「服务架构体系」第一章之Dubbo3新特性要点之RPC协议分析介绍
39 1
|
8月前
|
编解码 Dubbo 应用服务中间件
Alibaba开源Dubbo源码解析手册,竟引领出RPC的新潮流
前言 Apache Dubbo,一款由阿里巴巴于2011年开源的高性能Java RPC框架,自开源以来在业界产生了深远影响。有大量公司广泛使用,甚至很多公司的自研RPC框架中都能看到Dubbo的影子。Dubbo在国内服务化体系演进过程中扮演了重要角色。尽管经历了几年的沉寂,但在阿里巴巴重启对Dubbo的开源维护,Dubbo正在从微服务领域的高性能RPC框架逐步演变为一个完整的微服务生态。 对于开发者来说,深入了解Dubbo底层的架构设计和实现是一项挑战。因此,一份完整的、体系化的对Apache Dubbo进行深入原理剖析的手册就显得尤为重要。
|
8月前
|
消息中间件 负载均衡 Dubbo
如何自己设计一个类似Dubbo的RPC框架?
如何自己设计一个类似Dubbo的RPC框架?
64 0
|
2月前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
44 0
|
2月前
|
Dubbo Java 应用服务中间件
Spring Boot整合Dubbo+Zookeeper实现RPC调用
Spring Boot整合Dubbo+Zookeeper实现RPC调用 技术栈说明 Dubbo:Dubbo作为RPC框架,能在多个服务之间实现远程服务的调用。比如有两个独立的微服务A和B,A服务想要调用B服务时,因为两者不在同个内存空间中,不能直接调用,所以可以通过Dubbo实现这点。 功能和Spring Cloud的Feign相同,两者都是应用于微服务架构的远程调用框架 Zookeeper:作为注册中心去管理Dubbo服务,这点和Eureka、Nacos相同。 概述 通过一个示例说明Dubbo+Zookeeper在Spring Boot中的应用。 现有两个服务provider和con
120 4
|
9月前
|
Dubbo Java 应用服务中间件
springboot + dubbo + zookeeper入门到实战超级详解
springboot + dubbo + zookeeper入门到实战超级详解
128 0
|
5月前
|
Dubbo Java 应用服务中间件
分布式应用简单入门及SpringBoot整合Dubbo+Zookeeper
分布式应用简单入门及SpringBoot整合Dubbo+Zookeeper
48 1
|
6月前
|
Dubbo Java 应用服务中间件
微服务技术系列教程(29) - Dubbo-介绍&环境安装&入门案例
微服务技术系列教程(29) - Dubbo-介绍&环境安装&入门案例
56 0