天天看點

基于Dubbo的Hessian協定實作遠端調用

Dubbo基于Hessian實作了自己Hessian協定,可以直接通過配置的Dubbo内置的其他協定,在服務消費方進行遠端調用,也就是說,服務調用方需要使用Java語言來基于Dubbo調用提供方服務,限制了服務調用方。同時,使用Dubbo的Hessian協定實作提供方服務,而調用方可以使用标準的Hessian接口來調用,原生的Hessian協定已經支援多語言用戶端調用,支援語言如下所示:

下面,我們的思路是,先基于Dubbo封裝的Hessian協定,實作提供方服務和消費方調用服務,雙方必須都使用Dubbo來開發;然後,基于Dubbo封裝的Hessian協定實作提供方服務,然後服務消費方使用标準的Hessian接口來進行遠端調用,分别使用Java和Python語言來實作。而且,我們實作的提供方服務通過Tomcat釋出到服務注冊中心。

首先,使用Java語言定義一個搜尋服務的接口,代碼如下所示:

<code>1</code>

<code>package</code> <code>org.shirdrn.platform.dubbo.service.rpc.api;</code>

<code>2</code>

<code>3</code>

<code>public</code> <code>interface</code> <code>SolrSearchService {</code>

<code>4</code>

<code></code><code>String search(String collection, String q, String type,</code><code>int</code> <code>start,</code><code>int</code> <code>rows);</code>

<code>5</code>

<code>}</code>

上面接口提供了搜尋遠端調用功能。

基于Dubbo的Hessian協定實作提供方服務

提供方實作基于Dubbo封裝的Hessian協定,實作接口SolrSearchService,實作代碼如下所示:

<code>01</code>

<code>package</code> <code>org.shirdrn.platform.dubbo.service.rpc.server;</code>

<code>02</code>

<code>03</code>

<code>import</code> <code>java.io.IOException;</code>

<code>04</code>

<code>import</code> <code>java.util.HashMap;</code>

<code>05</code>

<code>import</code> <code>java.util.Map;</code>

<code>06</code>

<code>07</code>

<code>import</code> <code>org.apache.commons.logging.Log;</code>

<code>08</code>

<code>import</code> <code>org.apache.commons.logging.LogFactory;</code>

<code>09</code>

<code>import</code> <code>org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService;</code>

<code>10</code>

<code>import</code> <code>org.shirdrn.platform.dubbo.service.rpc.utils.QueryPostClient;</code>

<code>11</code>

<code>import</code> <code>org.springframework.context.support.ClassPathXmlApplicationContext;</code>

<code>12</code>

<code>13</code>

<code>public</code> <code>class</code> <code>SolrSearchServer</code><code>implements</code> <code>SolrSearchService {</code>

<code>14</code>

<code>15</code>

<code></code><code>private</code> <code>static</code> <code>final</code> <code>Log LOG = LogFactory.getLog(SolrSearchServer.</code><code>class</code><code>);</code>

<code>16</code>

<code></code><code>private</code> <code>String baseUrl;</code>

<code>17</code>

<code></code><code>private</code> <code>final</code> <code>QueryPostClient postClient;</code>

<code>18</code>

<code></code><code>private</code> <code>static</code> <code>final</code> <code>Map&lt;String, FormatHandler&gt; handlers =</code><code>new</code> <code>HashMap&lt;String, FormatHandler&gt;(</code><code>0</code><code>);</code>

<code>19</code>

<code></code><code>static</code> <code>{</code>

<code>20</code>

<code></code><code>handlers.put(</code><code>"xml"</code><code>,</code><code>new</code> <code>FormatHandler() {</code>

<code>21</code>

<code></code><code>public</code> <code>String format() {</code>

<code>22</code>

<code></code><code>return</code> <code>"&amp;wt=xml"</code><code>;</code>

<code>23</code>

<code></code><code>}</code>

<code>24</code>

<code></code><code>});</code>

<code>25</code>

<code></code><code>handlers.put(</code><code>"json"</code><code>,</code><code>new</code> <code>FormatHandler() {</code>

<code>26</code>

<code>27</code>

<code></code><code>return</code> <code>"&amp;wt=json"</code><code>;</code>

<code>28</code>

<code>29</code>

<code>30</code>

<code>31</code>

<code>32</code>

<code></code><code>public</code> <code>SolrSearchServer() {</code>

<code>33</code>

<code></code><code>super</code><code>();</code>

<code>34</code>

<code></code><code>postClient = QueryPostClient.newIndexingClient(</code><code>null</code><code>);</code>

<code>35</code>

<code>36</code>

<code>37</code>

<code></code><code>public</code> <code>void</code> <code>setBaseUrl(String baseUrl) {</code>

<code>38</code>

<code></code><code>this</code><code>.baseUrl = baseUrl;</code>

<code>39</code>

<code>40</code>

<code>41</code>

<code></code><code>public</code> <code>String search(String collection, String q, String type,</code><code>int</code> <code>start,</code><code>int</code><code>rows) {</code>

<code>42</code>

<code></code><code>StringBuffer url =</code><code>new</code> <code>StringBuffer();</code>

<code>43</code>

<code></code><code>url.append(baseUrl).append(collection).append(</code><code>"/select?"</code><code>).append(q);</code>

<code>44</code>

<code></code><code>url.append(</code><code>"&amp;start="</code><code>).append(start).append(</code><code>"&amp;rows="</code><code>).append(rows);</code>

<code>45</code>

<code></code><code>url.append(handlers.get(type.toLowerCase()).format());</code>

<code>46</code>

<code></code><code>LOG.info(</code><code>"[REQ] "</code> <code>+ url.toString());</code>

<code>47</code>

<code></code><code>return</code> <code>postClient.request(url.toString());</code>

<code>48</code>

<code>49</code>

<code>50</code>

<code></code><code>interface</code> <code>FormatHandler {</code>

<code>51</code>

<code></code><code>String format();</code>

<code>52</code>

<code>53</code>

因為考慮到後面要使用标準Hessian接口來調用,這裡接口方法參數全部使用内置标準類型。然後,我們使用Dubbo的配置檔案進行配置,檔案search-provider.xml的内容如下所示:

<code>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"UTF-8"</code><code>?&gt;</code>

<code></code><code>&lt;</code><code>dubbo:application</code> <code>name</code><code>=</code><code>"search-provider"</code> <code>/&gt;</code>

<code></code><code>&lt;</code><code>dubbo:registry</code>

<code></code><code>&lt;</code><code>dubbo:protocol</code> <code>name</code><code>=</code><code>"hessian"</code> <code>port</code><code>=</code><code>"8080"</code> <code>server</code><code>=</code><code>"servlet"</code> <code>/&gt;</code>

<code></code><code>&lt;</code><code>bean</code> <code>id</code><code>=</code><code>"searchService"</code>

<code></code><code>class</code><code>=</code><code>"org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer"</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>bean</code><code>&gt;</code>

<code></code><code>&lt;</code><code>dubbo:service</code>

<code></code><code>interface</code><code>=</code><code>"org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService"</code>

<code></code><code>ref</code><code>=</code><code>"searchService"</code> <code>path</code><code>=</code><code>"http_dubbo/search"</code> <code>/&gt;</code>

<code>&lt;/</code><code>beans</code><code>&gt;</code>

因為使用Tomcat釋出提供方服務,是以我們需要實作Spring的org.springframework.web.context.ContextLoader來初始化應用上下文(基于Spring的IoC容器來管理服務對象)。實作類SearchContextLoader代碼如下所示:

<code>package</code> <code>org.shirdrn.platform.dubbo.context;</code>

<code>import</code> <code>javax.servlet.ServletContextEvent;</code>

<code>import</code> <code>javax.servlet.ServletContextListener;</code>

<code>import</code> <code>org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer;</code>

<code>import</code> <code>org.springframework.web.context.ContextLoader;</code>

<code>public</code> <code>class</code> <code>SearchContextLoader</code><code>extends</code> <code>ContextLoader</code><code>implements</code><code>ServletContextListener {</code>

<code></code><code>@Override</code>

<code></code><code>public</code> <code>void</code> <code>contextDestroyed(ServletContextEvent arg0) {</code>

<code></code><code>// TODO Auto-generated method stub</code>

<code></code><code>public</code> <code>void</code> <code>contextInitialized(ServletContextEvent arg0) {</code>

<code></code><code>String config = arg0.getServletContext().getInitParameter(</code><code>"contextConfigLocation"</code><code>);</code>

<code></code><code>ClassPathXmlApplicationContext context =</code><code>new</code><code>ClassPathXmlApplicationContext(config);</code>

<code></code><code>context.start();</code>

最後,配置Web應用部署描述符檔案,web.xml内容如下所示:

<code>&lt;</code><code>web-app</code> <code>id</code><code>=</code><code>"WebApp_ID"</code> <code>version</code><code>=</code><code>"2.4"</code>

<code></code><code>&lt;</code><code>display-name</code><code>&gt;http_dubbo&lt;/</code><code>display-name</code><code>&gt;</code>

<code></code><code>&lt;</code><code>listener</code><code>&gt;</code>

<code></code><code>&lt;</code><code>listener-class</code><code>&gt;org.shirdrn.platform.dubbo.context.SearchContextLoader&lt;/</code><code>listener-class</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>listener</code><code>&gt;</code>

<code></code><code>&lt;</code><code>context-param</code><code>&gt;</code>

<code></code><code>&lt;</code><code>param-name</code><code>&gt;contextConfigLocation&lt;/</code><code>param-name</code><code>&gt;</code>

<code></code><code>&lt;</code><code>param-value</code><code>&gt;classpath:search-provider.xml&lt;/</code><code>param-value</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>context-param</code><code>&gt;</code>

<code></code><code>&lt;</code><code>servlet</code><code>&gt;</code>

<code></code><code>&lt;</code><code>servlet-name</code><code>&gt;search&lt;/</code><code>servlet-name</code><code>&gt;</code>

<code></code><code>&lt;</code><code>servlet-class</code><code>&gt;com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet&lt;/</code><code>servlet-class</code><code>&gt;</code>

<code></code><code>&lt;</code><code>init-param</code><code>&gt;</code>

<code></code><code>&lt;</code><code>param-name</code><code>&gt;home-class&lt;/</code><code>param-name</code><code>&gt;</code>

<code></code><code>&lt;</code><code>param-value</code><code>&gt;org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer&lt;/</code><code>param-value</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>init-param</code><code>&gt;</code>

<code></code><code>&lt;</code><code>param-name</code><code>&gt;home-api&lt;/</code><code>param-name</code><code>&gt;</code>

<code></code><code>&lt;</code><code>param-value</code><code>&gt;org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService&lt;/</code><code>param-value</code><code>&gt;</code>

<code></code><code>&lt;</code><code>load-on-startup</code><code>&gt;1&lt;/</code><code>load-on-startup</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>servlet</code><code>&gt;</code>

<code></code><code>&lt;</code><code>servlet-mapping</code><code>&gt;</code>

<code></code><code>&lt;</code><code>url-pattern</code><code>&gt;/search&lt;/</code><code>url-pattern</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>servlet-mapping</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file-list</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file</code><code>&gt;index.html&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file</code><code>&gt;index.htm&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file</code><code>&gt;index.jsp&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file</code><code>&gt;default.html&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file</code><code>&gt;default.htm&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code></code><code>&lt;</code><code>welcome-file</code><code>&gt;default.jsp&lt;/</code><code>welcome-file</code><code>&gt;</code>

<code></code><code>&lt;/</code><code>welcome-file-list</code><code>&gt;</code>

<code>&lt;/</code><code>web-app</code><code>&gt;</code>

啟動Tomcat以後,就可以将提供方服務釋出到服務注冊中心,這裡服務注冊中心我們使用的是ZooKeeper叢集,可以參考上面Dubbo配置檔案search-provider.xml的配置内容。

下面,我們通過兩種方式來調用已經注冊到服務注冊中心的服務。

基于Dubbo的Hessian協定遠端調用

服務消費方,通過Dubbo配置檔案來指定注冊到注冊中心的服務,配置檔案search-consumer.xml的内容,如下所示:

<code></code><code>&lt;</code><code>dubbo:application</code> <code>name</code><code>=</code><code>"search-consumer"</code> <code>/&gt;</code>

<code></code><code>&lt;</code><code>dubbo:reference</code> <code>id</code><code>=</code><code>"searchService"</code>

<code></code><code>interface</code><code>=</code><code>"org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService"</code> <code>/&gt;</code>

然後,使用Java實作遠端調用,實作代碼如下所示:

<code>package</code> <code>org.shirdrn.platform.dubbo.service.rpc.client;</code>

<code>import</code> <code>java.util.concurrent.Callable;</code>

<code>import</code> <code>java.util.concurrent.Future;</code>

<code>import</code> <code>org.springframework.beans.BeansException;</code>

<code>import</code> <code>org.springframework.context.support.AbstractXmlApplicationContext;</code>

<code>import</code> <code>com.alibaba.dubbo.rpc.RpcContext;</code>

<code>public</code> <code>class</code> <code>SearchConsumer {</code>

<code></code><code>private</code> <code>final</code> <code>String collection;</code>

<code></code><code>private</code> <code>AbstractXmlApplicationContext context;</code>

<code></code><code>private</code> <code>SolrSearchService searchService;</code>

<code></code><code>public</code> <code>SearchConsumer(String collection, Callable&lt;AbstractXmlApplicationContext&gt; call) {</code>

<code></code><code>this</code><code>.collection = collection;</code>

<code></code><code>try</code> <code>{</code>

<code></code><code>context = call.call();</code>

<code></code><code>searchService = (SolrSearchService) context.getBean(</code><code>"searchService"</code><code>);</code>

<code></code><code>}</code><code>catch</code> <code>(BeansException e) {</code>

<code></code><code>e.printStackTrace();</code>

<code></code><code>}</code><code>catch</code> <code>(Exception e) {</code>

<code></code><code>public</code> <code>Future&lt;String&gt; asyncCall(</code><code>final</code> <code>String q,</code><code>final</code> <code>String type,</code><code>final</code> <code>int</code><code>start,</code><code>final</code> <code>int</code> <code>rows) {</code>

<code></code><code>Future&lt;String&gt; future = RpcContext.getContext().asyncCall(</code><code>new</code> <code>Callable&lt;String&gt;() {</code>

<code></code><code>public</code> <code>String call()</code><code>throws</code> <code>Exception {</code>

<code></code><code>return</code> <code>search(q, type, start, rows);</code>

<code></code><code>return</code> <code>future;</code>

<code></code><code>public</code> <code>String syncCall(</code><code>final</code> <code>String q,</code><code>final</code> <code>String type,</code><code>final</code> <code>int</code> <code>start,</code><code>final</code><code>int</code> <code>rows) {</code>

<code></code><code>private</code> <code>String search(</code><code>final</code> <code>String q,</code><code>final</code> <code>String type,</code><code>final</code> <code>int</code> <code>start,</code><code>final</code><code>int</code> <code>rows) {</code>

<code></code><code>return</code> <code>searchService.search(collection, q, type, start, rows);</code>

<code></code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args)</code><code>throws</code> <code>Exception {</code>

<code></code><code>final</code> <code>String collection =</code><code>"tinycollection"</code><code>;</code>

<code></code><code>final</code> <code>String beanXML =</code><code>"search-consumer.xml"</code><code>;</code>

<code></code><code>final</code> <code>String config = SearchConsumer.</code><code>class</code><code>.getPackage().getName().replace(</code><code>'.'</code><code>,</code><code>'/'</code><code>) +</code><code>"/"</code> <code>+ beanXML;</code>

<code>54</code>

<code></code><code>SearchConsumer consumer =</code><code>new</code> <code>SearchConsumer(collection,</code><code>new</code><code>Callable&lt;AbstractXmlApplicationContext&gt;() {</code>

<code>55</code>

<code></code><code>public</code> <code>AbstractXmlApplicationContext call()</code><code>throws</code> <code>Exception {</code>

<code>56</code>

<code></code><code>final</code> <code>AbstractXmlApplicationContext context =</code><code>new</code><code>ClassPathXmlApplicationContext(config);</code>

<code>57</code>

<code></code><code>return</code> <code>context;</code>

<code>58</code>

<code>59</code>

<code>60</code>

<code>61</code>

<code></code><code>String q =</code><code>"q=上海&amp;fl=*&amp;fq=building_type:1"</code><code>;</code>

<code>62</code>

<code></code><code>int</code> <code>start =</code><code>0</code><code>;</code>

<code>63</code>

<code></code><code>int</code> <code>rows =</code><code>10</code><code>;</code>

<code>64</code>

<code></code><code>String type =</code><code>"xml"</code><code>;</code>

<code>65</code>

<code></code><code>for</code> <code>(</code><code>int</code> <code>k =</code><code>0</code><code>; k &lt;</code><code>10</code><code>; k++) {</code>

<code>66</code>

<code></code><code>for</code> <code>(</code><code>int</code> <code>i =</code><code>0</code><code>; i &lt;</code><code>10</code><code>; i++) {</code>

<code>67</code>

<code></code><code>start =</code><code>1</code> <code>*</code><code>10</code> <code>* i;</code>

<code>68</code>

<code></code><code>if</code> <code>(i %</code><code>2</code> <code>==</code><code>0</code><code>) {</code>

<code>69</code>

<code></code><code>type =</code><code>"xml"</code><code>;</code>

<code>70</code>

<code></code><code>}</code><code>else</code> <code>{</code>

<code>71</code>

<code></code><code>type =</code><code>"json"</code><code>;</code>

<code>72</code>

<code>73</code>

<code></code><code>String result = consumer.syncCall(q, type, start, rows);</code>

<code>74</code>

<code></code><code>System.out.println(result);</code>

<code>75</code>

<code></code><code>// Future&lt;String&gt; future = consumer.asyncCall(q, type, start,</code>

<code>76</code>

<code></code><code>// rows);</code>

<code>77</code>

<code></code><code>// System.out.println(future.get());</code>

<code>78</code>

<code>79</code>

<code>80</code>

<code>81</code>

<code>82</code>

執行該調用實作,可以遠端調用提供方釋出的服務。

這種方式限制了服務調用方也必須使用Dubbo來開發調用的代碼,也就是限制了程式設計的語言,而無論是對于内部還是外部,各個團隊之間必然存在語言的多樣性,如果限制了程式設計語言,那麼開發的服務也隻能在内部使用。

基于标準Hessian協定接口的遠端調用

下面,使用标準Hessian接口來實作遠端調用,這時就不需要關心服務提供方的所使用的開發語言,因為最終是通過HTTP的方式來通路。我們需要下載下傳Hessian對應語言的調用實作庫,才能更友善地程式設計。

使用Java語言實作遠端調用

使用Java語言實作,代碼如下所示:

<code>package</code> <code>org.shirdrn.rpc.hessian;</code>

<code>import</code> <code>com.caucho.hessian.client.HessianProxyFactory;</code>

<code>public</code> <code>class</code> <code>HessianConsumer {</code>

<code></code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args)</code><code>throws</code> <code>Throwable {</code>

<code></code><code>HessianProxyFactory factory =</code><code>new</code> <code>HessianProxyFactory();</code>

<code></code><code>SolrSearchService searchService = (SolrSearchService) factory.create(SolrSearchService.</code><code>class</code><code>, serviceUrl);</code>

<code></code><code>String collection =</code><code>"tinycollection"</code><code>;</code>

<code></code><code>String result = searchService.search(collection, q, type, start, rows);</code>

使用Python語言實作遠端調用

<code>cd</code> <code>mustaine</code>

<code>sudo</code> <code>python setup.py</code><code>install</code>

然後就可以使用了,使用Python進行遠端調用的實作代碼如下所示:

<code>#!/usr/bin/python</code>

<code># coding=utf-8</code>

<code>from</code> <code>mustaine.client</code><code>import</code> <code>HessianProxy</code>

<code>q</code><code>=</code> <code>'q=*:*&amp;fl=*&amp;fq=building_type:1'</code>

<code>start</code><code>=</code> <code>0</code>

<code>rows</code><code>=</code> <code>10</code>

<code>resType</code><code>=</code> <code>'xml'</code>

<code>collection</code><code>=</code> <code>'tinycollection'</code>

<code>if</code> <code>__name__</code><code>=</code><code>=</code> <code>'__main__'</code><code>:</code>

<code></code><code>proxy</code><code>=</code> <code>HessianProxy(serviceUrl)</code>

<code></code><code>result</code><code>=</code> <code>proxy.search(collection, q, resType, start, rows)</code>

<code></code><code>print</code> <code>result</code>

運作上面程式,就可以看到遠端調用的結果。