remoting 一個基于Java Spi機制實現(xiàn)的遠程服務調用組件

remoting

介紹

遠程服務調用組件
1.遠程服務調用組件是基于Java SPI(Service Provider Interface)機制實現(xiàn)贴硫,具有插件式棍弄,高度可擴展该园,集成使用簡單等特點兆解。
2.獨創(chuàng)的XML配置格式及配套解析方法披摄,讓遠程接口配置更為簡單亲雪,處理更為靈活,
3.組件提供統(tǒng)一的遠程服務調用API行疏,解決了遠程服務調用代碼分散匆光,實現(xiàn)各異套像,配置硬編碼等問題酿联。
4.同時統(tǒng)一抽象的調用器接口使得每個服務的具體通信方式和格式對使用者都是透明的,可通過配置不同的調用器類來支持相應的調用策略。
5.遠程服務調用組件贞让,屏蔽了具體的通信方式和數(shù)據(jù)格式轉換細節(jié)周崭,程序員只需要傳入?yún)?shù)對象和服務ID即可。

軟件架構

類關系模型

image

基于XML配置文件喳张,配置遠程接口续镇,提供統(tǒng)一遠程調用API,解決了遠程服務調用代碼分散销部,配置硬編碼在代碼中摸航,開發(fā)效率低下的問題。與現(xiàn)在使用的大多數(shù)方法相比舅桩,本方法簡化了遠程服務調用流程酱虎,配置靈活,開發(fā)效率大大提高,具體內容如下:

  1. 獨創(chuàng)XML配置格式:
<services>

     <http-endpoint id="http_credits" service-addr="/v1/integral/"type="https">
            <host address="www.test.com" port="" appKey="30a10e21" secretKey="43351af641be41e08d398f6952d4b8c1" livemode="0" scope="INTEGRAL" />
     </http-endpoint>

   <import service-id="add" api="add" endpoint-ref="http_credits">
     <url-param name="accesstoken">#accessToken</url-param>
     <data-param name="appid">#appKey</data-param>
     <data-param name="livemode">#livemode</data-param>
   </import>

</services>
  1. XML解析模塊:系統(tǒng)啟動時,讀取XML配置文件擂涛,經過XML文件格式校驗读串,節(jié)點完整性校驗,節(jié)點數(shù)據(jù)格式合法性校驗撒妈,解析等過程恢暖,最終將XML信息轉換為系統(tǒng)內部接口服務元數(shù)據(jù)信息,保存在內存中狰右,供下一步使用杰捂。

  2. 服務注冊模塊:完成XML解析之后,服務注冊模塊棋蚌,讀取接口服務元數(shù)據(jù)信息琼娘,將原數(shù)據(jù)信息轉換為服務調用模塊需要的接口服務信息格式,保存在內存中附鸽,供服務調用模塊使用脱拼。

  3. 接口服務調用模塊:提供了統(tǒng)一的遠程服務調用接口,程序根據(jù)服務ID定位到已經注冊的遠程服務接口坷备,傳入請求參數(shù)熄浓,即可完成遠程服務的調用,屏蔽了不同服務接口省撑,請求方式赌蔑,參數(shù)格式,協(xié)議類型等差異竟秫,讓使用更為簡單娃惯,開發(fā)效率更高。

  4. 服務響應處理模塊:接口服務調用模塊完成服務調用后肥败,返回信息將由此模塊進行統(tǒng)一處理趾浅,將不同接口服務響應數(shù)據(jù)格式轉換為統(tǒng)一的接口響應格式愕提,也可根據(jù)不同接口配置不同的響應數(shù)據(jù)處理(擴展開發(fā)),屏蔽了響應數(shù)據(jù)格式差異皿哨。

安裝教程

  1. Maven工程導入方式:pom.xml 文件添加如下依賴

    <dependency>
       <groupId>com.remoting</groupId>
       <artifactId>remoting-core</artifactId>
       <version>1.0.0</version>
    </dependency>
    
  2. jar包引入方式:導入以下包到工程

    remoting-api-1.0.0.jar
    remoting-core-1.0.0.jar
    remoting-tools-1.0.0.jar
    

使用說明

  1. 配置:以jar包引入方式浅侨,需要配置remoting.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    
     <!-- 遠程服務調用器 -->
     <bean id="remoteServiceExecutor"
         class="com.remoting.core.http.HttpRemoteServiceExecutor">
         <!-- 構造函數(shù):遠程服務配置  -->
         <constructor-arg ref="remoteSetting" />
     </bean>
     <!-- 遠程服務配置 -->
     <bean id="remoteSetting"
         class="com.remoting.api.RemoteSetting">
         <!-- 遠程服務配置文件路徑 -->
         <property name="configLocation" value="service-import*.xml" />
         <!-- 連接池最大并發(fā)連接數(shù) -->
         <property name="maxTotal" value="300" />
         <!-- 單路由最大并發(fā)數(shù) -->
         <property name="defaultMaxPerRoute" value="50" />
         <!-- 數(shù)據(jù)傳輸處理時間(毫秒) -->
         <property name="socketTimeout" value="4000" />
         <!-- 建立連接的timeout時間(毫秒) -->
         <property name="connectionTimeout" value="3000" />
         <!-- 從連接池中獲取連接的timeout時間(毫秒) -->
         <property name="connectionRequestTimeout" value="1000" />
     </bean>
    </beans>
    
  1. 遠程服務注冊配置

    <?xml version="1.0" encoding="UTF-8"?>
    <services>
        <!-- 注意:accessToken,livemode,appKey,secretKey 為系統(tǒng)變量,以#開頭使用证膨,使用后節(jié)點的值將會被替換為系統(tǒng)生成的值 -->
     <http-endpoint id="http_personalizedRecommend" service-addr="/CHiQ3/launcher/" type="http">
         <!-- 服務器地址
         <host address="chiq3.chiq-cloud.com" />-->
         <!-- 測試服務器地址 -->
         <host address="chiq3.test-cloud.com" port="" />
     </http-endpoint>
     <!-- 獲取子類目個性化推薦 -->
     <import service-id="together" api="together" endpoint-ref="http_personalizedRecommend" request-type="post" ></import>
     
    </services>
    
  2. 配置參數(shù)說明

    注意:accessToken,livemode,appKey 為系統(tǒng)變量如输,以#開頭使用,使用后節(jié)點的值將會被替換為系統(tǒng)生成的值

    對應配置文件說明:

    http-endpoint 節(jié)點
    屬性
    id:服務地址節(jié)點ID,全局唯一央勒。
    Service-addr:路徑不见。
    Type:類型。
    Host子點:
    屬性
    address:地址崔步。
    port:端口脖祈。
    appKey:應用的唯一標示
    secretKey:安全碼
    livemode:模式
    scope:作用域
    http-header 節(jié)點
    屬性
    id:服務地址節(jié)點ID,全局唯一。
    charset:編碼類型刷晋。
    Header子點:
    屬性
    name:header鍵盖高。
    Import 節(jié)點:要導入的接口
    屬性
    Service-id:服務ID,全局唯一眼虱。
    Api: 即積分系統(tǒng)發(fā)布的接口名稱喻奥。
    Endpoint-ref:服務地址配置引用ID。
    Header-ref:http請求頭信息配置引用ID捏悬。
    request-type:請求方式(post,get)撞蚕,默認post。
    子節(jié)點
    url-param:需要添加到URL中的參數(shù)配置过牙。
    data-param:需要添加到請求參數(shù)中的參數(shù)配置甥厦。
    注意:取值支持SpingEL表達式,需在請求參數(shù)中配置該值寇钉,如

     <!-- 積分查詢 -->
     <import service-id="query" api="" endpoint-ref="http_credits" request-type="get">
         <url-param name="accesstoken">#accessToken</url-param>
         <url-param name="appid">#appKey</url-param>
         <url-param name="livemode">#livemode</url-param>
     </import>
    

Key 也支持SpingEL表達式:根據(jù)配置的EL表達式找到對應的節(jié)點并添加對應的字段(要添加的節(jié)點必須存在)如

  <!-- 推送消息 -->
 <import service-id="pushMsg" api="pushMsg" endpoint-ref="http_mag">
     <data-param name="apikey">#appKey</data-param>
     <data-param name="secretkey">#secretKey</data-param>
     <data-param name="mType">THIRD</data-param>
     <data-param name="apiversion">1.0</data-param>
     <data-param name="['tag']['online']">0</data-param>
     <data-param name="['tag']['userflag']">2</data-param>
     <data-param name="['tag']['target']['condition']">AND</data-param>
 </import>
特別說明:
  1. url-param:需要添加到URL中的參數(shù)配置刀疙。
    如上例中的:
   ```
    <url-param name="accesstoken">#accessToken</url-param>
   ```
   
   ?      對應積分系統(tǒng)發(fā)布的接口規(guī)范中,url部分:
   
   ```
    https://www.xxx.com?accesstoken=xxx
   ```
   
    其中url-name 節(jié)點中的 name屬性對應的值為url中的accesstoken(注意大小寫與url中保持一致)扫倡。
   
   accessToken 則表示引用系統(tǒng)變量accessToken谦秧,系統(tǒng)自動識別,根據(jù)endpoint-ref引用配置中host配置(appKey, secretKey, scope)撵溃,以及當前import節(jié)點屬性api的值疚鲤,生成accessToken。
  1. data-param:需要添加到請求參數(shù)中的參數(shù)配置缘挑。
    如上例中的:

    <url-param name="appid">#appKey</url-param>
    <url-param name="livemode">#livemode</url-param>
    

    對應積分系統(tǒng)發(fā)布的接口規(guī)范中集歇,正文部分相應的字段。

像這種會根據(jù)服務器環(huán)境變化而變化语淘,且在請求參數(shù)中的參數(shù)诲宇,可以用此節(jié)點配置际歼,配置后,調用程序中就不需要再設置焕窝。
其中url-name 節(jié)點中的 name屬性對應的值為接口規(guī)范請求參數(shù)中的key。

  1. url-param 维贺,data-param:
    取值支持SpingEL表達式它掂,需在請求參數(shù)中配置該值。
    如:

    <url-param name="token]">['token']</data-param>
    

    Key EL表達式

    <data-param name="['tag']['online']">0</data-param>
    
  2. Header-ref:
    http請求頭信息引用ID溯泣,如果不設置則使用組件默認值:
    即:Content-Type:application/json虐秋,編碼為:UTF-8

  1. 程序調用

    package com.remoting;
    
    import com.remoting.api.RemoteServiceExecutor;
    import com.remoting.api.RemoteSetting;
    import com.remoting.api.spi.executor.RemoteServiceExecutorFactory;
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Unit test for simple App.
     */
    public class RemotingTest extends TestCase {
     private final Logger logger = LoggerFactory.getLogger(RemotingTest.class);
     private RemoteServiceExecutor remoteExecutor;
     protected void setUp() throws Exception {
         remoteExecutor = remoteExecutor();
     }
     private RemoteServiceExecutor remoteExecutor(){
         return RemoteServiceExecutorFactory.create();
     }
     /**
      * 構建遠程服務配置
      * <p>說明:</p>
      * <li></li>
      * @author DuanYong
      * @return
      * @since 2017年7月31日下午4:45:39
      */
     private RemoteSetting createRemoteSetting() {
         RemoteSetting remoteSetting = new RemoteSetting();
         remoteSetting.setConfigLocation("remoting/service-import*.xml");
         remoteSetting.setConnectionRequestTimeout(5 * 1000);
         remoteSetting.setConnectionTimeout(3 * 1000);
         remoteSetting.setDefaultMaxPerRoute(50);
         remoteSetting.setSocketTimeout(4 * 1000);
         remoteSetting.setMaxTotal(300);
         return remoteSetting;
     }
     /**
      * Create the test case
      *
      * @param testName
      *            name of the test case
      */
     public RemotingTest(String testName) {
         super(testName);
     }
    
     /**
      * @return the suite of tests being tested
      */
     public static Test suite() {
         return new TestSuite(RemotingTest.class);
     }
    
     /**
      * Rigourous Test :-)
      */
     public void testApp() {
    //       Map<String, Object> param = new HashMap<String, Object>();
    //       param.put("mac", "18-99-f5-5d-71-c7");
    //       param.put("sversion", "ZLM65HiS2_0.00094");
    //       param.put("img_size", "300,564");
    //       param.put("keyword", "tv");
    //       Object resultObj = remoteExecutor.execute("together", param);
    //       assertNotNull(resultObj);
         
         Map<String, Object> param = new HashMap<String, Object>();
         param.put("billUserId", "2088102971016535");
         param.put("startTime", "2019-02-18");
         param.put("endTime", "2019-02-19");
         param.put("ctoken", "u6vanf_DNIG55OWM");
         Object resultObj = remoteExecutor.execute("tradeListQuery", param);
         logger.info(resultObj.toString());
         System.out.println(resultObj.toString());
         assertNotNull(resultObj);
     }
     public void testTogether() {
         Map<String, Object> param = new HashMap<String, Object>();
         param.put("mac", "18-99-f5-5d-71-c7");
         param.put("sversion", "ZLM65HiS2_0.00094");
         param.put("img_size", "300,564");
         param.put("keyword", "tv");
         Object resultObj = remoteExecutor.execute("together", param);
         assertNotNull(resultObj);
     }
    
    }
    
    
項目信息
路漫漫其修遠兮,吾將上下而求索
碼云:https://gitee.com/javacoo/remoting
QQ:164863067
作者/微信:javacoo
郵箱:xihuady@126.com

下載地址

https://gitee.com/javacoo/remoting

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市垃沦,隨后出現(xiàn)的幾起案子客给,更是在濱河造成了極大的恐慌,老刑警劉巖肢簿,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靶剑,死亡現(xiàn)場離奇詭異,居然都是意外死亡池充,警方通過查閱死者的電腦和手機桩引,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來收夸,“玉大人坑匠,你說我怎么就攤上這事∥韵В” “怎么了厘灼?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咽瓷。 經常有香客問我设凹,道長,這世上最難降的妖魔是什么茅姜? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任围来,我火速辦了婚禮,結果婚禮上匈睁,老公的妹妹穿的比我還像新娘监透。我一直安慰自己,他們只是感情好航唆,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布胀蛮。 她就那樣靜靜地躺著,像睡著了一般糯钙。 火紅的嫁衣襯著肌膚如雪粪狼。 梳的紋絲不亂的頭發(fā)上退腥,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音再榄,去河邊找鬼狡刘。 笑死,一個胖子當著我的面吹牛困鸥,可吹牛的內容都是我干的嗅蔬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疾就,長吁一口氣:“原來是場噩夢啊……” “哼澜术!你這毒婦竟也來了?” 一聲冷哼從身側響起猬腰,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤鸟废,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后姑荷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盒延,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年鼠冕,在試婚紗的時候發(fā)現(xiàn)自己被綠了兰英。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡供鸠,死狀恐怖畦贸,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情楞捂,我是刑警寧澤薄坏,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站寨闹,受9級特大地震影響胶坠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜繁堡,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一沈善、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧椭蹄,春花似錦闻牡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至翼馆,卻和暖如春割以,著一層夾襖步出監(jiān)牢的瞬間金度,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工严沥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猜极,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓消玄,卻偏偏與公主長得像跟伏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子莱找,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354