zoukankan      html  css  js  c++  java
  • 基于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

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

    参考链接

  • 相关阅读:
    hdu 4002 Find the maximum
    hdu 2837 坑题。
    hdu 3123
    zoj Treasure Hunt IV
    hdu 2053 Switch Game 水题一枚,鉴定完毕
    poj 1430 Binary Stirling Numbers
    hdu 3037 Saving Beans
    hdu 3944 dp?
    南阳oj 求N!的二进制表示最低位的1的位置(从右向左数)。
    fzu 2171 防守阵地 II
  • 原文地址:https://www.cnblogs.com/zzwx/p/8820210.html
Copyright © 2011-2022 走看看