HSF源碼剖析

前言

HSF是一個(gè)分布式的遠(yuǎn)程服務(wù)調(diào)用框架熔恢,其實(shí)我更喜歡把分布式幾個(gè)字去掉轻姿,因?yàn)镠SF本身并不是一個(gè)單獨(dú)的服務(wù)(指一個(gè)進(jìn)程)筑累,他是附屬在你的應(yīng)用里的一個(gè)組件袱蜡,一個(gè)RPC組件(遠(yuǎn)程過程調(diào)用——Remote Procedure Call,是一種通過網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請求服務(wù)慢宗,而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議坪蚁。在OSI網(wǎng)絡(luò)通信模型中,RPC跨越了傳輸層和應(yīng)用層镜沽。RPC使得開發(fā)分布式應(yīng)用更加容易)敏晤,當(dāng)然HSF完全的內(nèi)容肯定不止這些。

說了那么久HSF全稱是什么呢缅茉?High-Speed Service Framework

RPC

我們先來看一張圖:

hsf-up.png

很多同學(xué)看了這張圖可能會(huì)覺得這跟http的過程有什么區(qū)別嘴脾?

有這么一個(gè)場景(本來想舉一個(gè)便具體業(yè)務(wù)的例子,想想還是已技術(shù)實(shí)現(xiàn)相關(guān)的比較好)蔬墩,監(jiān)控平臺(tái):監(jiān)控所有主機(jī)的狀態(tài)译打,這時(shí)候每臺(tái)主機(jī)上有一個(gè)agent,每個(gè)幾秒向監(jiān)控平臺(tái)上傳一次數(shù)據(jù)(主機(jī)內(nèi)存使用率拇颅、硬盤狀況奏司、CPU、load樟插、進(jìn)程信息等等)韵洋。

可能在開發(fā)的時(shí)候最簡單的方式就是監(jiān)控平臺(tái)有一個(gè)http接口,agent每隔幾秒請求一次岸夯,能夠滿足需求麻献,但是如果主機(jī)數(shù)快速增長了很多、監(jiān)控項(xiàng)越來越多猜扮、請求體越來越大,你會(huì)發(fā)現(xiàn)http的傳輸效率下降了监婶,每一次調(diào)用的耗時(shí)增加了旅赢。

這時(shí)我們會(huì)去研究http協(xié)議,想去優(yōu)化這個(gè)過程惑惶,發(fā)現(xiàn)http的過程是:建立連接煮盼、發(fā)送請求信息、發(fā)送響應(yīng)信息带污、關(guān)閉連接僵控,看到這個(gè)過程首先想優(yōu)化的就是能不能不要每次都去建立連接關(guān)閉連接,因?yàn)閿?shù)據(jù)上報(bào)是個(gè)持續(xù)的過程鱼冀;緊接著去研究http頭报破,發(fā)現(xiàn)很多協(xié)議用不到悠就,繁雜,白白增加了消息體充易;后來又覺得http的協(xié)議解析還原過程很復(fù)雜梗脾,可以自己開發(fā)一個(gè)提升性能......

RPC來了,他能滿足這些需求盹靴,但是前提是需要開發(fā)炸茧,需要前期成本,所以想項(xiàng)目設(shè)計(jì)時(shí)就要去衡量稿静,不過沒事梭冠,我們有HSF啊。

我們將上圖稍微改造一下:

hsf-http-rpc.png

現(xiàn)在從圖中可以看著改备,client和server之間有一條長連接妈嘹,并且我們有自己的協(xié)議體:RpcRequest和RpcResponse。

RPC就講到這里绍妨,畢竟重點(diǎn)是HSF润脸,想要更多的了解RPC,可以上wiki或者網(wǎng)上查詢他去。

HSF架構(gòu)

其實(shí)在我們的應(yīng)用中毙驯,一般情況下你的應(yīng)用不僅僅是client,也是server灾测,因?yàn)槟悴粌H需要去調(diào)用其他應(yīng)用提供的服務(wù)爆价,也提供服務(wù)給其他應(yīng)用,所以這樣一來媳搪,整個(gè)hsf的服務(wù)調(diào)用鏈路也會(huì)很復(fù)雜铭段。

從上面兩幅圖中我們很顯然的發(fā)現(xiàn)一個(gè)問題,就是服務(wù)提供者如何告知客戶端他提供的服務(wù)秦爆,所以需要有一個(gè)服務(wù)注冊與發(fā)現(xiàn)的地方序愚,在HSF架構(gòu)中提供這個(gè)功能的是configserver,如下圖:

hsf-configserver.png

從上圖可以看出server端啟動(dòng)的時(shí)候會(huì)向configserver注冊自己提供的服務(wù)等限,client會(huì)向configserver訂閱需要的服務(wù)爸吮,configserver通過訂閱信息將相關(guān)服務(wù)提供者的地址以及其他關(guān)鍵信息推送給client

上面已經(jīng)實(shí)現(xiàn)了基本的能力望门,但是如何動(dòng)態(tài)配置負(fù)載(線程池大行谓俊)、默認(rèn)配置(configserver地址等)筹误、還有一些特性功能(如路由規(guī)則)桐早,這時(shí)候就需要有一個(gè)持久化配置中心,如下圖:

hsf-diamond.png

client和server啟動(dòng)的時(shí)候會(huì)先去diamond獲取需要的配置信息,如最關(guān)鍵的服務(wù)注冊中心的類型和地址哄酝,除此之外之外還有服務(wù)治理的類型和地址等友存。

重點(diǎn)說一下路由規(guī)則,舉個(gè)例子:通過路由規(guī)則配置在服務(wù)調(diào)用的時(shí)候只調(diào)用同機(jī)房的server炫七,這樣子服務(wù)調(diào)用的耗時(shí)肯定比跨機(jī)房的耗時(shí)短爬立。除此之外hsf里還單獨(dú)寫了unitService進(jìn)行服務(wù)單元發(fā)布來區(qū)分中心發(fā)布,這些番外的東西以后有時(shí)間再寫個(gè)番外篇万哪,這里就不過多闡述了侠驯,畢竟這些有點(diǎn)偏場景偏業(yè)務(wù)的內(nèi)容以后可能就改成別的方式了。

hsf-ops.png

相信大家都用過hsf服務(wù)治理網(wǎng)站奕巍,通過這個(gè)網(wǎng)站可以看到有哪些服務(wù)吟策、服務(wù)提供者的地址是多少、有多少提供者的止、具體的消費(fèi)者是誰檩坚,hsf通過configserver、redis诅福、diamond里的存儲(chǔ)信息獲取到這些信息匾委。

redis功能:HSF使用Redis存儲(chǔ)元數(shù)據(jù),每一個(gè)HSF Consumer/Provider 都會(huì)在啟動(dòng)后氓润、每隔一段時(shí)間向redis上報(bào)元數(shù)據(jù)赂乐,這些元數(shù)據(jù)采集起來又提供給HSFOPS做服務(wù)治理,包括應(yīng)用名和服務(wù)的映射咖气、服務(wù)的元數(shù)據(jù)等挨措。

服務(wù)的注冊與發(fā)布

hsf-server.png

接下來我們把這個(gè)server解開,看看里面是怎么樣的崩溪。

<bean id="hsfTestService"
        class="com.test.service.impl.HsfTestServiceImpl" />
    <bean class="com.taobao.hsf.app.spring.util.HSFSpringProviderBean"
        init-method="init">
        <property name="serviceName" value="hsfTestService" />
        <property name="target" ref="hsfTestService" />
        <property name="serviceInterface">
            <value>com.test.service.HsfTestService
            </value>
        </property>
        <property name="serviceVersion">
            <value>${hsf.common.provider.version}</value>
        </property>
    </bean>

相信同學(xué)們對上面這段配置代碼很熟悉浅役,那么服務(wù)到底是怎么注冊的呢,為什么這里配置了這個(gè)服務(wù)就可以被調(diào)用了呢伶唯?

從配置文件看到有個(gè)關(guān)鍵的bean——HSFSpringProviderBean觉既,還有個(gè)關(guān)鍵的初始化方法init,其實(shí)init的過程就是服務(wù)發(fā)布的過程抵怎,我們來看看HSFSpringProviderBean中的部分代碼:

public void init() throws Exception {
        // 避免被初始化多次
        if (!providerBean.getInited().compareAndSet(false, true)) {
            return;
        }
        LoggerInit.initHSFLog();

        SpasInit.initSpas();
        providerBean.checkConfig();
        publishIfNotInSpringContainer();
    }

    private void publishIfNotInSpringContainer() {
        if (!isInSpringContainer) {
            LOGGER.warn("[SpringProviderBean]不是在Spring容器中創(chuàng)建, 不推薦使用");
            providerBean.publish();
        }
    }

從代碼中很明顯的看到服務(wù)發(fā)布providerBean.publish()奋救,先來看大致類圖,類圖中有些不是很關(guān)鍵的先省略了:

hsf-server-uml.png

大致對類圖進(jìn)行解釋一下反惕,這也是服務(wù)發(fā)布的一個(gè)過程:

  1. 服務(wù)初始化,首先需要有一個(gè)提供服務(wù)的service實(shí)現(xiàn)類(spring bean)和接口演侯;
  2. 初始化HSFSpringProviderBean姿染,從配置文件獲取服務(wù)名稱、接口、實(shí)現(xiàn)類悬赏、版本等等狡汉;
  3. providerBean是HSFApiProviderBean在HSFSpringProviderBean中的變量,HSFSpringProviderBean會(huì)將從配置文件獲取的服務(wù)名稱闽颇、接口盾戴、實(shí)現(xiàn)類、版本等等賦值給providerBean兵多;
  4. providerBean中有個(gè)服務(wù)實(shí)體類ServiceMetadata尖啡,providerBean會(huì)將服務(wù)發(fā)布的所有信息放在這里,如接口剩膘、實(shí)現(xiàn)類衅斩、版本等等,在整個(gè)發(fā)布過程中怠褐,ServiceMetadata是所有對象之間的傳輸對象畏梆;
  5. 這里先來解釋一下為什么有HSFSpringProviderBean和HSFApiProviderBean,其實(shí)兩個(gè)可以合并成一個(gè)奈懒,但是為什么要分開呢奠涌?我的理解是對于不同環(huán)境的不同實(shí)現(xiàn),比如現(xiàn)在用的是spring環(huán)境磷杏,那就需要有個(gè)spring適配類HSFSpringProviderBean來獲取配置信息溜畅,假如是其他環(huán)境那么就會(huì)有另一個(gè)適配類,最終把信息統(tǒng)一轉(zhuǎn)成給HSFApiProviderBean茴丰,HSFApiProviderBean是來具體操作實(shí)現(xiàn)达皿;
  6. 當(dāng)執(zhí)行providerBean.publish()時(shí),會(huì)調(diào)用ProcessService的publish方法贿肩,具體實(shí)現(xiàn)類是ProcessComponent峦椰;
  7. 發(fā)布的具體流程就是ProcessComponent里:
    • 第一步,調(diào)用rpcProtocolService來注冊發(fā)布RPC服務(wù)汰规,這個(gè)動(dòng)作是在server本地發(fā)布一個(gè)線程池汤功,每一個(gè)服務(wù)都會(huì)申請一個(gè)線程池,當(dāng)請求過來時(shí)從線程池獲取executor進(jìn)行執(zhí)行并返回溜哮;
    • 第二步滔金,檢查單元化發(fā)布,就unitService在發(fā)布前檢查是中心發(fā)布還是單元發(fā)布茂嗓,對ServiceMetadata設(shè)置不同的發(fā)布路由餐茵;
    • 第三步,通過metadataService將ServiceMetadata發(fā)布到ConfigServer上述吸;
    • 第四步忿族,通過metadataInfoStoreService將ServiceMetadata保存到redis供服務(wù)治理或者其他用途。

服務(wù)注冊發(fā)布大致就是這么一個(gè)過程。

HSF的Client

hsf-client.png

現(xiàn)在來看看client是如何去調(diào)用服務(wù)的道批。

<bean id="hsfTestService" class="com.taobao.hsf.app.spring.util.HSFSpringConsumerBean" init-method="init">
    <property name="interfaceName" value="com.test.service.hsfTestService"/>
    <property name="version" value="1.0.0.daily"/>
</bean>

上面一段配置文件相信在項(xiàng)目中肯定也非常常見错英,那么他是怎么運(yùn)作的呢?在spring注入的時(shí)候并沒有具體的實(shí)現(xiàn)類啊隆豹,只有一個(gè)接口椭岩?怎么實(shí)現(xiàn)調(diào)用的呢?

其實(shí)這是我一個(gè)好奇心的地方璃赡,我想去看個(gè)究竟判哥,hsf到底是用何種方式去實(shí)現(xiàn)的。

我們先來思考一個(gè)問題鉴吹,那就是沒有具體實(shí)現(xiàn)類姨伟,hsf是如何實(shí)現(xiàn)在spring中注冊服務(wù)的呢?答案就是動(dòng)態(tài)代理豆励,類似mybatis的方式夺荒,mybatis在寫dao層的時(shí)候只是寫了個(gè)接口,并沒有具體實(shí)現(xiàn)良蒸,hsf跟這種方式很相像技扼。

客戶端分兩部分來講解:服務(wù)的訂閱和被推送,服務(wù)的調(diào)用嫩痰。

服務(wù)的訂閱和被推送

先來看類圖:

hsf-client-uml.png

一樣我們通過類圖來看服務(wù)的訂閱和接收過程:

  1. 服務(wù)初始化剿吻,首先需要引入服務(wù)接口相關(guān)的pom,然后寫配置文件串纺;

  2. 將需要被調(diào)用的服務(wù)注冊成spring bean丽旅,即上面配置文件中的內(nèi)容。

    • 這里用到了動(dòng)態(tài)代理纺棺,通過類圖我們可以看到HSFSpringConsumerBean實(shí)現(xiàn)了FactoryBean榄笙;

    • FactoryBean:是一個(gè)Java Bean,但是它是一個(gè)能生產(chǎn)對象的工廠Bean祷蝌,通過getObject方法返回具體的bean茅撞,在spring bean實(shí)例化bean的過程中會(huì)去判斷是不是FactoryBean,如果不是就返回bean巨朦,否則返回FactoryBean生產(chǎn)的bean米丘,具體同學(xué)們可以去看AbstractBeanFactory的doGetBean方法,里面會(huì)調(diào)用getObjectForBeanInstance方法糊啡,這個(gè)方法里有具體實(shí)現(xiàn)拄查;

    • HSFSpringConsumerBean實(shí)現(xiàn)了FactoryBean,那么getObject方法具體返回了什么呢棚蓄?怎么返回的呢靶累?

      @Override
      public Object getObject() throws Exception {
          return consumerBean.getObject();
      }
      

      從代碼看得出是調(diào)用了consumerBean(HSFApiConsumerBean)的getObject方法返回的腺毫,那么我們再來看getObject方法:

      public Object getObject() throws Exception {
          return metadata.getTarget();
      }
      

      這個(gè)方法返回的是metadata(ServiceMetadata)的target癣疟,那么target是怎么獲取的呢挣柬?下面重點(diǎn)說明;

    • HSFSpringConsumerBean的init方法調(diào)用了consumerBean(HSFApiConsumerBean)的init方法睛挚,我們來看consumerBean里init方法的某一段代碼:

      ProcessService processService = HSFServiceContainer.getInstance(ProcessService.class);
      try {
          metadata.setTarget(processService.consume(metadata));
          LOGGER.warn("成功生成對接口為[" + metadata.getInterfaceName() + "]版本為[" + metadata.getVersion() + "]的HSF服務(wù)調(diào)用的代理邪蛔!");
      } catch (Exception e) {
          LOGGER.error("", "生成對接口為[" + metadata.getInterfaceName() + "]版本為[" + metadata.getVersion()
                  + "]的HSF服務(wù)調(diào)用的代理失敗", e);
          // since 2007,一旦初始化異常就拋出
          throw e;
      }
      int waitTime = metadata.getMaxWaitTimeForCsAddress();
      if (waitTime > 0) {
          try {
              metadata.getCsAddressCountDownLatch().await(waitTime, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
              // ignore
          }
      }
      

      這一段代碼包含了動(dòng)態(tài)代理對象的具體生成和服務(wù)訂閱以及服務(wù)信息接收扎狱;

    • 先說了一下代碼邏輯侧到,服務(wù)的訂閱和服務(wù)信息的接收(被推送)在processService中執(zhí)行,動(dòng)態(tài)代理對象在processService中生成淤击,下面的wait我推測是用來等目標(biāo)服務(wù)信息的推送(當(dāng)收到訂閱的目標(biāo)具體服務(wù)實(shí)現(xiàn)匠抗,接下來的調(diào)用過程才能走通);

    • 看來processService是一個(gè)很重要的組件污抬,這邊通過processService.consume(metadata)這樣的方法調(diào)用實(shí)現(xiàn)了那么多步驟汞贸,target也在這里面生成,說一下這個(gè)方法內(nèi)的邏輯:

      • 首先去緩存中找是否之前target有生成印机,有就返回矢腻;

      • 沒有就通過java Proxy生成對象;

      • 訂閱服務(wù)信息(返回的可調(diào)用地址)射赛;

      • 保存客戶端metadata到redis多柑,返回target。

      target.png

到此為止楣责,服務(wù)代理對象的生成竣灌,服務(wù)的訂閱都完成了,接下來看看服務(wù)的調(diào)用秆麸。

服務(wù)的調(diào)用

其實(shí)通過上面兩個(gè)部分整個(gè)框架已經(jīng)定好了初嘹,服務(wù)信息已經(jīng)注冊發(fā)布,客戶端也獲取到了服務(wù)的調(diào)用地址蛔屹,接下去就是調(diào)用就行削樊,調(diào)用呢就是真正的rpc請求了,hsf的rpc是通過netty實(shí)現(xiàn)的兔毒。

直接上類圖:

hsf-call.png

之前說了動(dòng)態(tài)代理漫贞,那么在方法執(zhí)行時(shí)就行進(jìn)入代理類執(zhí)行,執(zhí)行HSFServiceProxy的invoke方法育叁,invoke方法會(huì)調(diào)用trueInvoke方法:

  • 在trueInvoke里調(diào)用RPCProtocolTemplateService迅脐,在這里封裝HSFRequest,執(zhí)行具體的invoke方法豪嗽;

  • 具體的invoke方法調(diào)用RPCProtocolService谴蔑,在這里主要是根據(jù)invokeType來確定具體的InvokeService實(shí)現(xiàn)豌骏,最基本的我們知道hsf服務(wù)有同步調(diào)用和異步調(diào)用,具體實(shí)現(xiàn)就在這里隐锭;

  • 最后在具體的實(shí)現(xiàn)類的獲取NettyClient窃躲,跟server進(jìn)行通信,返回HSFResponse钦睡。

簡單說下服務(wù)端的流程:

  • 服務(wù)端會(huì)啟動(dòng)nettyServer蒂窒,具體由NettyServerHandler來處理所有rpc請求;

  • NettyServerHandler會(huì)根據(jù)HSFRequest找到具體的handler荞怒,這邊是RPCServerHandler洒琢,除此之外還有心跳啊等等handler;

  • 通過handler獲取具體執(zhí)行的executor(這個(gè)在之前服務(wù)注冊那邊有講褐桌,每個(gè)服務(wù)本地會(huì)申請線程池衰抑,threadpoolexecutor);

  • new一個(gè)HandlerRunnable放進(jìn)executor執(zhí)行executor.execute(new HandlerRunnable);

  • 最終在handler里調(diào)用ProviderProcessor,ProviderProcessor會(huì)找到具體的服務(wù)實(shí)現(xiàn)類并執(zhí)行荧嵌,將執(zhí)行結(jié)果封裝成HSFResponse呛踊,向client返回HSFResponse。

total.png

寫在最后

我在這里講得更多的是主鏈路完丽,里面有很多具體的細(xì)節(jié)比如路由恋技、鷹眼追蹤、日志逻族、負(fù)載等等沒有展開講蜻底,其實(shí)每個(gè)點(diǎn)拿出來都可以寫一篇文章,可能對于hsf的開發(fā)同學(xué)來說聘鳞,每一個(gè)點(diǎn)都會(huì)有一個(gè)很好玩的故事薄辅,那么關(guān)于HSF就先講到這里。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抠璃,一起剝皮案震驚了整個(gè)濱河市站楚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搏嗡,老刑警劉巖窿春,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異采盒,居然都是意外死亡旧乞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門磅氨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尺栖,“玉大人,你說我怎么就攤上這事烦租⊙佣模” “怎么了除盏?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挫以。 經(jīng)常有香客問我者蠕,道長,這世上最難降的妖魔是什么屡贺? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任蠢棱,我火速辦了婚禮,結(jié)果婚禮上甩栈,老公的妹妹穿的比我還像新娘。我一直安慰自己糕再,他們只是感情好量没,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著突想,像睡著了一般殴蹄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猾担,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天袭灯,我揣著相機(jī)與錄音,去河邊找鬼绑嘹。 笑死稽荧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的工腋。 我是一名探鬼主播姨丈,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼擅腰!你這毒婦竟也來了蟋恬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤趁冈,失蹤者是張志新(化名)和其女友劉穎歼争,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渗勘,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沐绒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呀邢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洒沦。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖价淌,靈堂內(nèi)的尸體忽然破棺而出申眼,到底是詐尸還是另有隱情瞒津,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布括尸,位于F島的核電站巷蚪,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏濒翻。R本人自食惡果不足惜屁柏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望有送。 院中可真熱鬧淌喻,春花似錦、人聲如沸雀摘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阵赠。三九已至涯塔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間清蚀,已是汗流浹背匕荸。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枷邪,地道東北人榛搔。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像齿风,于是被迫代替她去往敵國和親药薯。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理救斑,服務(wù)發(fā)現(xiàn)童本,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • JAVA面試題 1脸候、作用域public,private,protected,以及不寫時(shí)的區(qū)別答:區(qū)別如下:作用域 ...
    JA尐白閱讀 1,153評(píng)論 1 0
  • 一縷清香東自西穷娱,遙看飛雪壓枝低。 梨花早入詩心去运沦,五月梢頭誰作題泵额?
    夢之旅_926e閱讀 475評(píng)論 0 7
  • 一段感情的結(jié)束不管是哪一方先開始的放棄,都不能把錯(cuò)誤都?xì)w結(jié)在一個(gè)人身上携添〖廾ぃ或許剛開始的時(shí)候都有想過要好好在一起吧。我...
    酷啦啦閱讀 478評(píng)論 0 0
  • 冬日的暖陽烈掠,夏日的清風(fēng)羞秤,秋天的夕陽缸托,都不如你春天的顰顰一笑。 故事的開頭總是這樣瘾蛋,適逢其會(huì)俐镐,猝不及防。故事的結(jié)局總...
    你我的劇本閱讀 121評(píng)論 0 0