• 基于Dubbo的Hessian协议实现远程调用


    基于Dubbo的Hessian协议实现远程调用

    Dubbo基于Hessian实现了自己Hessian协议,可以直接通过配置的Dubbo内置的其他协议,在服务消费方进行远程调用,也就是说,服务调用方需要使用Java语言来基于Dubbo调用提供方服务,限制了服务调用方。同时,使用Dubbo的Hessian协议实现提供方服务,而调用方可以使用标准的Hessian接口来调用,原生的Hessian协议已经支持多语言客户端调用,支持语言如下所示:

    下面,我们的思路是,先基于Dubbo封装的Hessian协议,实现提供方服务和消费方调用服务,双方必须都使用Dubbo来开发;然后,基于Dubbo封装的Hessian协议实现提供方服务,然后服务消费方使用标准的Hessian接口来进行远程调用,分别使用Java和Python语言来实现。而且,我们实现的提供方服务通过Tomcat发布到服务注册中心。
    首先,使用Java语言定义一个搜索服务的接口,代码如下所示:

    1
    2
    3
    4
    5
    package org.shirdrn.platform.dubbo.service.rpc.api;
     
    public interface SolrSearchService {
        String search(String collection, String q, String type, int start, int rows);
    }

    上面接口提供了搜索远程调用功能。

    基于Dubbo的Hessian协议实现提供方服务

    提供方实现基于Dubbo封装的Hessian协议,实现接口SolrSearchService,实现代码如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package org.shirdrn.platform.dubbo.service.rpc.server;
     
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
     
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;
    import org.shirdrn.platform.dubbo.service.rpc.utils.QueryPostClient;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
     
    public class SolrSearchServer implements SolrSearchService {
     
        private static final Log LOG = LogFactory.getLog(SolrSearchServer.class);
        private String baseUrl;
        private final QueryPostClient postClient;
        private static final Map<String, FormatHandler> handlers = new HashMap<String, FormatHandler>(0);
        static {
            handlers.put("xml", new FormatHandler() {
                public String format() {
                    return "&wt=xml";
                }
            });
            handlers.put("json", new FormatHandler() {
                public String format() {
                    return "&wt=json";
                }
            });
        }
     
        public SolrSearchServer() {
            super();
            postClient = QueryPostClient.newIndexingClient(null);
        }
     
        public void setBaseUrl(String baseUrl) {
            this.baseUrl = baseUrl;
        }
     
        public String search(String collection, String q, String type, int start, int rows) {
            StringBuffer url = new StringBuffer();
            url.append(baseUrl).append(collection).append("/select?").append(q);
            url.append("&start=").append(start).append("&rows=").append(rows);
            url.append(handlers.get(type.toLowerCase()).format());
            LOG.info("[REQ] " + url.toString());
            return postClient.request(url.toString());
        }
     
        interface FormatHandler {
            String format();
        }
    }

    因为考虑到后面要使用标准Hessian接口来调用,这里接口方法参数全部使用内置标准类型。然后,我们使用Dubbo的配置文件进行配置,文件search-provider.xml的内容如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?xml version="1.0" encoding="UTF-8"?>
     
     
        <dubbo:application name="search-provider" />
        <dubbo:registry
            address="zookeeper://slave1:2188?backup=slave3:2188,slave4:2188" />
        <dubbo:protocol name="hessian" port="8080" server="servlet" />
        <bean id="searchService"
            class="org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer">
            <property name="baseUrl" value="http://nginx-lbserver/solr-cloud/" />
        </bean>
        <dubbo:service
            interface="org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService"
            ref="searchService" path="http_dubbo/search" />
     
    </beans>

    因为使用Tomcat发布提供方服务,所以我们需要实现Spring的org.springframework.web.context.ContextLoader来初始化应用上下文(基于Spring的IoC容器来管理服务对象)。实现类SearchContextLoader代码如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package org.shirdrn.platform.dubbo.context;
     
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
     
    import org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.web.context.ContextLoader;
     
    public class SearchContextLoader extends ContextLoader implements ServletContextListener {
     
        @Override
        public void contextDestroyed(ServletContextEvent arg0) {
            // TODO Auto-generated method stub
     
        }
     
        @Override
        public void contextInitialized(ServletContextEvent arg0) {
            String config = arg0.getServletContext().getInitParameter("contextConfigLocation");
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
            context.start();
        }
     
    }

    最后,配置Web应用部署描述符文件,web.xml内容如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app id="WebApp_ID" version="2.4"
        <display-name>http_dubbo</display-name>
     
        <listener>
            <listener-class>org.shirdrn.platform.dubbo.context.SearchContextLoader</listener-class>
        </listener>
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:search-provider.xml</param-value>
        </context-param>
     
        <servlet>
            <servlet-name>search</servlet-name>
            <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>home-class</param-name>
                <param-value>org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer</param-value>
            </init-param>
            <init-param>
                <param-name>home-api</param-name>
                <param-value>org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>search</servlet-name>
            <url-pattern>/search</url-pattern>
        </servlet-mapping>
     
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
            <welcome-file>default.html</welcome-file>
            <welcome-file>default.htm</welcome-file>
            <welcome-file>default.jsp</welcome-file>
        </welcome-file-list>
    </web-app>

    启动Tomcat以后,就可以将提供方服务发布到服务注册中心,这里服务注册中心我们使用的是ZooKeeper集群,可以参考上面Dubbo配置文件search-provider.xml的配置内容。

    下面,我们通过两种方式来调用已经注册到服务注册中心的服务。

    • 基于Dubbo的Hessian协议远程调用

    服务消费方,通过Dubbo配置文件来指定注册到注册中心的服务,配置文件search-consumer.xml的内容,如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="UTF-8"?>
     
     
        <dubbo:application name="search-consumer" />
        <dubbo:registry
            address="zookeeper://slave1:2188?backup=slave3:2188,slave4:2188" />
        <dubbo:reference id="searchService"
            interface="org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService" />
     
    </beans>

    然后,使用Java实现远程调用,实现代码如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    package org.shirdrn.platform.dubbo.service.rpc.client;
     
    import java.util.concurrent.Callable;
    import java.util.concurrent.Future;
     
    import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;
    import org.springframework.beans.BeansException;
    import org.springframework.context.support.AbstractXmlApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
     
    import com.alibaba.dubbo.rpc.RpcContext;
     
    public class SearchConsumer {
     
        private final String collection;
        private AbstractXmlApplicationContext context;
        private SolrSearchService searchService;
     
        public SearchConsumer(String collection, Callable<AbstractXmlApplicationContext> call) {
            super();
            this.collection = collection;
            try {
                context = call.call();
                context.start();
                searchService = (SolrSearchService) context.getBean("searchService");
            } catch (BeansException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
     
        public Future<String> asyncCall(final String q, final String type, final int start, final int rows) {
            Future<String> future = RpcContext.getContext().asyncCall(new Callable<String>() {
                public String call() throws Exception {
                    return search(q, type, start, rows);
                }
            });
            return future;
        }
     
        public String syncCall(final String q, final String type, final int start, final int rows) {
            return search(q, type, start, rows);
        }
     
        private String search(final String q, final String type, final int start, final int rows) {
            return searchService.search(collection, q, type, start, rows);
        }
     
        public static void main(String[] args) throws Exception {
            final String collection = "tinycollection";
            final String beanXML = "search-consumer.xml";
            final String config = SearchConsumer.class.getPackage().getName().replace('.', '/') + "/" + beanXML;
            SearchConsumer consumer = new SearchConsumer(collection, new Callable<AbstractXmlApplicationContext>() {
                public AbstractXmlApplicationContext call() throws Exception {
                    final AbstractXmlApplicationContext context = new ClassPathXmlApplicationContext(config);
                    return context;
                }
            });
     
            String q = "q=上海&fl=*&fq=building_type:1";
            int start = 0;
            int rows = 10;
            String type = "xml";
            for (int k = 0; k < 10; k++) {
                for (int i = 0; i < 10; i++) {
                    start = 1 * 10 * i;
                    if (i % 2 == 0) {
                        type = "xml";
                    } else {
                        type = "json";
                    }
                    String result = consumer.syncCall(q, type, start, rows);
                    System.out.println(result);
                    // Future<String> future = consumer.asyncCall(q, type, start,
                    // rows);
                    // System.out.println(future.get());
                }
            }
        }
     
    }

    执行该调用实现,可以远程调用提供方发布的服务。
    这种方式限制了服务调用方也必须使用Dubbo来开发调用的代码,也就是限制了编程的语言,而无论是对于内部还是外部,各个团队之间必然存在语言的多样性,如果限制了编程语言,那么开发的服务也只能在内部使用。

    • 基于标准Hessian协议接口的远程调用

    下面,使用标准Hessian接口来实现远程调用,这时就不需要关心服务提供方的所使用的开发语言,因为最终是通过HTTP的方式来访问。我们需要下载Hessian对应语言的调用实现库,才能更方便地编程。

    使用Java语言实现远程调用
    使用Java语言实现,代码如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package org.shirdrn.rpc.hessian;
     
    import org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;
     
    import com.caucho.hessian.client.HessianProxyFactory;
     
    public class HessianConsumer {
     
        public static void main(String[] args) throws Throwable {
     
            String serviceUrl = "http://10.95.3.74:8080/http_dubbo/search";
            HessianProxyFactory factory = new HessianProxyFactory();
     
            SolrSearchService searchService = (SolrSearchService) factory.create(SolrSearchService.class, serviceUrl);
     
            String q = "q=上海&fl=*&fq=building_type:1";
            String collection = "tinycollection";
            int start = 0;
            int rows = 10;
            String type = "xml";
            String result = searchService.search(collection, q, type, start, rows);
            System.out.println(result);
        }
    }

    我们只需要知道提供服务暴露的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
    2
    3
    git clone https://github.com/bgilmore/mustaine.git
    cd mustaine
    sudo python setup.py install

    然后就可以使用了,使用Python进行远程调用的实现代码如下所示:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    #!/usr/bin/python
     
    # coding=utf-8
    from mustaine.client import HessianProxy
     
    q = 'q=*:*&fl=*&fq=building_type:1'
    start = 0
    rows = 10
    resType = 'xml'
    collection = 'tinycollection'
     
    if __name__ == '__main__':
         proxy = HessianProxy(serviceUrl)
         result = proxy.search(collection, q, resType, start, rows)
         print result

    运行上面程序,就可以看到远程调用的结果。

    参考链接

  • 相关阅读:
    Cocos2d-x 3.0 beta 中加入附加项目,解决无法打开包括文件:“extensions/ExtensionMacros.h”: No such file or directory”
    C、Shell、Perl基于Tomcat开发CGI程序环境配置
    Windows机器配置启动加载器的高级选项后,机器出现蓝屏,无法RDP
    Linux由于物理节点故障导致的异常重启-Case1
    Azure经典虚拟机(Windows)如何监测单个磁盘的使用空间
    ARM VM安装Linux Diagnostic 2.3扩展
    rsyslog服务日志报错分析1
    登陆Linux服务器时触发邮件提醒
    部署Azure Log Analytics
    获取指定订阅下所有Azure ARM虚拟机配置(CPU核数,内存大小,磁盘信息)的使用情况
  • 原文地址:https://www.cnblogs.com/zzwx/p/8820210.html
Copyright © 2020-2023  润新知