這是一個簡單小巧的Java RPC框架,適用於Java平台內、為系統之間的交互提供了、高性能、低延遲的方案。適合在集群數量偏少的情況下使用(50台以下集群環境)。當然、它也可以在大型集群環境下使用,由於未引入Zookeeper支持,所以它在大型集群環境下不夠成熟,例如服務發現以及監控都沒有做,但是作為RPC框架來用已經足夠,至少比使用rest、webservice等性能高得多,也比直接使用thrift、avro等方便的多。
為了讓它保持小巧、簡單,所以不打算引入Zookeeper支持。我認為50台server組成的集群,已經可以滿足絕大部分需求,所以簡單、小巧、高性能才是最重要的。如果你認為簡單不重要,或者成熟度是最重要的,那麼淘寶的Dubbo在等著你。http://dubbo.io/
心血來潮,在公司很無聊,這才是主要原因。 其次是我們要對系統模塊進行拆分,從原系統移出,那麼就需要尋找一個遠程調用工具。
1、基於HTTP(rest、webservice等) 主要性能很差,其次是難以支持高並發,而且組裝HTTP請求也比較麻煩,難以形成規范,所以此項被pass。
2、基於thrift,性能雖好,但是使用起來非常麻煩,需要頻繁的生成代碼,而且對Client開發者要求較高,需要自己寫連接池,長連接無法使用LVS還要寫負載均衡和容錯。而且thrift的服務端需要將業務邏輯全部放在一個接口(一個接口就需要發布一個服務,占用一個線程池),這將是個很惡心的事,所以也被pass。
正因為以上兩點,所以我打算自己寫一個框架。要求是:簡單小巧、依賴少、高性能、高並發、支持集群、負載均衡、容錯。無學習成本,源碼簡單可定制修改,我認為這些才是最主要的。
如果你也在尋找這樣一個框架,那麼很值得看一下。
我做完了這個框架,沒多久便發現了Dubbo,在看了Dubbo的設計以後,驚喜的發現此框架和Dubbo的核心功能幾乎一樣。
說起來很簡單,就是框架會在Client端代理一個接口,調用這個接口的方法,將發送遠程請求,參數序列化傳遞到遠程Server端,Server端處理業務邏輯,完成後、將返回結果序列化給Client端,作為被調用方法的返回值。因此整個過程對用戶是透明的。
項目底層使用thrift,這是為了使用thrift的各種Server模型,以此來支持高並發,低延遲。沒有使用Netty,原因是Netty較重,延遲要比thrift稍高一些,Netty適合處理高吞吐的異步IO,對於RPC的同步調用沒有好處。Netty並不適合。您不用擔心thrift性能有問題,也不用擔心thrift框架太重,我做過測試,性能和直接使用soket通信幾乎不相上下,thrift框架的代碼特別少,僅僅是對soket的簡單封裝,框架非常輕便。
序列化工具使用kryo,這也是性能的關鍵,您也可以自己去查一下kryo相關資料,這裡就不說他了,序列化結果很小,速度很快就是了。
框架依賴 thrift、kryo、commons-pool、spring-beans(其中kryo可以自行替換為您喜歡的序列化工具)
集群支持隨機負載均衡,輪詢負載均衡(您也可以自己寫負載均衡實現),優雅停機(kill pid不要加-9),容錯(集群某幾台掛掉並不影響服務)
線程模型 以ThriftThreadPoolServer、ThriftTThreadedSelectorServer 兩種為主,具體細節參考thrift(您也可以自己實現Server)
服務端:
/appchina-rpc-test/src/main/java/main/Server.java
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("/application-server.xml");
ctx.registerShutdownHook();
ctx.start();
/appchina-rpc-test/src/main/resources/application-server.xml
<bean id="servicePublisher" class="com.appchina.rpc.thrift.remote.base.ThriftServicePublisher">
<property name="definitions">
<!-- 需要發布的服務列表 -->
<array>
<!-- ServiceDefinition 定義了服務的信息 -->
<bean class="com.appchina.rpc.remote.ServiceDefinition">
<!-- 可選,用於區分不同實現類 -->
<property name="serviceName" value="addServiceImpl"></property>
<!-- 發布服務的接口 -->
<property name="interfaceClass" value="com.appchina.rpc.test.api.AddService"></property>
<!-- 發布服務實現類 -->
<property name="implInstance">
<bean class="com.appchina.rpc.test.impl.AddServiceImpl" />
</property>
</bean>
</array>
</property>
</bean>
<bean class="com.appchina.rpc.thrift.server.ThriftThreadPoolServer">
<property name="processor" ref="servicePublisher"></property>
<property name="port" value="9090"></property>
<property name="minWorkerThreads" value="100"></property>
<property name="workerThreads" value="500"></property>
<property name="security" value="true"></property>
<property name="stopTimeoutVal" value="3000"></property>
<property name="clientTimeout" value="10000"></property>
<property name="allowedFromTokens">
<map>
<entry key="DONGJIAN" value="DSIksduiKIOYUIOkYIOhIOUIOhjklYUI"></entry>
</map>
</property>
</bean>
客戶端:
/appchina-rpc-test/src/main/java/main/Client.java
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("/application-client.xml");
AddService addService = (AddService) ctx.getBean("addService");
addService.add(100);
/appchina-rpc-test/src/main/resources/application-client.xml
<bean id="factoryProvider" class="com.appchina.rpc.thrift.cluster.ThriftClientFactoryProvider">
<!-- server列表 -->
<property name="hostPorts">
<list>
<value>127.0.0.1:9090</value>
<value>127.0.0.1:9191</value>
</list>
</property>
<!-- 請求超時,根據業務設置 -->
<property name="timeout" value="60000"></property>
<!-- 連接超時,超過這個時間無法創建連接的server將被設置為暫時無效,恢復時設置為有效 -->
<property name="connectionTimeout" value="10000"></property>
<!-- 如果服務端是NIO,需要啟用此配置 -->
<property name="framed" value="false"></property>
<!-- 安全選項 -->
<property name="from" value="DONGJIAN"></property>
<property name="token" value="DSIksduiKIOYUIOkYIOhIOUIOhjklYUI"></property>
</bean>
<!-- 關於集群的相關配置 -->
<bean id="client" class="com.appchina.rpc.base.cluster.client.DistributeClient">
<property name="factoryProvider" ref="factoryProvider"></property>
<!-- 負載均衡實現類 -->
<property name="loadBalance">
<bean class="com.appchina.rpc.base.cluster.RoundrobinLoadBalance"></bean>
</property>
<!-- 心跳頻率,用於檢測Server可用性的間隔 -->
<property name="heartbeat" value="1000"></property>
<!-- 處理心跳的最大線程數,一般1個線程足夠 -->
<property name="maxHeartbeatThread" value="1"></property>
<!-- 連接池耗完直接重試,重試其他池子的次數,因此、maxWait的時間可能疊加 -->
<property name="retry" value="3"></property>
<!-- 由於被借走時間不一樣,可能導致單個池子不夠用,建議這個值大一些,可以通過maxIdle來限制長連接數量 -->
<property name="maxActive" value="300"></property>
<!-- 最大空閒,當池內連接大於maxIdle,每次returnObject都會銷毀連接,maxIdle保證了長連接的數量 -->
<property name="maxIdle" value="200"></property>
<!-- 最小空閒 -->
<property name="minIdle" value="5"></property>
<!-- 等待連接池的時間 -->
<property name="maxWait" value="1000"></property>
<!-- 連接使用多久之後被銷毀 -->
<property name="maxKeepMillis" value="-1"></property>
<!-- 連接使用多少次之後被銷毀 -->
<property name="maxSendCount" value="-1"></property>
</bean>
<bean id="addService" class="com.appchina.rpc.thrift.remote.base.ThriftRemoteProxyFactory">
<property name="serviceName" value="addService"></property>
<property name="proxyInterface" value="com.appchina.rpc.test.api.AddService"></property>
<property name="client" ref="client"></property>
</bean>
git:https://github.com/dongjian1029/java-rpc-thrift.git