日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

HSF源碼剖析

 liang1234_ 2019-01-24

前言

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

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

RPC

我們先來看一張圖:

hsf-up.png

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

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

可能在開發(fā)的時候最簡單的方式就是監(jiān)控平臺有一個http接口,agent每隔幾秒請求一次,能夠滿足需求,但是如果主機數(shù)快速增長了很多、監(jiān)控項越來越多、請求體越來越大,你會發(fā)現(xiàn)http的傳輸效率下降了,每一次調(diào)用的耗時增加了。

這時我們會去研究http協(xié)議,想去優(yōu)化這個過程,發(fā)現(xiàn)http的過程是:建立連接、發(fā)送請求信息、發(fā)送響應信息、關(guān)閉連接,看到這個過程首先想優(yōu)化的就是能不能不要每次都去建立連接關(guān)閉連接,因為數(shù)據(jù)上報是個持續(xù)的過程;緊接著去研究http頭,發(fā)現(xiàn)很多協(xié)議用不到,繁雜,白白增加了消息體;后來又覺得http的協(xié)議解析還原過程很復雜,可以自己開發(fā)一個提升性能......

RPC來了,他能滿足這些需求,但是前提是需要開發(fā),需要前期成本,所以想項目設(shè)計時就要去衡量,不過沒事,我們有HSF啊。

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

hsf-http-rpc.png

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

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

HSF架構(gòu)

其實在我們的應用中,一般情況下你的應用不僅僅是client,也是server,因為你不僅需要去調(diào)用其他應用提供的服務,也提供服務給其他應用,所以這樣一來,整個hsf的服務調(diào)用鏈路也會很復雜。

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

hsf-configserver.png

從上圖可以看出server端啟動的時候會向configserver注冊自己提供的服務,client會向configserver訂閱需要的服務,configserver通過訂閱信息將相關(guān)服務提供者的地址以及其他關(guān)鍵信息推送給client。

上面已經(jīng)實現(xiàn)了基本的能力,但是如何動態(tài)配置負載(線程池大?。?、默認配置(configserver地址等)、還有一些特性功能(如路由規(guī)則),這時候就需要有一個持久化配置中心,如下圖:

hsf-diamond.png

client和server啟動的時候會先去diamond獲取需要的配置信息,如最關(guān)鍵的服務注冊中心的類型和地址,除此之外之外還有服務治理的類型和地址等。

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

hsf-ops.png

相信大家都用過hsf服務治理網(wǎng)站,通過這個網(wǎng)站可以看到有哪些服務、服務提供者的地址是多少、有多少提供者、具體的消費者是誰,hsf通過configserver、redis、diamond里的存儲信息獲取到這些信息。

redis功能:HSF使用Redis存儲元數(shù)據(jù),每一個HSF Consumer/Provider 都會在啟動后、每隔一段時間向redis上報元數(shù)據(jù),這些元數(shù)據(jù)采集起來又提供給HSFOPS做服務治理,包括應用名和服務的映射、服務的元數(shù)據(jù)等。

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

hsf-server.png

接下來我們把這個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>

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

從配置文件看到有個關(guān)鍵的bean——HSFSpringProviderBean,還有個關(guān)鍵的初始化方法init,其實init的過程就是服務發(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();
        }
    }

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

hsf-server-uml.png

大致對類圖進行解釋一下,這也是服務發(fā)布的一個過程:

  1. 服務初始化,首先需要有一個提供服務的service實現(xiàn)類(spring bean)和接口;
  2. 初始化HSFSpringProviderBean,從配置文件獲取服務名稱、接口、實現(xiàn)類、版本等等;
  3. providerBean是HSFApiProviderBean在HSFSpringProviderBean中的變量,HSFSpringProviderBean會將從配置文件獲取的服務名稱、接口、實現(xiàn)類、版本等等賦值給providerBean;
  4. providerBean中有個服務實體類ServiceMetadata,providerBean會將服務發(fā)布的所有信息放在這里,如接口、實現(xiàn)類、版本等等,在整個發(fā)布過程中,ServiceMetadata是所有對象之間的傳輸對象;
  5. 這里先來解釋一下為什么有HSFSpringProviderBean和HSFApiProviderBean,其實兩個可以合并成一個,但是為什么要分開呢?我的理解是對于不同環(huán)境的不同實現(xiàn),比如現(xiàn)在用的是spring環(huán)境,那就需要有個spring適配類HSFSpringProviderBean來獲取配置信息,假如是其他環(huán)境那么就會有另一個適配類,最終把信息統(tǒng)一轉(zhuǎn)成給HSFApiProviderBean,HSFApiProviderBean是來具體操作實現(xiàn);
  6. 當執(zhí)行providerBean.publish()時,會調(diào)用ProcessService的publish方法,具體實現(xiàn)類是ProcessComponent;
  7. 發(fā)布的具體流程就是ProcessComponent里:
    • 第一步,調(diào)用rpcProtocolService來注冊發(fā)布RPC服務,這個動作是在server本地發(fā)布一個線程池,每一個服務都會申請一個線程池,當請求過來時從線程池獲取executor進行執(zhí)行并返回;
    • 第二步,檢查單元化發(fā)布,就unitService在發(fā)布前檢查是中心發(fā)布還是單元發(fā)布,對ServiceMetadata設(shè)置不同的發(fā)布路由;
    • 第三步,通過metadataService將ServiceMetadata發(fā)布到ConfigServer上;
    • 第四步,通過metadataInfoStoreService將ServiceMetadata保存到redis供服務治理或者其他用途。

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

HSF的Client

hsf-client.png

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

<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>

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

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

我們先來思考一個問題,那就是沒有具體實現(xiàn)類,hsf是如何實現(xiàn)在spring中注冊服務的呢?答案就是動態(tài)代理,類似mybatis的方式,mybatis在寫dao層的時候只是寫了個接口,并沒有具體實現(xiàn),hsf跟這種方式很相像。

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

服務的訂閱和被推送

先來看類圖:

hsf-client-uml.png

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

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

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

    • 這里用到了動態(tài)代理,通過類圖我們可以看到HSFSpringConsumerBean實現(xiàn)了FactoryBean;

    • FactoryBean:是一個Java Bean,但是它是一個能生產(chǎn)對象的工廠Bean,通過getObject方法返回具體的bean,在spring bean實例化bean的過程中會去判斷是不是FactoryBean,如果不是就返回bean,否則返回FactoryBean生產(chǎn)的bean,具體同學們可以去看AbstractBeanFactory的doGetBean方法,里面會調(diào)用getObjectForBeanInstance方法,這個方法里有具體實現(xiàn);

    • HSFSpringConsumerBean實現(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(); }

      這個方法返回的是metadata(ServiceMetadata)的target,那么target是怎么獲取的呢?下面重點說明;

    • 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服務調(diào)用的代理!');
      } catch (Exception e) {
          LOGGER.error('', '生成對接口為['   metadata.getInterfaceName()   ']版本為['   metadata.getVersion()
                    ']的HSF服務調(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
          }
      }
      

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

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

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

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

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

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

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

      target.png

到此為止,服務代理對象的生成,服務的訂閱都完成了,接下來看看服務的調(diào)用。

服務的調(diào)用

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

直接上類圖:

hsf-call.png

之前說了動態(tài)代理,那么在方法執(zhí)行時就行進入代理類執(zhí)行,執(zhí)行HSFServiceProxy的invoke方法,invoke方法會調(diào)用trueInvoke方法:

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

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

  • 最后在具體的實現(xiàn)類的獲取NettyClient,跟server進行通信,返回HSFResponse。

簡單說下服務端的流程:

  • 服務端會啟動nettyServer,具體由NettyServerHandler來處理所有rpc請求;

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

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

  • new一個HandlerRunnable放進executor執(zhí)行executor.execute(new HandlerRunnable);

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

total.png

寫在最后

我在這里講得更多的是主鏈路,里面有很多具體的細節(jié)比如路由、鷹眼追蹤、日志、負載等等沒有展開講,其實每個點拿出來都可以寫一篇文章,可能對于hsf的開發(fā)同學來說,每一個點都會有一個很好玩的故事,那么關(guān)于HSF就先講到這里。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多