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<String, FormatHandler> handlers =</code><code>new</code> <code>HashMap<String, FormatHandler>(</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>"&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>"&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>"&start="</code><code>).append(start).append(</code><code>"&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><?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"UTF-8"</code><code>?></code>
<code></code><code><</code><code>dubbo:application</code> <code>name</code><code>=</code><code>"search-provider"</code> <code>/></code>
<code></code><code><</code><code>dubbo:registry</code>
<code></code><code><</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>/></code>
<code></code><code><</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>></code>
<code></code><code></</code><code>bean</code><code>></code>
<code></code><code><</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>/></code>
<code></</code><code>beans</code><code>></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><</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><</code><code>display-name</code><code>>http_dubbo</</code><code>display-name</code><code>></code>
<code></code><code><</code><code>listener</code><code>></code>
<code></code><code><</code><code>listener-class</code><code>>org.shirdrn.platform.dubbo.context.SearchContextLoader</</code><code>listener-class</code><code>></code>
<code></code><code></</code><code>listener</code><code>></code>
<code></code><code><</code><code>context-param</code><code>></code>
<code></code><code><</code><code>param-name</code><code>>contextConfigLocation</</code><code>param-name</code><code>></code>
<code></code><code><</code><code>param-value</code><code>>classpath:search-provider.xml</</code><code>param-value</code><code>></code>
<code></code><code></</code><code>context-param</code><code>></code>
<code></code><code><</code><code>servlet</code><code>></code>
<code></code><code><</code><code>servlet-name</code><code>>search</</code><code>servlet-name</code><code>></code>
<code></code><code><</code><code>servlet-class</code><code>>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</</code><code>servlet-class</code><code>></code>
<code></code><code><</code><code>init-param</code><code>></code>
<code></code><code><</code><code>param-name</code><code>>home-class</</code><code>param-name</code><code>></code>
<code></code><code><</code><code>param-value</code><code>>org.shirdrn.platform.dubbo.service.rpc.server.SolrSearchServer</</code><code>param-value</code><code>></code>
<code></code><code></</code><code>init-param</code><code>></code>
<code></code><code><</code><code>param-name</code><code>>home-api</</code><code>param-name</code><code>></code>
<code></code><code><</code><code>param-value</code><code>>org.shirdrn.platform.dubbo.service.rpc.api.SolrSearchService</</code><code>param-value</code><code>></code>
<code></code><code><</code><code>load-on-startup</code><code>>1</</code><code>load-on-startup</code><code>></code>
<code></code><code></</code><code>servlet</code><code>></code>
<code></code><code><</code><code>servlet-mapping</code><code>></code>
<code></code><code><</code><code>url-pattern</code><code>>/search</</code><code>url-pattern</code><code>></code>
<code></code><code></</code><code>servlet-mapping</code><code>></code>
<code></code><code><</code><code>welcome-file-list</code><code>></code>
<code></code><code><</code><code>welcome-file</code><code>>index.html</</code><code>welcome-file</code><code>></code>
<code></code><code><</code><code>welcome-file</code><code>>index.htm</</code><code>welcome-file</code><code>></code>
<code></code><code><</code><code>welcome-file</code><code>>index.jsp</</code><code>welcome-file</code><code>></code>
<code></code><code><</code><code>welcome-file</code><code>>default.html</</code><code>welcome-file</code><code>></code>
<code></code><code><</code><code>welcome-file</code><code>>default.htm</</code><code>welcome-file</code><code>></code>
<code></code><code><</code><code>welcome-file</code><code>>default.jsp</</code><code>welcome-file</code><code>></code>
<code></code><code></</code><code>welcome-file-list</code><code>></code>
<code></</code><code>web-app</code><code>></code>
啟動Tomcat以後,就可以将提供方服務釋出到服務注冊中心,這裡服務注冊中心我們使用的是ZooKeeper叢集,可以參考上面Dubbo配置檔案search-provider.xml的配置内容。
下面,我們通過兩種方式來調用已經注冊到服務注冊中心的服務。
基于Dubbo的Hessian協定遠端調用
服務消費方,通過Dubbo配置檔案來指定注冊到注冊中心的服務,配置檔案search-consumer.xml的内容,如下所示:
<code></code><code><</code><code>dubbo:application</code> <code>name</code><code>=</code><code>"search-consumer"</code> <code>/></code>
<code></code><code><</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>/></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<AbstractXmlApplicationContext> 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<String> 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<String> future = RpcContext.getContext().asyncCall(</code><code>new</code> <code>Callable<String>() {</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<AbstractXmlApplicationContext>() {</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=上海&fl=*&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 <</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 <</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<String> 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=*:*&fl=*&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>
運作上面程式,就可以看到遠端調用的結果。