前兩天,我在開源中國的微信公衆號看到新浪微博的輕量Rpc框架——Motan開源了。上網查了下,才得知這個Motan來頭不小,支撐着新浪微博的千億調用,曾經在2014年的春晚中有着千億次的調用,對抗了春晚的最高峯值。java
2013 年微博 RPC 框架 Motan 在前輩大師們(福林、fishermen、小麥、王喆等)的精心設計和辛勤工做中誕生,向各位大師們致敬,也獲得了微博各個技術團隊的鼎力支持及不斷完善,現在 Motan 在微博平臺中已經普遍應用,天天爲數百個服務完成近千億次的調用。」 —— 張雷git
微博的Motan RPC服務,底層通信引擎採用了Netty網絡框架,序列化協議支持Hessian和Java序列化,通信協議支持Motan、http、tcp、mc等,Motan框架在內部大量使用,在系統的健壯性和服務治理方面,有較爲成熟的技術解決方案,健壯性上,基於Config配置管理服務實現了High Availability與Load Balance策略(支持靈活的FailOver和FailFast HA策略,以及Round Robin、LRU、Consistent Hash等Load Balance策略),服務治理方面,生成完整的服務調用鏈數據,服務請求性能數據,響應時間(Response Time)、QPS以及標準化Error、Exception日誌信息。github
Motan 屬於服務治理類型,是一個基於 Java 開發的高性能的輕量級 RPC 框架,Motan 提供了實用的服務治理功能和優秀的 RPC 協議擴展能力。
Motan 提供的主要功能包括:
服務發現 :服務發佈、訂閱、通知
高可用策略 :失敗重試(Failover)、快速失敗(Failfast)、異常隔離(Server 連續失敗超過指定次數置爲不可用,而後按期進行心跳探測)
負載均衡 :支持低併發優先、一致性 Hash、隨機請求、輪詢等
擴展性 :支持 SPI 擴展(service provider interface)
其餘 :調用統計、訪問日誌等spring
Motan 能夠支持不一樣的 RPC 協議、傳輸協議。Motan 可以無縫支持 Spring 配置方式使用 RPC 服務,經過簡單、靈活的配置就能夠提供或使用 RPC 服務。經過使用 Motan 框架,能夠十分方便的進行服務拆分、分佈式服務部署。shell
關於Motan的更多內容可參考:http://h2ex.com/820
以及Motan的源碼:https://github.com/weibocom/motanapache
參照github中wiki,能夠快速的跑一跑motan,提早感覺一下,因爲Motan剛開源,不少東西還不完整,我我的在這中間也遇到不少坑,後面一一介紹。我按照wiki介紹,先建立maven項目motandemo,api
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hjc.demo</groupId> <artifactId>motandemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>motandemo</name> <repositories> <repository> <id>spy</id> <name>36</name> <layout>default</layout> <url>http://repo1.maven.org/maven2</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-core</artifactId> <version>0.0.1</version> </dependency> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-transport-netty</artifactId> <version>0.0.1</version> </dependency> <!-- 集羣相關 --> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-registry-consul</artifactId> <version>0.0.1</version> </dependency> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-registry-zookeeper</artifactId> <version>0.0.1</version> </dependency> <!-- dependencies blow were only needed for spring-based features --> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-springsupport</artifactId> <version>0.0.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.5.RELEASE</version> </dependency> </dependencies> <build> <finalName>motan</finalName> </build> </project>
注意若是maven下載不下來能夠去倉庫直接搜,而後下載jar包,我在搭建的過程就遇到了jar包下載不下來的狀況,多是網絡緣由吧。
倉庫地址:http://mvnrepository.com/
下載下來後使用maven進行本地安裝,安裝命令以下:微信
mvn install:install-file -Dfile=<path-to-file> -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=<version> -Dpackaging=<packaging>
肯定pom.xml不報錯以後再進行下面的步驟網絡
建立接口,服務方和調用方都使用這個接口FooService
FooService:併發
package com.hjc.motan.server; import java.util.List; import java.util.Map; import com.hjc.motan.DemoBean; public interface FooService { public String hello(String name); public int helloInt(int number1); public double helloDouble(double number2); public List<String> helloList(List<String> list); public Map<String, List<String>> helloMap(Map<String, List<String>> map); public DemoBean helloJavabean(DemoBean bean); }
服務方來實現這個接口的邏輯
FooServiceImpl:
package com.hjc.motan.server; import java.util.List; import java.util.Map; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.hjc.motan.DemoBean; public class FooServiceImpl implements FooService { public static void main(String[] args) throws InterruptedException { ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "classpath:motan_server.xml"); System.out.println("server start..."); } public String hello(String name) { System.out.println("invoked rpc service " + name); return "hello " + name; } public int helloInt(int number1) { System.out.println("invoked rpc service " + number1); return number1; } public double helloDouble(double number2) { System.out.println("invoked rpc service " + number2); return number2; } public List<String> helloList(List<String> list) { System.out.print("invoked rpc service "); for (String string : list) { System.out.print(string + ","); } System.out.println(); return list; } public Map<String, List<String>> helloMap(Map<String, List<String>> map) { System.out.print("invoked rpc service "); for (String key : map.keySet()) { System.out.print(key + ":["); for (String list : map.get(key)) { System.out.print(list + ","); } System.out.print("],"); } System.out.println(); return map; } public DemoBean helloJavabean(DemoBean bean) { System.out.print("invoked rpc service " + bean); System.out.print("," + bean.getId()); System.out.print("," + bean.getName()); System.out.print("," + bean.getScore()); System.out.println(); return bean; } }
在項目根目錄(src/main/datasource)建立motan_server.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:motan="http://api.weibo.com/schema/motan" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd"> <!-- service implemention bean --> <bean id="serviceImpl" class="com.hjc.motan.server.FooServiceImpl" /> <!-- exporting service by motan --> <motan:service interface="com.hjc.motan.server.FooService" ref="serviceImpl" export="8002" /> </beans>
在這個過程當中,我發現個人eclipse不能自動下載motan.xsd,這時候我只能手動配置,從motan-core的jar包中,找到這個schema文件,複製到任意位置,而後eclipse中,選擇Window->Preferences->XML->XML Catalog->User Specified Entries,點擊Add,輸入Location和Key,按照以下圖所示:
以上步驟完成以後,就能夠啓動Motan Rpc的服務方了,在FooServiceImpl中已經寫好了main方法,右鍵run便可
在項目根目錄(src/main/datasource)建立motan_server.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:motan="http://api.weibo.com/schema/motan" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd"> <!-- reference to the remote service --> <motan:referer id="remoteService" interface="com.hjc.motan.server.FooService" directUrl="localhost:8002"/> </beans>
建立Client類調用服務方的接口並輸出
Client:
package com.hjc.motan.client; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Map; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.hjc.motan.DemoBean; import com.hjc.motan.server.FooService; public class Client { public static void main(String[] args) throws InterruptedException { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:motan_client.xml"); // 獲取到service FooService service = (FooService) ctx.getBean("remoteService"); // rpc調用 /** String **/ String ret1 = service.hello("motan"); System.out.println(ret1); /** int **/ int ret2 = service.helloInt(110); System.out.println(ret2); /** double **/ double ret3 = service.helloDouble(11.2); System.out.println(ret3); /** list **/ List<String> list = new ArrayList<String>(); list.add("hello"); list.add("motan"); List<String> ret4 = service.helloList(list); for (String string : ret4) { System.out.print(string + ","); } System.out.println(); /** map **/ Map<String, List<String>> map = new HashMap<String, List<String>>(); map.put("key1", Arrays.asList(new String[] { "val1","val2" })); map.put("key2", Arrays.asList(new String[] { "val1","val2","val3" })); map.put("key3", Arrays.asList(new String[] { "val1","val2","val3","val4" })); Map<String, List<String>> ret5 = service.helloMap(map); for (String key : ret5.keySet()) { System.out.print(key + ":["); for (String tmp : map.get(key)) { System.out.print(tmp + ","); } System.out.print("],"); } System.out.println(); /** javabean **/ DemoBean bean = new DemoBean(); bean.setId(1001l); bean.setName("motan bean"); bean.setScore(99.998); DemoBean ret6 = service.helloJavabean(bean); System.out.print(ret6.getId()); System.out.print("," + ret6.getName()); System.out.print("," + ret6.getScore()); System.out.println(); } }
啓動Client的main方法開始調用
經過以上demo建立了server端和client端,分別啓動服務方和調用方以後,查看控制檯輸出以下:
服務方:
server start... invoked rpc service motan invoked rpc service 110 invoked rpc service 11.2 invoked rpc service hello,motan, invoked rpc service key3:[val1,val2,val3,val4,],key2:[val1,val2,val3,],key1:[val1,val2,], invoked rpc service com.hjc.motan.DemoBean@2cf3e1fd,1001,motan bean,99.998
調用方:
hello motan 110 11.2 hello,motan, key3:[val1,val2,val3,val4,],key2:[val1,val2,val3,],key1:[val1,val2,], 1001,motan bean,99.998
實現集羣使用只須要在上面的基礎作一點稍稍的改變就能夠,motan的集羣與阿里的dubbo的原理相似,經過註冊方、服務方、調用方三方來實現,三者關係圖以下:
目前按照wiki說明,motan支持Consul和Zookeeper兩種外部服務發現組件
如下咱們再上面實現的基礎之上進行更改(兩種組件分別有介紹)
在最上面,其實已經寫出來了
<!-- consul --> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-registry-consul</artifactId> <version>0.0.1</version> </dependency> <!-- zookeeper --> <dependency> <groupId>com.weibo</groupId> <artifactId>motan-registry-zookeeper</artifactId> <version>0.0.1</version> </dependency>
consul
<motan:registry regProtocol="consul" name="my_consul" address="127.0.0.1:8500"/>
zookeeper單節點
<motan:registry regProtocol="zookeeper" name="my_zookeeper" address="127.0.0.1:2181"/>
zookeeper多節點集羣
<motan:registry regProtocol="zookeeper" name="my_zookeeper" address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"/>
consul client
<motan:referer id="remoteService" interface="quickstart.FooService" registry="my_consul"/>
consul server
<motan:service interface="quickstart.FooService" ref="serviceImpl" registry="my_consul" export="8002" />
zookeeper client
<motan:referer id="remoteService" interface="quickstart.FooService" registry="my_zookeeper"/>
zookeeper server
<motan:service interface="quickstart.FooService" ref="serviceImpl" registry="my_zookeeper" export="8002" />
consul須要顯示調用心跳開關注冊到consul(zookeeper不須要)
MotanSwitcherUtil.setSwitcher(ConsulConstants.NAMING_PROCESS_HEARTBEAT_SWITCHER, true)
我也是正好最近項目空閒,恰好又看到這麼一則新聞,因而就動手瞭解了下,固然,我所作的只是淺層次的使用,至於更深層次的內容(如,跟dubbo等其餘rpc框架的對比,集羣上下線對zookeeper的影響等)還沒來得及去研究,不過既然它曾經支撐過千億的調用,那必定是通過實際運營檢驗的,做爲搞技術的,也應該多去了解了解開源的東西,這裏我想說一句,開源真好!
另外,以上demo代碼我也傳到了個人github上,歡迎交流學習:
https://github.com/hjcenry/motan-demo
個人我的博客開通啦,每一篇文章都在簡書跟我的博客同步,地址是:http://hjcenry.github.io