• [转载] Dubbo实现RPC调用使用入门


    转载自http://shiyanjun.cn/archives/341.html

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

    示例场景

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

    应用设计

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

    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的搜索服务系统,设计如下图所示:
    solrcloud-cluster
    上图中,实际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  
    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 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, finalint rows) {
    44         return search(q, type, start, rows);
    45     }
    46  
    47     private String search(final String q, final ResponseType type, final int start, finalint 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  
    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所实现的远程搜索服务。

    参考链接

  • 相关阅读:
    linux socket里的send和recv,阻塞与非阻塞socket、TCP与UDP在这方面的区别
    leetcode 149 Max Points on a Line
    leetcode 126 Word Ladder II
    leetcode 123 Best Time to Buy and Sell Stock III
    LC 297 Serialize and Deserialize Binary Tree
    栈和队列问题
    链表问题
    day17--权限管理和配置服务
    谷粒学院功能简介及系统架构
    day01--MybatisPlus的使用
  • 原文地址:https://www.cnblogs.com/scott19820130/p/4614925.html
Copyright © 2020-2023  润新知