《轉(zhuǎn)》RPC 原理

原文地址:http://blog.jobbole.com/92290/

在校期間大家都寫(xiě)過(guò)不少程序墨坚,比如寫(xiě)個(gè)hello world服務(wù)類,然后本地調(diào)用下申鱼,如下所示英妓。這些程序的特點(diǎn)是服務(wù)消費(fèi)方和服務(wù)提供方是本地調(diào)用關(guān)系。

而一旦踏入公司尤其是大型互聯(lián)網(wǎng)公司就會(huì)發(fā)現(xiàn)酒请,公司的系統(tǒng)都由成千上萬(wàn)大大小小的服務(wù)組成骡技,各服務(wù)部署在不同的機(jī)器上,由不同的團(tuán)隊(duì)負(fù)責(zé)羞反。這時(shí)就會(huì)遇到兩個(gè)問(wèn)題:1)要搭建一個(gè)新服務(wù)布朦,免不了需要依賴他人的服務(wù),而現(xiàn)在他人的服務(wù)都在遠(yuǎn)端昼窗,怎么調(diào)用是趴?2)其它團(tuán)隊(duì)要使用我們的服務(wù),我們的服務(wù)該怎么發(fā)布以便他人調(diào)用膏秫?下文我們將對(duì)這兩個(gè)問(wèn)題展開(kāi)探討右遭。

public interface HelloWorldService {
    String sayHello(String msg);
}
public class HelloWorldServiceImpl implements HelloWorldService {
    @Override
    public String sayHello(String msg) {
        String result = "hello world " + msg;
        System.out.println(result);
        return result;
    }
}
public class Test {
     public static void main(String[] args) {
         HelloWorldService helloWorldService = new HelloWorldServiceImpl();
         helloWorldService.sayHello("test");
     }
 }

1 如何調(diào)用他人的遠(yuǎn)程服務(wù)做盅?

由于各服務(wù)部署在不同機(jī)器缤削,服務(wù)間的調(diào)用免不了網(wǎng)絡(luò)通信過(guò)程窘哈,服務(wù)消費(fèi)方每調(diào)用一個(gè)服務(wù)都要寫(xiě)一坨網(wǎng)絡(luò)通信相關(guān)的代碼,不僅復(fù)雜而且極易出錯(cuò)亭敢。

如果有一種方式能讓我們像調(diào)用本地服務(wù)一樣調(diào)用遠(yuǎn)程服務(wù)滚婉,而讓調(diào)用者對(duì)網(wǎng)絡(luò)通信這些細(xì)節(jié)透明,那么將大大提高生產(chǎn)力帅刀,比如服務(wù)消費(fèi)方在執(zhí)行helloWorldService.sayHello(“test”)時(shí)让腹,實(shí)質(zhì)上調(diào)用的是遠(yuǎn)端的服務(wù)。這種方式其實(shí)就是RPC(Remote Procedure Call Protocol)扣溺,在各大互聯(lián)網(wǎng)公司中被廣泛使用骇窍,如阿里巴巴的hsf、dubbo(開(kāi)源)锥余、Facebook的thrift(開(kāi)源)腹纳、Google grpc(開(kāi)源)、Twitter的finagle等驱犹。

要讓網(wǎng)絡(luò)通信細(xì)節(jié)對(duì)使用者透明嘲恍,我們自然需要對(duì)通信細(xì)節(jié)進(jìn)行封裝,我們先看下一個(gè)RPC調(diào)用的流程:

image
  • 1)服務(wù)消費(fèi)方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù)雄驹;
  • 2)client stub接收到調(diào)用后負(fù)責(zé)將方法佃牛、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;
  • 3)client stub找到服務(wù)地址医舆,并將消息發(fā)送到服務(wù)端俘侠;
  • 4)server stub收到消息后進(jìn)行解碼;
  • 5)server stub根據(jù)解碼結(jié)果調(diào)用本地的服務(wù)蔬将;
  • 6)本地服務(wù)執(zhí)行并將結(jié)果返回給server stub兼贡;
  • 7)server stub將返回結(jié)果打包成消息并發(fā)送至消費(fèi)方;
  • 8)client stub接收到消息娃胆,并進(jìn)行解碼遍希;
  • 9)服務(wù)消費(fèi)方得到最終結(jié)果。

RPC的目標(biāo)就是要2~8這些步驟都封裝起來(lái)里烦,讓用戶對(duì)這些細(xì)節(jié)透明凿蒜。

1.1 怎么做到透明化遠(yuǎn)程服務(wù)調(diào)用?

怎么封裝通信細(xì)節(jié)才能讓用戶像以本地調(diào)用方式調(diào)用遠(yuǎn)程服務(wù)呢胁黑?對(duì)java來(lái)說(shuō)就是使用代理废封!java代理有兩種方式:1) jdk 動(dòng)態(tài)代理;2)字節(jié)碼生成丧蘸。盡管字節(jié)碼生成方式實(shí)現(xiàn)的代理更為強(qiáng)大和高效漂洋,但代碼不易維護(hù),大部分公司實(shí)現(xiàn)RPC框架時(shí)還是選擇動(dòng)態(tài)代理方式。

下面簡(jiǎn)單介紹下動(dòng)態(tài)代理怎么實(shí)現(xiàn)我們的需求刽漂。我們需要實(shí)現(xiàn)RPCProxyClient代理類演训,代理類的invoke方法中封裝了與遠(yuǎn)端服務(wù)通信的細(xì)節(jié),消費(fèi)方首先從RPCProxyClient獲得服務(wù)提供方的接口贝咙,當(dāng)執(zhí)行helloWorldService.sayHello(“test”)方法時(shí)就會(huì)調(diào)用invoke方法样悟。

public class RPCProxyClient implements java.lang.reflect.InvocationHandler{
    private Object obj;
    public RPCProxyClient(Object obj){
        this.obj=obj;
    }
    /**
     * 得到被代理對(duì)象;
     */
    public static Object getProxy(Object obj){
        return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), new RPCProxyClient(obj));
    }

    /**
     * 調(diào)用此方法執(zhí)行
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //結(jié)果參數(shù);
        Object result = new Object();
        // ...執(zhí)行通信相關(guān)邏輯
        // ...
        return result;
    }
}
public class Test {
    public static void main(String[] args) {
        HelloWorldService helloWorldService = (HelloWorldService)RPCProxyClient.getProxy(HelloWorldService.class);
        helloWorldService.sayHello("test");
    }
}

1.2 怎么對(duì)消息進(jìn)行編碼和解碼?

1.2.1 確定消息數(shù)據(jù)結(jié)構(gòu)

上節(jié)講了invoke里需要封裝通信細(xì)節(jié)庭猩,而通信的第一步就是要確定客戶端和服務(wù)端相互通信的消息結(jié)構(gòu)窟她。客戶端的請(qǐng)求消息結(jié)構(gòu)一般需要包括以下內(nèi)容:

1)接口名稱

在我們的例子里接口名是“HelloWorldService”蔼水,如果不傳震糖,服務(wù)端就不知道調(diào)用哪個(gè)接口了;

2)方法名

一個(gè)接口內(nèi)可能有很多方法趴腋,如果不傳方法名服務(wù)端也就不知道調(diào)用哪個(gè)方法吊说;

3)參數(shù)類型&參數(shù)值

參數(shù)類型有很多,比如有bool于样、int疏叨、long、double穿剖、string蚤蔓、map、list糊余,甚至如struct(class)秀又;

以及相應(yīng)的參數(shù)值;

4)超時(shí)時(shí)間

5)requestID贬芥,標(biāo)識(shí)唯一請(qǐng)求id吐辙,在下面一節(jié)會(huì)詳細(xì)描述requestID的用處。

同理服務(wù)端返回的消息結(jié)構(gòu)一般包括以下內(nèi)容蘸劈。

1)返回值

2)狀態(tài)code

3)requestID

1.2.2 序列化

一旦確定了消息的數(shù)據(jù)結(jié)構(gòu)后昏苏,下一步就是要考慮序列化與反序列化了。

什么是序列化威沫?序列化就是將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過(guò)程贤惯,也就是編碼的過(guò)程。

什么是反序列化棒掠?將在序列化過(guò)程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對(duì)象的過(guò)程孵构。

為什么需要序列化?轉(zhuǎn)換為二進(jìn)制串后才好進(jìn)行網(wǎng)絡(luò)傳輸嘛烟很!為什么需要反序列化颈墅?將二進(jìn)制轉(zhuǎn)換為對(duì)象才好進(jìn)行后續(xù)處理蜡镶!

現(xiàn)如今序列化的方案越來(lái)越多,每種序列化方案都有優(yōu)點(diǎn)和缺點(diǎn)恤筛,它們?cè)谠O(shè)計(jì)之初有自己獨(dú)特的應(yīng)用場(chǎng)景官还,那到底選擇哪種呢?從RPC的角度上看叹俏,主要看三點(diǎn):1)通用性妻枕,比如是否能支持Map等復(fù)雜的數(shù)據(jù)結(jié)構(gòu)僻族;2)性能粘驰,包括時(shí)間復(fù)雜度和空間復(fù)雜度,由于RPC框架將會(huì)被公司幾乎所有服務(wù)使用述么,如果序列化上能節(jié)約一點(diǎn)時(shí)間蝌数,對(duì)整個(gè)公司的收益都將非常可觀度秘,同理如果序列化上能節(jié)約一點(diǎn)內(nèi)存顶伞,網(wǎng)絡(luò)帶寬也能省下不少;3)可擴(kuò)展性剑梳,對(duì)互聯(lián)網(wǎng)公司而言唆貌,業(yè)務(wù)變化快,如果序列化協(xié)議具有良好的可擴(kuò)展性垢乙,支持自動(dòng)增加新的業(yè)務(wù)字段锨咙,刪除老的字段,而不影響老的服務(wù)追逮,這將大大提供系統(tǒng)的健壯性酪刀。

目前國(guó)內(nèi)各大互聯(lián)網(wǎng)公司廣泛使用hessian、protobuf钮孵、thrift骂倘、avro等成熟的序列化解決方案來(lái)搭建RPC框架,這些都是久經(jīng)考驗(yàn)的解決方案巴席。

1.3 通信

消息數(shù)據(jù)結(jié)構(gòu)被序列化為二進(jìn)制串后历涝,下一步就要進(jìn)行網(wǎng)絡(luò)通信了。目前有兩種IO通信模型:1)BIO漾唉;2)NIO荧库。一般RPC框架需要支持這兩種IO模型,原理可參考:《一個(gè)故事講清楚 NIO》毡证。

如何實(shí)現(xiàn)RPC的IO通信框架电爹?1)使用java nio方式自研,這種方式較為復(fù)雜料睛,而且很有可能出現(xiàn)隱藏bug丐箩,見(jiàn)過(guò)一些互聯(lián)網(wǎng)公司使用這種方式摇邦;2)基于mina,mina在早幾年比較火熱屎勘,不過(guò)這些年版本更新緩慢施籍;3)基于netty,現(xiàn)在很多RPC框架都直接基于netty這一IO通信框架概漱,比如阿里巴巴的HSF丑慎、dubbo,Twitter的finagle等瓤摧。

1.4 消息里為什么要帶有requestID竿裂?

如果使用netty的話,一般會(huì)用channel.writeAndFlush()方法來(lái)發(fā)送消息二進(jìn)制串照弥,這個(gè)方法調(diào)用后對(duì)于整個(gè)遠(yuǎn)程調(diào)用(從發(fā)出請(qǐng)求到接收到結(jié)果)來(lái)說(shuō)是一個(gè)異步的腻异,即對(duì)于當(dāng)前線程來(lái)說(shuō),將請(qǐng)求發(fā)送出來(lái)后这揣,線程就可以往后執(zhí)行了悔常,至于服務(wù)端的結(jié)果,是服務(wù)端處理完成后给赞,再以消息的形式發(fā)送給客戶端的机打。于是這里出現(xiàn)以下兩個(gè)問(wèn)題:

1)怎么讓當(dāng)前線程“暫停”片迅,等結(jié)果回來(lái)后残邀,再向后執(zhí)行?

2)如果有多個(gè)線程同時(shí)進(jìn)行遠(yuǎn)程方法調(diào)用障涯,這時(shí)建立在client server之間的socket連接上會(huì)有很多雙方發(fā)送的消息傳遞罐旗,前后順序也可能是隨機(jī)的,server處理完結(jié)果后唯蝶,將結(jié)果消息發(fā)送給client九秀,client收到很多消息,怎么知道哪個(gè)消息結(jié)果是原先哪個(gè)線程調(diào)用的粘我?

如下圖所示鼓蜒,線程A和線程B同時(shí)向client socket發(fā)送請(qǐng)求requestA和requestB,socket先后將requestB和requestA發(fā)送至server征字,而server可能將responseA先返回都弹,盡管requestA請(qǐng)求到達(dá)時(shí)間更晚。我們需要一種機(jī)制保證responseA丟給ThreadA匙姜,responseB丟給ThreadB畅厢。

image

怎么解決呢?

1)client線程每次通過(guò)socket調(diào)用一次遠(yuǎn)程接口前氮昧,生成一個(gè)唯一的ID框杜,即requestID(requestID必需保證在一個(gè)Socket連接里面是唯一的)浦楣,一般常常使用AtomicLong從0開(kāi)始累計(jì)數(shù)字生成唯一ID;

2)將處理結(jié)果的回調(diào)對(duì)象callback咪辱,存放到全局ConcurrentHashMap里面put(requestID, callback)振劳;

3)當(dāng)線程調(diào)用channel.writeAndFlush()發(fā)送消息后,緊接著執(zhí)行callback的get()方法試圖獲取遠(yuǎn)程返回的結(jié)果油狂。在get()內(nèi)部历恐,則使用synchronized獲取回調(diào)對(duì)象callback的鎖,再先檢測(cè)是否已經(jīng)獲取到結(jié)果专筷,如果沒(méi)有弱贼,然后調(diào)用callback的wait()方法,釋放callback上的鎖仁堪,讓當(dāng)前線程處于等待狀態(tài)哮洽。

4)服務(wù)端接收到請(qǐng)求并處理后填渠,將response結(jié)果(此結(jié)果中包含了前面的requestID)發(fā)送給客戶端弦聂,客戶端socket連接上專門監(jiān)聽(tīng)消息的線程收到消息,分析結(jié)果氛什,取到requestID莺葫,再?gòu)那懊娴腃oncurrentHashMap里面get(requestID),從而找到callback對(duì)象枪眉,再用synchronized獲取callback上的鎖捺檬,將方法調(diào)用結(jié)果設(shè)置到callback對(duì)象里,再調(diào)用callback.notifyAll()喚醒前面處于等待狀態(tài)的線程贸铜。

public Object get() {
        synchronized (this) {  // 旋鎖
            while (!isDone) {  // 是否有結(jié)果了
                wait(); //沒(méi)結(jié)果是釋放鎖堡纬,讓當(dāng)前線程處于等待狀態(tài)
            }
        }
    }
private void setDone(Response res) {
        this.res = res;
        isDone = true;
        synchronized (this) { //獲取鎖,因?yàn)榍懊鎤ait()已經(jīng)釋放了callback的鎖了
            notifyAll(); // 喚醒處于等待的線程
        }
    }

2 如何發(fā)布自己的服務(wù)蒿秦?

如何讓別人使用我們的服務(wù)呢烤镐?有同學(xué)說(shuō)很簡(jiǎn)單嘛,告訴使用者服務(wù)的IP以及端口就可以了啊棍鳖。確實(shí)是這樣炮叶,這里問(wèn)題的關(guān)鍵在于是自動(dòng)告知還是人肉告知。

人肉告知的方式:如果你發(fā)現(xiàn)你的服務(wù)一臺(tái)機(jī)器不夠渡处,要再添加一臺(tái)镜悉,這個(gè)時(shí)候就要告訴調(diào)用者我現(xiàn)在有兩個(gè)ip了,你們要輪詢調(diào)用來(lái)實(shí)現(xiàn)負(fù)載均衡医瘫;調(diào)用者咬咬牙改了侣肄,結(jié)果某天一臺(tái)機(jī)器掛了,調(diào)用者發(fā)現(xiàn)服務(wù)有一半不可用醇份,他又只能手動(dòng)修改代碼來(lái)刪除掛掉那臺(tái)機(jī)器的ip〖诠現(xiàn)實(shí)生產(chǎn)環(huán)境當(dāng)然不會(huì)使用人肉方式叮喳。

有沒(méi)有一種方法能實(shí)現(xiàn)自動(dòng)告知,即機(jī)器的增添缰贝、剔除對(duì)調(diào)用方透明馍悟,調(diào)用者不再需要寫(xiě)死服務(wù)提供方地址?當(dāng)然可以剩晴,現(xiàn)如今zookeeper被廣泛用于實(shí)現(xiàn)服務(wù)自動(dòng)注冊(cè)與發(fā)現(xiàn)功能锣咒!

簡(jiǎn)單來(lái)講,zookeeper可以充當(dāng)一個(gè)服務(wù)注冊(cè)表(Service Registry)赞弥,讓多個(gè)服務(wù)提供者形成一個(gè)集群毅整,讓服務(wù)消費(fèi)者通過(guò)服務(wù)注冊(cè)表獲取具體的服務(wù)訪問(wèn)地址(ip+端口)去訪問(wèn)具體的服務(wù)提供者。如下圖所示:

image

具體來(lái)說(shuō)绽左,zookeeper就是個(gè)分布式文件系統(tǒng)悼嫉,每當(dāng)一個(gè)服務(wù)提供者部署后都要將自己的服務(wù)注冊(cè)到zookeeper的某一路徑上: /{service}/{version}/{ip:port}, 比如我們的HelloWorldService部署到兩臺(tái)機(jī)器,那么zookeeper上就會(huì)創(chuàng)建兩條目錄:分別為/HelloWorldService/1.0.0/100.19.20.01:16888 /HelloWorldService/1.0.0/100.19.20.02:16888拼窥。

zookeeper提供了“心跳檢測(cè)”功能戏蔑,它會(huì)定時(shí)向各個(gè)服務(wù)提供者發(fā)送一個(gè)請(qǐng)求(實(shí)際上建立的是一個(gè) socket 長(zhǎng)連接),如果長(zhǎng)期沒(méi)有響應(yīng)鲁纠,服務(wù)中心就認(rèn)為該服務(wù)提供者已經(jīng)“掛了”总棵,并將其剔除,比如100.19.20.02這臺(tái)機(jī)器如果宕機(jī)了改含,那么zookeeper上的路徑就會(huì)只剩/HelloWorldService/1.0.0/100.19.20.01:16888情龄。

服務(wù)消費(fèi)者會(huì)去監(jiān)聽(tīng)相應(yīng)路徑(/HelloWorldService/1.0.0),一旦路徑上的數(shù)據(jù)有任務(wù)變化(增加或減少)捍壤,zookeeper都會(huì)通知服務(wù)消費(fèi)方服務(wù)提供者地址列表已經(jīng)發(fā)生改變骤视,從而進(jìn)行更新。

更為重要的是zookeeper 與生俱來(lái)的容錯(cuò)容災(zāi)能力(比如leader選舉)鹃觉,可以確保服務(wù)注冊(cè)表的高可用性专酗。

3 小結(jié)

RPC幾乎是每一個(gè)從學(xué)校進(jìn)入互聯(lián)網(wǎng)公司的同學(xué)都要首先學(xué)習(xí)的框架,之前面試過(guò)一個(gè)在大型互聯(lián)網(wǎng)公司工作過(guò)兩年的同學(xué)帜慢,對(duì)RPC還是停留在使用層面笼裳,這是不應(yīng)該的。本文也僅是對(duì)RPC的一個(gè)比較粗糙的描述粱玲,希望對(duì)大家有所幫助躬柬,錯(cuò)誤之處也請(qǐng)指出修正轧葛。

4 一些開(kāi)源的RPC框架

https://github.com/alibaba/dubbo

http://thrift.apache.org/?cm_mc_uid=87762817217214314008006&cm_mc_sid_50200000=1444181090

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末敦锌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒋得,更是在濱河造成了極大的恐慌卵沉,老刑警劉巖颠锉,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件法牲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡琼掠,警方通過(guò)查閱死者的電腦和手機(jī)拒垃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瓷蛙,“玉大人悼瓮,你說(shuō)我怎么就攤上這事〖桠” “怎么了横堡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冠桃。 經(jīng)常有香客問(wèn)我命贴,道長(zhǎng),這世上最難降的妖魔是什么食听? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任胸蛛,我火速辦了婚禮,結(jié)果婚禮上碳蛋,老公的妹妹穿的比我還像新娘胚泌。我一直安慰自己,他們只是感情好肃弟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著零蓉,像睡著了一般笤受。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敌蜂,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天箩兽,我揣著相機(jī)與錄音,去河邊找鬼章喉。 笑死汗贫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秸脱。 我是一名探鬼主播落包,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摊唇!你這毒婦竟也來(lái)了咐蝇?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤巷查,失蹤者是張志新(化名)和其女友劉穎有序,沒(méi)想到半個(gè)月后抹腿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旭寿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年警绩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盅称。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡房蝉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出微渠,到底是詐尸還是另有隱情搭幻,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布逞盆,位于F島的核電站檀蹋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏云芦。R本人自食惡果不足惜俯逾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舅逸。 院中可真熱鬧桌肴,春花似錦、人聲如沸琉历。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旗笔。三九已至彪置,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝇恶,已是汗流浹背拳魁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撮弧,地道東北人潘懊。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像贿衍,于是被迫代替她去往敵國(guó)和親授舟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理舌厨,服務(wù)發(fā)現(xiàn)岂却,斷路器,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • 摘自: https://my.oschina.net/hosee/blog/711632摘要: 本文主要說(shuō)明RPC...
    holy_z閱讀 1,121評(píng)論 0 7
  • 轉(zhuǎn)自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志閱讀 24,784評(píng)論 2 38
  • 轉(zhuǎn)載地址:http://www.cnblogs.com/LBSer/p/4853234.html 在學(xué)校期間大家都...
    冥瀧閱讀 1,814評(píng)論 0 3
  • 有多少棵樹(shù)的影子 垂在水中 有多少行人 在河岸邊來(lái)去匆匆 我穿過(guò)喧鬧的馬路 東南西北的車輛 被幾盞帶顏色的燈安排著...
    叮咚的你閱讀 101評(píng)論 0 0