(五):C++分布式實時應用框架——微服務架構的演進


C++分布式實時應用框架——微服務架構的演進

 技術交流合作QQ群:436466587 歡迎討論交流

上一篇:(四):C++分布式實時應用框架——狀態中心模塊

 

版權聲明:本文版權及所用技術歸屬smartguys團隊所有,對於抄襲,非經同意轉載等行為保留法律追究的權利!

 

  OCS(online charging system,在線計費系統)在進行雲化改造的過程中,從實用主義角度出發,微服務架構並不是我們的目標。雖然我們也對系統進行了容器化改造(Docker),並根據業務進程的功能將系統分成了好幾類的容器,但這一切多是出於對系統中的某些處理節點進行動態擴縮容的需要,跟微服務半點關系沒有。隨着系統改造 的深入,系統的通訊關系復雜程度開始超過我們之前的估計。如果說數量眾多的功能節點還有人可以勉強掌握,這些節點間錯綜復雜的通訊關系連線已超過程序員可以駕馭的范疇。在討論如何簡化程序員實現整個系統各類節點的通訊關系的配置過程中,節點微服務化的理念漸漸進入我們的腦海之中……

  下面先給大家介紹下我們所面臨的困境,下面的圖是我們系統一部分節點的通訊關系總圖(注意,只是其中一部分):

 

  還記得第二篇《基於ZeroMQ的實時通訊平台》中那個我們引以為傲的通訊配置文件嗎,就是程序中所有的通訊連接關系不再是寫死在代碼中,而是通過AppInit.json配置文件進行配置,程序啟動的時候再由CDRAF進行實時加載。當初酷炫的功能,現在卻成我們的惡夢。此時AppInit.json這個文件已到達1700多行,你沒看錯,一個配置文件1700多行,並且還不是全部,還會繼續變大。

 

"OLC" : {
      "AUTO_START" : "YES",
      "ENDPOINTS" : [
         {  // 用於與SmartMonitor建立心跳
            "name" : "MonitorSUB",   
            "zmq_socket_action" : "CONNECT",  // ZMQ的連接模式
            "zmq_socket_type" : "ZMQ_SUB"     // ZMQ的通訊模式
         },
         { // 下發消息給OCDis,這邊存在轉發功能,支持業務實現按條件轉發
            "downstream" : [ "OCDis2OLC"],
            "name" : "NE2OLC",                // 根據這個名字在業務代碼中實現轉發
            "zmq_socket_action" : "BIND",
            "zmq_socket_type" : "ZMQ_STREAM" 
         },
         { // OLC到OCDis的鏈路
            "name" : "OCDis2OLC",
            "statistics_on" : true,
            "zmq_socket_action" : "CONNECT",
            "zmq_socket_type" : "ZMQ_DEALER"
         },
         { // OCDis回OLC的鏈路,之所以來去分開,主要用於實現優雅啟停功能(啟停節點保證不丟消息)
            "name" : "OCDis2OLC_Backway",
            "statistics_on" : true,
            "zmq_socket_action" : "CONNECT",
            "zmq_socket_type" : "ZMQ_DEALER",
            "backway_pair" : "OCDis2OLC"
         },
         {  // 用於與SmartMonitor的命令消息鏈路
            "name" : "OLC2Monitor",
            "zmq_socket_action" : "CONNECT",
            "zmq_socket_type" : "ZMQ_DEALER"
         },
      ],
      "ENDPOINT_TO_MONITOR" : "OLC2Monitor",
      "INSTANCE_GROUP" : [
         {
            "instance_endpoints_address" : [
               {
                  "endpoint_name" : "NE2OLC",
                  "zmq_socket_address" : "tcp://*:6701"
               },
               {
                  "endpoint_name" : "OCDis2OLC",
                  "zmq_socket_address" : [
                     "tcp://127.0.0.1:7201"   // 跨機的IP地址與端口,配合狀態中心可實現自動管理,無需人工參與配置
                  ]
               },
               {
                  "endpoint_name" : "OCDis2OLC_Backway",
                  "zmq_socket_address" : [
                     "tcp://127.0.0.1:7202"
                  ]
               },
               {
                  "endpoint_name" : "OLC2Monitor",
                  "zmq_socket_address" : "ipc://Monitor2Business_IPC"
               },
               {
                  "endpoint_name" : "MonitorSUB",
                  "zmq_socket_address" : "ipc://MonitorPUB"
               }
            ],
            "instance_group_name" : "1"
         }
      ]
   },

 

  一個業務程序員如果要調整系統中某個程序的通訊連接,一定得盯着上面那副圖研究半天,並且要搞明白“CONNECT”、“BIND"、”ZMQ_ROUTER"、“ZMQ_DEALER"等等這些zeromq專業詞匯的含義,才可能進行准確配置,我們隱隱感到這已是一個mission impossible。如何簡化這個配置文件,如何對系統的復雜度進行分層,讓不同層級的人員僅僅只需關注自身層級情況,再通過我們的CDRAF最終將這些散落的配置、代碼組成一個完成可運行的系統才是我們現在亟需解決的問題。相信這也是每個系統架構師所面臨的問題,當一個系統的復雜度超過單個人可承受能力范圍,就要對這個系統進行適當分層,分模塊。讓每個人去管理一小部分復雜點,並且大家只需實現好自己的模塊,無需去關心別的模塊的實現細節。通過事先設計好的接口,各個模塊可以相互協作,整體系統是可以依此完美地運行的。這里CDARF正是起這么一個不同模塊的橋梁(接口)的作用。

  一、節點間通訊模式的統一

  原來節點內的應用程序都是通訊全能應用程序,所謂全能是指應用程序既可以跟節點內的進程進行通訊也可以跟節點外的任意進程進行通訊。這樣乍看起來沒啥問題,但一旦節點數和進程數變多后,通訊關系將是一個指數級增長的過程。如下圖,如果再增加一個CDR節點,或者OCS節點,連接數都將增加非常多。

  

  我們的解決辦法是統一節點的通訊模式,每個節點內都有一個Dis進程,統一對外負責跟其他節點進行通訊。在收到外部發給節點的消息后,根據功能和負載轉發給內部業務處理進程。業務進程如果有消息需要發往別的節點,就直接發給Dis進程,由它進行轉發。統一通訊模式帶來的好處除了在節點和進程增多后,通訊關系不會變得太復雜以外。由於模式統一, CDARF可以替業務程序員完成很多工作,直接的好處就是業務程序員不再需要配置很多與業務無關的配置。最大化的將通訊模塊的復雜度留給CDRAF去處理,業務程序員將更加專注於自身的業務邏輯。下面的圖中其實系統開始已經有微服務的樣子,但我們希望做到的不僅是從系統架構上是微服務架構,在程序員開發程序的時候,也應該是帶着微服務思維的,我們的CDRAF應該提供這么一種能力來支持這種開發模式。

  

 

  二、配置文件的簡化

  通訊模式統一后,我們對通訊配置文件進行了一次較大的簡化,從原來1700行減少到了200行左右。這當中省去了很多冗余的配置項,通訊配置文件不再是對系統通訊簡單直接的對應,而更多的是對節點通訊能力的一種表述。

  應用程序分為Dis和非Dis兩類,Dis類程序主要承擔節點間的通訊和節點內的消息轉發,非Dis類程序就是普通的業務處理進程。從下面的文件中可以看到“OCDis”進程中分為“InterContainerEndpoints”和“InnerContainerEndpoints”兩大類,分別表示節點間的通訊和節點內的通訊。對於節點間的通訊,每個服務端口只要寫上相應的“服務名字”就可以以了,配置中的“OCDisCDRDis”表示OCSDis與CDRDis的通訊,“OLCDisOLCProxy”、“OCDis_SyDis_SNR”也是類似。當業務側程序需要對外提供一個服務(或者說與外部進行通訊),只需要寫一個服務名字,而如:端口、機器的IP地址、服務端還是客戶端、通訊模式等等都完全不需要去關心,這是多大一種便利。配置中的注釋部分是不需要業務程序員去填的,而是由CDRAF的狀態中心,根據集群節點的實時情況自動生成,並進行連接和維護。

  

{
  "OCDis": {
    "MaxInstanceGroupNum": 3,
    "InterContainerEndpoints": 
    {
      "OCDisCDRDis": 
      {
        //"Port": [6001, 6002, 6003],
        //"Cluster": ["10.45.4.10:6001", "10.45.4.10:6001"]
      },

      "OCDisOLCProxy": 
      {
        //"Port": [6101, 6102, 6103],
        "DownStreams": ["OCDis2IN", "OCDis2PS", "OCDis2SMS", "OCDis2ISMP", "OCDis2IMS"],
        "router": true
      },
      "OCDis_SyDis_SNR": 
      { 
          //"Peer": "ZSmartSyDis.OCDis_SyDis_SNR" 
      }
    },

    "InnerContainerEndpoints": 
    {
      "OCPro_OCDis_CDR": { "DownStreams": ["OCDisCDRDis"] },
      "OCPro_OCDis_SNR": { "DownStreams": ["OCDis_SyDis_SNR"] },
    }
  },

  "OCPro": {
    "Groups": ["IN", "PS", "SMS", "IMS", "ISMP"],
    "InnerContainerEndpoints": {
      "OCPro2OCDis": {
        "PeerMap": [
          "OCDis.OCDis2IN",
          "OCDis.OCDis2PS",
          "OCDis.OCDis2SMS",
          "OCDis.OCDis2ISMP",
          "OCDis.OCDis2IMS"
        ]
      },
      "OCPro_OCDis_SNR": {"Peer": "OCDis.OCPro_OCDis_SNR"},
      "OCPro_OCDis_CDR": {"Peer": "OCDis"}
    }
  },

  "CDRDis": {
    "InterContainerEndpoints": 
    {
      "OCDisCDRDis" : 
      {
        "DownStreams": ["CDRDisCDR"],
        //"Peer": "OCDis"
      }
    }
  },

  "CDR": {
    "InnerContainerEndpoints": 
    {
      "CDRDisCDR" : {"Peer": "CDRDis"}
    }
  }
}

  想像一下,對於每一個業務節點,開發人員僅需考慮節點內的業務實現邏輯,並為本節點對外所提供的服務起個名字,而不再需要關心這個服務到底是提供給誰,更不用操心誰會來連我的進程,怎么連。這是多么精妙的事情!我們不僅是從架構上做到了微服務架構,程序員在開發業務程序的時候,不需要去關心除了自身模塊以外的其它復雜信息,從此可以輕裝上陣,而不再需要負重前行。這應該就是CDRAF對微服務架構提供的最直接、最好的支持了,幫助業務程序員從傳統的開發模式轉變,進而適應微服務的思維方式。

 

  三、節點間的通訊關系配置

  上面我們提到配置文件只定義了節點的服務名,那么這么多的微服務節點是如何組合起來工作的?一個業務應用系統會由許多的微服務一起協同提供服務,這些服務對於每個不同的現場可能功能是不一樣的,或者說微服務集合是不一樣的。那么,對這些微服務的組合的過程就像一個“編排”的過程。通過“編排”,選擇合適的微服務進行搭配組合提供服務,而編排的過程就是我們通訊建立的過程。下面我們就來看一下CDRAF是如何做到“編排”功能的。

  

  上面的第一張表,描述了所有的微服務列表,所有節點服務要向外通訊都必須到這張表中增加相應的服務名,這里的服務名是與前面配置文件中的服務名相對應的。第二張表描述了這些微服務名之間的通訊關系,比如第二條記錄表達的是OCDis程序的OCDis2CDRDis到CDRDis的OCDis2CDRDis之間會有一個通訊關系。只要通過這個簡單的配置,就可以完成兩個節點間的通訊關系的建立。這樣的設計會帶來幾個好處。

  1、對於一個復雜的系統,可能有幾十類微服務節點,運行實例可能有上百個,如果有上面的表二,就可以容器的從上面的數據中畫出整個集群的實時拓撲圖,這個對於系統的監控是十分重要的。

  2、集群通訊關系的設計上升了一個等級,業務程序員只需要根據模塊接口設計提供相應的微服務節點,而不需要關心與其它微服務是如何協調工作的。而這些微服務如何“編排”提升到了架構師的工作范圍的層級。這顯然是對復雜度進行分層隔離很好的一個范例。

  3、運維或者管理人員,通過表二的配置可以很容易地操作集群里的某個微服務下線或者上線。在一個龐大的集群里面,如果某類微服務出故障,而CDARF提供了這么一種手段可以去讓這類故障微服務下線,將給系統的穩定性帶來極大的可靠保證。

  4.、原來集群所有的通訊都配置在一個文件中,在分布式系統中就涉及文件的全局一致性的問題。解決的方案可能是,如果要上線一個新類型的配置文件(新增節點、刪除節點、通訊關系改變等等),就要去更新所有在網節點的配置文件。但此時如果新的配置文件有bug,那么可能導致整個集群的故障,並且為了升級某個功能去升級整個集群所有節點的配置也是極不合理的。在新的方案中,節點的配置只定義節點內的通訊和對外提供的微服務名。那么如果要新增某種類型的微服務,不再需要去更新其它節點的配置,只需要將新節點上線,然后在上面的表一新增微服務名,表二增加連接關系就可以了。真正做到了增量升級!

 

  未完待續……

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM