乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      HSF源碼剖析

       liang1234_ 2019-01-24

      前言

      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)程過(guò)程調(diào)用——Remote Procedure Call,是一種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。在OSI網(wǎng)絡(luò)通信模型中,RPC跨越了傳輸層和應(yīng)用層。RPC使得開發(fā)分布式應(yīng)用更加容易),當(dāng)然HSF完全的內(nèi)容肯定不止這些。

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

      RPC

      我們先來(lái)看一張圖:

      hsf-up.png

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

      有這么一個(gè)場(chǎng)景(本來(lái)想舉一個(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)單的方式就是監(jiān)控平臺(tái)有一個(gè)http接口,agent每隔幾秒請(qǐng)求一次,能夠滿足需求,但是如果主機(jī)數(shù)快速增長(zhǎng)了很多、監(jiān)控項(xiàng)越來(lái)越多、請(qǐng)求體越來(lái)越大,你會(huì)發(fā)現(xiàn)http的傳輸效率下降了,每一次調(diào)用的耗時(shí)增加了。

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

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

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

      hsf-http-rpc.png

      現(xiàn)在從圖中可以看著,client和server之間有一條長(zhǎng)連接,并且我們有自己的協(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)用,所以這樣一來(lái),整個(gè)hsf的服務(wù)調(diào)用鏈路也會(huì)很復(fù)雜。

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

      hsf-configserver.png

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

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

      hsf-diamond.png

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

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

      hsf-ops.png

      相信大家都用過(guò)hsf服務(wù)治理網(wǎng)站,通過(guò)這個(gè)網(wǎng)站可以看到有哪些服務(wù)、服務(wù)提供者的地址是多少、有多少提供者、具體的消費(fèi)者是誰(shuí),hsf通過(guò)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ù)采集起來(lái)又提供給HSFOPS做服務(wù)治理,包括應(yīng)用名和服務(wù)的映射、服務(wù)的元數(shù)據(jù)等。

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

      hsf-server.png

      接下來(lái)我們把這個(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é)們對(duì)上面這段配置代碼很熟悉,那么服務(wù)到底是怎么注冊(cè)的呢,為什么這里配置了這個(gè)服務(wù)就可以被調(diào)用了呢?

      從配置文件看到有個(gè)關(guān)鍵的bean——HSFSpringProviderBean,還有個(gè)關(guān)鍵的初始化方法init,其實(shí)init的過(guò)程就是服務(wù)發(fā)布的過(guò)程,我們來(lái)看看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(),先來(lái)看大致類圖,類圖中有些不是很關(guān)鍵的先省略了:

      hsf-server-uml.png

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

      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ā)布過(guò)程中,ServiceMetadata是所有對(duì)象之間的傳輸對(duì)象;
      5. 這里先來(lái)解釋一下為什么有HSFSpringProviderBean和HSFApiProviderBean,其實(shí)兩個(gè)可以合并成一個(gè),但是為什么要分開呢?我的理解是對(duì)于不同環(huán)境的不同實(shí)現(xiàn),比如現(xiàn)在用的是spring環(huán)境,那就需要有個(gè)spring適配類HSFSpringProviderBean來(lái)獲取配置信息,假如是其他環(huán)境那么就會(huì)有另一個(gè)適配類,最終把信息統(tǒng)一轉(zhuǎn)成給HSFApiProviderBean,HSFApiProviderBean是來(lái)具體操作實(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來(lái)注冊(cè)發(fā)布RPC服務(wù),這個(gè)動(dòng)作是在server本地發(fā)布一個(gè)線程池,每一個(gè)服務(wù)都會(huì)申請(qǐng)一個(gè)線程池,當(dāng)請(qǐng)求過(guò)來(lái)時(shí)從線程池獲取executor進(jìn)行執(zhí)行并返回;
        • 第二步,檢查單元化發(fā)布,就unitService在發(fā)布前檢查是中心發(fā)布還是單元發(fā)布,對(duì)ServiceMetadata設(shè)置不同的發(fā)布路由;
        • 第三步,通過(guò)metadataService將ServiceMetadata發(fā)布到ConfigServer上;
        • 第四步,通過(guò)metadataInfoStoreService將ServiceMetadata保存到redis供服務(wù)治理或者其他用途。

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

      HSF的Client

      hsf-client.png

      現(xiàn)在來(lái)看看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)的。

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

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

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

      先來(lái)看類圖:

      hsf-client-uml.png

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

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

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

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

        • FactoryBean:是一個(gè)Java Bean,但是它是一個(gè)能生產(chǎn)對(duì)象的工廠Bean,通過(guò)getObject方法返回具體的bean,在spring bean實(shí)例化bean的過(guò)程中會(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方法返回的,那么我們?cè)賮?lái)看getObject方法:

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

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

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

          ProcessService processService = HSFServiceContainer.getInstance(ProcessService.class);
          try {
              metadata.setTarget(processService.consume(metadata));
              LOGGER.warn('成功生成對(duì)接口為['   metadata.getInterfaceName()   ']版本為['   metadata.getVersion()   ']的HSF服務(wù)調(diào)用的代理!');
          } catch (Exception e) {
              LOGGER.error('', '生成對(duì)接口為['   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)代理對(duì)象的具體生成和服務(wù)訂閱以及服務(wù)信息接收;

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

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

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

          • 沒有就通過(guò)java Proxy生成對(duì)象;

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

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

          target.png

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

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

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

      直接上類圖:

      hsf-call.png

      之前說(shuō)了動(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來(lái)確定具體的InvokeService實(shí)現(xiàn),最基本的我們知道hsf服務(wù)有同步調(diào)用和異步調(diào)用,具體實(shí)現(xiàn)就在這里;

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

      簡(jiǎn)單說(shuō)下服務(wù)端的流程:

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

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

      • 通過(guò)handler獲取具體執(zhí)行的executor(這個(gè)在之前服務(wù)注冊(cè)那邊有講,每個(gè)服務(wù)本地會(huì)申請(qǐng)線程池,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)拿出來(lái)都可以寫一篇文章,可能對(duì)于hsf的開發(fā)同學(xué)來(lái)說(shuō),每一個(gè)點(diǎn)都會(huì)有一個(gè)很好玩的故事,那么關(guān)于HSF就先講到這里。

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

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多