Surging實踐經驗

背景

在去年9月份的時候,我入職一家作航空軟件產品的公司。當時公司部門領導決定構建一個技術平臺(或稱爲技術中臺),經過該技術平臺進而孵化各個業務系統。說白了就是須要經過一個分佈式框架或是微服務框架提升應用系統的性能和併發處理能力、業務處理能力。
當時現有的系統是在 .net framework平臺上搭建的簡單的單體應用,並不具有可用性、擴展性、易用性等。
我在入職前也學習過一些微服務相關的知識,並經過搜索引擎瞭解了.net平臺下的一些微服務框架和分佈式架構。在對比不一樣技術框架的背景後,我決定使用surging做爲公司的技術平臺。緣由在於:linux

  • Surging的設計思想和理念更符合微服務的架構思想,經過dotnetty框架實現的RPC通訊,內置了服務治理保證通訊的可靠性。
  • 經過向服務註冊中心(Zookeeper、Consul)註冊相關元數據來管理集羣的服務命令、路由信息、緩存中間件等數據,服務註冊不須要進行額外的處理
  • Surging內置了負載均衡算法。
  • Surging支持多種協議的通訊方式,而且支持ws服務主機與通常服務(Http、TCP)主機之間直接經過RPC進行通訊。
  • 服務之間的調用很方便,做者提供了基於ServiceProxyProvider、和基於ServiceProxyFactory的rpc調用方式,簡單易用。
  • 模塊化設計,很方便的對模塊進行擴展。
  • 支持事件總線,經過消息對象實現的時效件縱向的適配能夠實現發佈訂閱的交互模式。
  • .net core 跨平臺,性能更高。

架構維護

因爲做者一直在維護surging,並且咱們也須要對surging的一些模塊進行調整,也須要擴展一些surging的組件包,因此咱們在使用surging的過程當中是直接獲取源代碼後,在公司維護一份本身的源碼,而後打包成nuget包,併發布到內部的nuget服務,經過內部的nuget對surging組件進行分發。
在獲取surging源碼後,我對surging進行了以下調整:git

  1. 根據公司要求,對名稱空間和包名稱進行了調整。
  2. 對異常處理進行了重構
  3. 將消息返回的數據結果名稱重命名爲Data,統一了消息返回碼。
  4. 修改了默認的json序列化器,默認使用camelCame風格
  5. 重構了簽發token的方法(使用jose-jwt組件)
  6. 支持經過RpcContext設置token的payload和獲取payload,經過擴展RpcContextSession獲取運行時登陸用戶
  7. 擴展了Dapper、Domain、Validation、Schedule(基於Quartz的分佈式任務調度)等組件包
  8. swagger文檔支持jwt token驗證
  9. 新增surging打包腳本等等
  10. 如今surging的demo案例和內部的開發者文檔

若是你在使用surging的過程當中,對surging源碼較爲熟悉,並但願對surging進行必定的調整、擴展本身公司的一些組件的時候,您能夠經過社區獲取surging的源代碼,並在公司的代碼庫維護本身的分支。可是須要對做者對源碼的修改要及時瞭解和熟悉。
nuget服務的搭建可使用nuget官方提供的nuget.server或是nexus 。
對架構的維護多是一個持續的和長久的過程,你能夠經過企業內部的需求和做者對框架的調整對技術框架持續的進行調整和維護。在對surging進行調整維護後,就經過經過打包腳本進行打包發佈到內部的nuget服務。github

業務框架

構建微服務主機

因爲在構建每一個微服務主機的代碼和配置文件都是一致的,沒法就是對配置文件的一些配置項進行調整,因此能夠將構建微服務主機的代碼和配置文件抽象出來,統一放置在Shared目錄中,再在項目文件中經過import進入便可。
如何將公共的腳本、配置文件、屬性抽象出來,能夠參考:https://github.com/surging-cloud/Surging.Hero/tree/develop/hero/src/Shared 。
如何構建主機呢?Surging經過ServiceHostBuilder來構建微服務主機,在構建主機過程當中,能夠添加一些服務組件或是指定相應的配置文件。構建主機的代碼以下:web

須要注意的是能夠經過SurgingServiceEngine來指定surging服務引擎掃描的業務組件的目錄。以及也能夠經過Startup注入相應的服務或是制定配置文件。redis

var host = new ServiceHostBuilder()
                 .RegisterServices(builder =>
                 {
                     builder.AddMicroService(option =>
                      {
                          option.AddServiceRuntime()
                           .AddClientProxy()
                           .AddRelateServiceRuntime()
                           .AddConfigurationWatch()
                           .AddServiceEngine(typeof(SurgingServiceEngine))
                           ;

                          builder.Register(p => new CPlatformContainer(ServiceLocator.Current));
                      });
                 })
                 .ConfigureLogging(loggging =>
                 {
                     loggging.AddConfiguration(
                         AppConfig.GetSection("Logging"));
                 })
                 .UseServer(options => { })
                 .UseConsoleLifetime()
                 .Configure(build =>
                 {
#if DEBUG
                     build.AddCacheFile("${cachePath}|/app/configs/cacheSettings.json", optional: false, reloadOnChange: true);
                     build.AddCPlatformFile("${surgingPath}|/app/configs/surgingSettings.json", optional: false, reloadOnChange: true);
                     build.AddEventBusFile("${eventBusPath}|/app/configs/eventBusSettings.json", optional: false);
                     build.AddConsulFile("${consulPath}|/app/configs/consul.json", optional: false, reloadOnChange: true);


#else
                    build.AddCacheFile("${cachePath}|configs/cacheSettings.json", optional: false, reloadOnChange: true);                      
                    build.AddCPlatformFile("${surgingPath}|configs/surgingSettings.json", optional: false,reloadOnChange: true);                    
                    build.AddEventBusFile("configs/eventBusSettings.json", optional: false);
                    build.AddConsulFile("configs/consul.json", optional: false, reloadOnChange: true);
#endif
                 })
                 .UseProxy()
                 .UseStartup<Startup>()
                 .Build();

            using (host.Run())
            {
                Console.WriteLine($"服務主機啓動成功{DateTime.Now}。");
            }

 

業務框架的分層

通常地,我會將每一個微服務組件分爲以下幾層:算法

1. Host

用於構建微服務主機和服務寄宿,通常我會直接引用Application層,託管應用服務自己。docker

2. IApplication 應用接口層

  • 用於定義應用接口,每一個應用接口都應當繼承IServiceKey,Surging經過應用接口生成服務條目(ServiceEntry)
  • 使用ServiceBundle特性來標識路由模板。
  • 可使用ServiceCommand來對Action進行註解,該元數據會被註冊到服務註冊中心,在RPC通訊過程當中,經過ServiceCommand註解的元數據實現服務治理。該特性可不須要配置,能夠經過SurgingSettings.json統一指定相關的配置,若是配置了ServiceCommand特性,會優先選擇特性指定的配置值。
  • 能夠經過Service特性指定Action的一些元數據。
  • 應用接口層除了定義應用接口以外,還須要定義相關的DTO對象。
  • 應用接口層能夠被其餘微服務組件應用或是經過nuget進行分發,經過IServiceProxyFactory建立應用接口的代理,從而實現RPC通訊。

3. Application 應用層

  • 應用層主要是實現業務流程和輸入輸出判斷,不實現複雜的業務邏輯
  • 應用層的應用須要實現應用接口定義的接口,並繼承ProxyServiceBase,基類ProxyServiceBase提供了一些通用的方法。

4. Domain 領域層

  • 領域層主要是實現具體的業務邏輯

5. Domian.Shared

  • 定義微服務組件通用的值類型(model或是枚舉類型),可被其餘微服務組件引用

容器化服務和服務編排

服務容器化

docker是一款優秀的容器引擎產品。將服務容器化,可以最大化的發揮微服務的體驗性。可以讓開發者感覺到docker構建一次,到處運行的魅力所在。因此我強烈推薦在開發過程當中,使用docker容器化服務組件,使用docker-compose編排微服務組件。
vs對docker-compose進行開發調試提供了很是友好的體驗性。
通常地,會在服務組件的Host層提供Dockerfile用於構建docker鏡像。以下的dockerfile提供了微服務組件的編譯、構建等過程。數據庫

FROM microsoft/dotnet:2.2.0-runtime AS base
WORKDIR /app
ARG rpc_port=100
ARG http_port=8080
ARG ws_port=96
ENV TZ=Asia/Shanghai 
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 
EXPOSE ${rpc_port} ${http_port} ${ws_port}

FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY . .
ARG sln_name
RUN dotnet restore ${sln_name} && \
    dotnet build --no-restore -c Release ${sln_name}

FROM build AS publish
ARG host_workdir
WORKDIR ${host_workdir}
RUN dotnet publish --no-restore -c Release -o /app

FROM base AS final
ARG host_name
ENV host_name=${host_name}
COPY --from=publish /app .
ENTRYPOINT dotnet ${host_name}

 

服務編排

使用docker-compose編排微服務組件,通常的,使用docker-compose.yml定義鏡像構建的上下文、指定網絡、鏡像名稱、掛載的目錄等,經過docker-compose.override.yml來指定配置文件的環境變量,.env來設置環境變量的值,經過docker-compose.vs.debug.yml來指定調試過程當中的相關設置(部署中可不指定該編排文件)。npm

須要注意的是,surging在開發過程當中,基礎服務也經過docker-compose來編排和啓動,且必須在開發和調試前啓動基礎服務。基礎服務和surging服務組件指定的網絡必須同一個。json

基礎服務編排以下所示:

因爲開發過程當中的基礎服務並無考慮到高可用,在生產環境中建議基礎服務集羣化。

version: '3.7'

services:
  consul:
    image: consul:latest
    ports:
      - "8400:8400"
      - "8500:8500"
      - "8600:8600"
      - "8600:8600/udp"
    command: "agent -server -bootstrap-expect 1 -ui -client 0.0.0.0"
    networks:
      - surging_hero_service_net
  redis:
    image: redis:latest
    ports:
      - "6379:6379"
    networks:
      - surging_hero_service_net
  rabbitmq:
    image: rabbitmq:management
    environment:
      RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG"
      RABBITMQ_DEFAULT_USER: "rabbitmq"
      RABBITMQ_DEFAULT_PASS: "rabbitmq"
      RABBITMQ_DEFAULT_VHOST: "/"
    ports:
      - "15672:15672"
      - "5672:5672"
    networks:
      - surging_hero_service_net
      
networks:
  surging_hero_service_net: 
    driver: bridge
    name: surging_hero_service_net
    ipam:
      driver: default
      config:
      - subnet: 172.22.0.1/16

 

微服務組件的編排請參考: https://github.com/surging-cloud/Surging.Hero/tree/develop/hero/docker-compose/surging.hero

開發與調試

其實在開發過程當中,因爲業務模塊的不一樣,責任人不一樣,開發團隊不一樣,開發者擁有的權限不一樣,業務模塊的代碼有可能放到不一樣的git倉庫。建議將微服務服務組件的應用接口層和Domian.Shared能夠發佈的企業內部的nuget服務。其餘微服務組件能夠經過nuget服務引用應用接口層和Domian.Shared組件。
若是源代碼都放到一個git倉庫中,也能夠創建多個解決方案或是docker-compose編排文件項目來編排不一樣的服務,方便開發和調試。

常見問題

首次使用docker-compose進行調試服務時,因爲vs會從網絡上下載vsdbg組件,因爲網絡緣由,通常都會比較慢,開發者能夠從其餘同事的電腦的家目錄下拷貝vsdbg到本機,從新打開vs,而後再進行調試。

Devops

業務流程

在開發過程當中,咱們使用Jenkins實現持續集成和部署。整個流程以下所述:

  1. 開發者編寫業務代碼或修復完bug後,提交代碼,push到遠程倉庫,併發起pr請求,請求合併到develop分支。
  2. 當代碼審覈經過後,合併到develop分支後,經過設置gitlab或是giteewebhook,觸發jenkins執行構建。或是經過設置Jenkins的定時任務檢測代碼庫變化,當代碼庫變化後,jenkins獲取最新代碼,執行構建操做(因爲當時咱們Jenkins部署的環境是內網,gitee沒法訪問公司內網,因此沒法設置webhook)
  3. Jenkins經過預先設置好的命令和腳本執行構建打包程序。本質上是執行docker-compose build打包docker鏡像,當完成構建和打包docker鏡像後,而後將鏡像推送到企業內部的docker鏡像倉庫。
  4. 以後,jenkins經過Jenkins SSH插件將部署腳本拷貝到k8集羣的master節點,經過ssh插件在k8s master節點執行部署命令。完成後,微服務集羣將自動部署到指定的k8s集羣中。

整個devops流程以下所述(可是咱們沒有與釘釘作集成):
devops

注意事項

  1. 企業內部的docker倉庫除了可使用harbor搭建以外,還可使用nexus。推薦使用nexus做爲倉庫管理服務,由於nexus除了支持docker鏡像倉庫以外,還支持nuget包、npm等格式的包管理。
  2. 建議企業內部在構建業務平臺時,根據業務模塊劃分主題,一個主題對應一個數據庫,一個git倉庫,一個項目組,多個相關的微服務組件,一個Jenkins構建項目。每一個主題獨立的進行持續集成與部署。
  3. 建議基礎服務consul、rabbitmq、redis考慮集羣。

產品交付和部署

  1. 通常的,咱們經過docker鏡像完成產品交付與部署。能夠經過編寫部署腳本在k8s集羣或是經過rancher進行部署。
  2. 可使用k8s或是rancher提供的Dashborad進行容器和服務的監控和管理。

體會

  1. surging的設計思想是無疑正確的。相比於市面上其餘的.net微服務框架或是分佈式框架,不管是服務治理仍是內部通訊機制,服務引擎設置,主機寄宿均有獨到之處。(abp vnext的微服務框架經過內部網關Ocelot進行通訊,徹底違反的去中心化設計,並且性能也相對較差的多)
  2. 在使用surging的過程當中,也遇到了一些問題或是bug(例如:1.首次訪問性能較差;2.服務實例沒法支持同時擴展),在反饋到github社區或是請求做者協助,都可以獲得及時反饋。目前做者已經即將完成對surging2.0的開發,相信會有更優秀的體驗。
  3. 在開發和測試、部署和產品交付中推薦將服務容器化,推薦使用linux做爲部署服務器。
  • 最後

    • 若是你對surging感興趣,能夠在github上對surging關注。
    • 若是你對如何使用surging落地開發,您能夠在github上關注surging.hero
      • surging.hero是一個使用surging做爲開發框架的權限管理平臺。目前項目剛剛開始,歡迎各位開發者加入,若是您想加入surging.hero的開發或是願意爲surging的生態作出貢獻,歡迎加入surging-cloud社區。
      • 若是你但願加入surging-cloud社區,能夠將你的github帳號經過email到:1029765111@qq.com,並備註`申請加入 surging-cloud社區 便可。
      • 若是您對surging.hero感興趣並但願加入surging.hero的開發,也能夠申請加入qq羣:713943626
    • 若是你們對surging確實感興趣,後期我有時間的話,能夠寫一些我使用surging的經驗或是對源碼的理解。
相關文章
相關標籤/搜索