1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        GitHub 從單體到微服務之旅

        共 5330字,需瀏覽 11分鐘

         ·

        2021-08-03 05:11

        轉自:InfoQ

        作者:Sha Ma

        本文作者 Sha Ma 是 GitHub 軟件工程部門的副總裁,負責核心平臺和生態(tài)系統(tǒng)。本文內(nèi)容是介紹 GitHub 如何從單體架構遷移到微服務架構,并對其中一些最佳實踐做了詳細說明。

        1旅程開啟

        GitHub 創(chuàng)建于 2008 年,其宗旨是為開發(fā)人員托管和分享代碼提供便利。GitHub 的創(chuàng)建者也是開源貢獻者,他們在 Ruby 社區(qū)非常有影響力。正因為如此,GitHub 的架構深深地扎根于 Ruby on Rails。

        在公司的整個發(fā)展歷程中,我們雇傭了世界上最好的 Ruby 開發(fā)人員,幫助我們擴展和優(yōu)化代碼庫。如今,我們的平臺上已經(jīng)有超過 5000 萬名開發(fā)人員,每年有超過 8000 萬個 pull 請求合并,全球各大洲有超過 1 億個代碼存儲庫。

        如你所見,這個 單體架構 已經(jīng)帶我們走得很遠。一個演進了 12 年的代碼庫,每天要協(xié)調多次部署。我們有一個規(guī)模很大的平臺,每天處理 10 億次 API 調用,我們還提供了一個高性能的用戶界面,專注于完成這項工作。

        2內(nèi)部快速增長

        在過去 18 個月中,GitHub 內(nèi)部經(jīng)歷了快速增長。我們已經(jīng)有超過 2000 名員工,為代碼庫做貢獻的工程師數(shù)量已經(jīng)是以前的兩倍多。這種增長既包括自身的逐步發(fā)展,也包括收購,如 Semmle、npm、Dependabot 和 Pull Panda。

        此外,GitHub 是一個高度分散的團隊,在疫情發(fā)生前,我們就有超過 70% 的員工是在舊金山總部以外的地方辦公。GitHub 的員工和承包商要跨六大洲展開協(xié)作,他們工作的時區(qū)各不相同。我們有 1000 多名內(nèi)部開發(fā)人員,他們有各種各樣的開發(fā)技能,涉及到許多不同的技術。

        顯然,我們需要從根本上重新考慮下 GitHub 的軟件開發(fā)工作。讓每個人在參與開發(fā)之前都學習 Ruby,讓所有人都在同一個單體代碼庫上進行開發(fā),不再是擴展 GitHub 最高效、最優(yōu)化的方法。根據(jù)康威定律,任何組織設計的系統(tǒng),其結構都是對組織溝通結構的復制。

        反之亦然,單體架構會導致更大規(guī)模的涉眾會議,更復雜的決策過程,因為交織的邏輯和共享的數(shù)據(jù)會影響所有團隊。

        3單體 vs. 微服務

        因此我們就想,是不是該從 Ruby on Rails 單體遷出,轉向一種微服務架構了?如果是這樣的話,我們該如何進行?單體架構和微服務架構各有所長。

        在單體環(huán)境中,配置并運行應用程序更簡單,不用考慮復雜的依賴關系,拉取所有必要的依賴項。新建一個 Hubber,只需幾個小時就可以在本機上配置好 GitHub 并運行起來。在單體架構中,代碼在有些情況下會更簡潔。例如,不用添加超時處理邏輯,也不用考慮如何優(yōu)雅地處理由網(wǎng)絡延遲和中斷所導致的失敗。

        此外,由于所有人都工作在同一個技術棧上,大家對代碼庫都很熟悉,所以可以方便地將開發(fā)人員和團隊調去開發(fā)單體的其他特性,有利于實現(xiàn)特性的全局最優(yōu)??紤]到 GitHub 在過去 18 個月中的增長情況,微服務環(huán)境的一部分優(yōu)點吸引了我們。

        例如,建立具有系統(tǒng)級所有權的特性團隊,通過清晰定義的 API 契約確立職責邊界。在遵循 API 契約的前提下,團隊有充分的自由選擇最適合自己的技術棧。代碼庫更小意味著閱讀更容易、啟動速度更快、問題排查更簡單。開發(fā)人員不用為了提高生產(chǎn)力去理解一整個龐大的代碼庫的內(nèi)部運行機制。最重要的是,服務現(xiàn)在可以根據(jù)各自的需求單獨擴展。

        4務實——以賦能為出發(fā)點

        在開始遷移 GitHub 之前,我們花了一些時間考慮為什么要這樣做,以及這樣做的目標是什么。對我們來說,這是文化上的巨大轉變,需要做大量的工作。我們得想好,到底要解決什么問題和痛點。

        在 GitHub,這樣做可以讓超過一半的開發(fā)人員(在過去的 18 個月中加入)在單體代碼庫之外富有成效地開展工作。我們的目標是賦能而非替代。

        為此,我們得接受這樣一個現(xiàn)實,GitHub 未來的特性將基于一個單體 - 微服務混合的環(huán)境。也就是說,對于我們來說,維護和改進現(xiàn)有的單體代碼庫仍然很重要。有一個很好的例子是,我們最近升級到了 Ruby2.7。感興趣的話,可以從 GitHub 官方博客上了解我們做了什么,以及我們總體上如何改進系統(tǒng)。

        5良好的架構始于模塊化

        良好的架構始于模塊化。拆分單體的第一步是考慮基于特性功能分割代碼和數(shù)據(jù)。這個過程可以在真正在微服務環(huán)境中拆分之前在單體中完成。使代碼庫易于管理,通常都是一種良好的架構實踐。確保每個服務都有自己的數(shù)據(jù),并且能夠控制對這些數(shù)據(jù)的訪問,而且只能通過明確定義的 API 契約訪問。

        我看到,在很多情況下,人們會首先抽出代碼邏輯,但仍然使用單體的共享數(shù)據(jù)庫。這往往會導致分布式單體,這是最糟糕的單體,同時也是最糟糕的分布式。沒有獲得任何好處(比如,單獨快速地向生產(chǎn)環(huán)境中部署一組特性),卻還要應對微服務的復雜性。

        6數(shù)據(jù)拆分

        正確地拆分數(shù)據(jù)是從單體架構轉向 微服務 的基礎。這里將稍微詳細地介紹下 GitHub 的做法。

        首先,我們在現(xiàn)有的數(shù)據(jù)庫模式中識別功能邊界,并按照這些邊界將實際的數(shù)據(jù)庫表分組。例如,我們將所有存儲庫相關的表分到一起,所有用戶相關的分到一起,所有項目相關的分到一起。我們將生成的功能分組稱為模式域,并記錄在 YAML 定義文件中?,F(xiàn)在,這個文件就成了事實來源。在數(shù)據(jù)庫模式中添加或刪除表,都要更新這個文件。我們通過一種靜態(tài)分析測試方法來提醒開發(fā)人員,在修改數(shù)據(jù)庫模式時,要更新這個文件。

        接下來,對于每個模式域,我們找了一個分區(qū)鍵。這是一個共享字段,將一個功能組中的所有信息聯(lián)系在一起。例如,存儲庫模式域(其中包含所有與存儲庫相關的數(shù)據(jù),如問題、pull 請求、評審意見)使用存儲庫 ID 作為分區(qū)鍵。最終,創(chuàng)建數(shù)據(jù)庫模式功能組幫助我們將數(shù)據(jù)拆分到微服務架構所需的不同服務器和集群上。

        對于當前的跨域查詢,我們做了修復,以防數(shù)據(jù)拆分對產(chǎn)品造成破壞。在 GitHub,我們在單體中實現(xiàn)了一個查詢監(jiān)視器來幫助我們檢測,并在發(fā)現(xiàn)跨域查詢時發(fā)出告警信息。我們會根據(jù)域邊界,把這些查詢拆分并重寫成多個,并在應用程序層實現(xiàn)必要的連接。在劃分完功能組后,我們開始通過一個類似的過程,進一步將數(shù)據(jù)分片到相應的租戶組。

        GitHub 有超過 5000 萬用戶和 1 億個存儲庫,在這樣的規(guī)模下,功能組可能會變得非常大。這時,分區(qū)鍵就派上用場了。例如,一種簡單的方法是根據(jù)數(shù)值范圍將不同的用戶分配到不同的數(shù)據(jù)存儲。更常見的可能是根據(jù)每個數(shù)據(jù)集的特性(如區(qū)域和大小)所做的邏輯分組。Tenantizing 是一個很好的方法,可以將數(shù)據(jù)存儲故障的爆炸半徑限制在客戶的一個子集里,而不是一下子影響到所有人。

        7從核心服務和共享資源入手

        我們已經(jīng)花了很多時間討論數(shù)據(jù)拆分的重要性?,F(xiàn)在,我們換個話題,介紹下從單體中抽取服務的基礎工作。一定要記住,依賴方向只能從單體內(nèi)到單體外,不能反過來,否則,我們最終會得到一個分布式單體。也就是說,當從單體中抽取服務時,要從核心服務入手,然后逐步到特性層面。

        接下來,找出開發(fā)人員在單體環(huán)境中開發(fā)時所使用的助力工具。隨著時間的推移構建一些共享工具以方便單體開發(fā),這是很常見的。例如,我們的特性標識,可以讓單體開發(fā)者安心地將新特性從測試環(huán)境轉到生產(chǎn)環(huán)境,因為在這個過程中,他們可以通過這個標識控制誰能看到這些特性。將助力工具轉移出來,讓開發(fā)人員在單體之外也可以使用這些工具。

        最后,在新服務上線運行后,務必要刪除舊的代碼路徑。通過工具來識別誰在調用這個服務,并規(guī)劃好如何將流量全部導向新服務,這樣你就不用老是為兩套代碼提供支持了。在 GitHub,我們使用一個名為 Scientist 的工具幫我們處理這種上線,我們可以用它并排運行和比較新舊代碼路徑。

        8AuthN/AuthZ 抽取

        在 GitHub,我們決定首先抽取的核心服務是身份驗證和授權。身份驗證相當復雜,因為所有東西都依賴于它。網(wǎng)站和 Git 操作之間有一大堆的共享邏輯。也就是說,如果 github.com 宕掉了,那么 Git 系統(tǒng)就無法訪問了,即使是使用命令行窗口,也無法執(zhí)行像 pull、push 這樣的 Git 操作。這就是為什么把這些基礎部分抽取出來如此重要,那可以讓主要功能脫離單體而運行。

        對于我們來說,身份驗證已經(jīng)很簡單,因為我們已經(jīng)在單體外部將它重寫為一個鏡像服務。當前的 Rails 應用程序(即我們的單體)使用 Twirp(這是一個 gRPC 風格的服務到服務通信框架)和它通信,依賴方向是由內(nèi)到外。

        9運營變化

        監(jiān)控、CI/CD、容器化都不是什么新概念,但為了支持從單體到微服務的轉型,節(jié)省時間,加速向微服務的過渡,運營要做必要的改變。在修改這些工作流時,要時刻記著微服務的特性。與為一個大型單體運行單個高度定制化的管道相比,為眾多小型的、獨立運行的、基于不同技術棧的服務提供運營支持存在很大的差別。將監(jiān)控從功能調用指標升級為網(wǎng)絡指標和契約接口。推動實現(xiàn)自動化程度更高、更可靠的 CI/CD 管道,并使其可以在服務之間共享。使用容器化技術支持各種語言和技術棧。創(chuàng)建工作流模板以實現(xiàn)重用。

        例如,在 GitHub,我們創(chuàng)建了一個自助服務運行時平臺,可以用于微服務的打包交付。其目的是大幅減輕每個團隊創(chuàng)建微服務時的運營負擔。它提供了現(xiàn)成的 Kubernetes 模板,可自由使用的 Ingress 負載均衡設置。它可以將日志自動提取到 Splunk,并集成了我們內(nèi)部的部署流程。這樣,任何團隊想要試驗或上線一個新的微服務都會更容易。

        10小處著手,考慮產(chǎn)品 / 業(yè)務價值

        到目前為止,我們主要討論的還是結構性變化,以及從單體成功過渡到微服務架構所需要的基礎工作。此后,任何新特性都應該創(chuàng)建成單體外的一個微服務。

        下一步,找一些簡單的小特性從單體中遷移出來,例如,那些沒有復雜依賴和共享邏輯的特性。在 GitHub,我們是從 webhook 推送和語法高亮開始的。我們希望在遷移更多更大的單體功能之前,找出常見的模式和兩種架構之間的差別。我們是根據(jù)產(chǎn)品和業(yè)務價值來確定微服務的大小。

        我們通過查找經(jīng)常一起更改和部署的代碼和數(shù)據(jù),來確定耦合度較高的特性或功能,并以此為基礎,自然地劃分成可以獨立于其他部分單獨迭代和部署的分組。此外,專注于產(chǎn)品和業(yè)務價值,還有助于組織內(nèi)跨工程團隊、產(chǎn)品和設計開展緊密合作。請注意,拆分得太小往往會增加不必要的復雜度和開銷。例如,需要維護單獨的部署密鑰,更多的服務臺職責,以及由于缺少知識共享而導致的單點故障。

        11實現(xiàn)異步性和彈性代碼

        從單體轉向微服務是重大的模式轉變。在這個過程中,不管是軟件開發(fā)流程,還是實際的代碼庫,都會發(fā)生很大的變化。在最后一部分內(nèi)容中,我們將快速了解下服務之間的通信以及失敗機制(designing for failure),這兩個都是微服務開發(fā)中非常重要的概念。

        服務之間的通信方式有兩種:同步和異步。使用同步通信,客戶端在發(fā)送請求后會等待服務器的響應。使用異步通信, 客戶端在發(fā)送請求后不會等待響應,每條消息都可以由多個接收者處理。在 GitHub,我們使用 Twirp 實現(xiàn)單體與單體外部核心服務(如授權)之間的同步通信。

        然而,隨著越來越多的服務移到單體之外,同步通信開始變得非常低效。而且,那還導致了服務之間的緊耦合,背離了遷移到微服務架構的初衷。更好的做法是創(chuàng)建一個共享的事件管道,協(xié)調多個生產(chǎn)者和消費者之間的消息。在 SendGrid,我們使用的就是這種架構。

        由于服務不再是運行在一臺服務器上,所以考慮網(wǎng)絡通信中的延遲和故障非常重要。對于大部分暫時的網(wǎng)絡問題,使用一種簡單的重試機制,定義好重試頻率和最大重試次數(shù),就足夠了??梢钥紤]使用指數(shù)退避讓重試邏輯變得更加智能。例如,隨著重試次數(shù)的增加延長等待時間,而不是間隔同樣的時間,從而緩解那些因為過載而無法響應的服務器的壓力。作為一種自我保護和自愈機制,還可以在服務之間增加斷路器。例如,在多次嘗試失敗之后,斷路器會打開,在服務恢復之前,不再允許額外的請求進入。為服務設置超時時間,這樣服務就不會一直等待外部服務的響應。設法實現(xiàn)優(yōu)雅的失敗,可以向用戶展示友好的提示信息,或者恢復到緩存中上一個已知的良好狀態(tài)。關注用戶體驗,做對企業(yè)有益的事。

        12小結

        本文前 4 部分主要介紹了在開啟從單體到微服務的旅程之前應該了解的基礎內(nèi)容。關注遷移原因??紤]模塊化和數(shù)據(jù)拆分。從核心服務和共享資源入手,做必要的運營調整。做好這些準備,整個組織的微服務轉型之旅就會更加令人愉快。接下來,我們討論了從哪里入手,以及如何將微服務與產(chǎn)品和業(yè)務價值聯(lián)系起來。最后,我們介紹了微服務的兩個關鍵概念:服務之間的通信和構建彈性系統(tǒng)。

        - END -

        ?? 關注「HelloGitHub」第一時間收到更新??

        瀏覽 91
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            美女做爰高潮免费视频 | 亚洲欧洲一区 | 欧美高清xxxx做受3d左右屏 | 我被同桌扒开双腿摸出了白浆 | 日韩无码中文字幕电影 | 狠狠躁夜夜躁人蜜臀av | 91麻豆精品国产乱码二区三区 | 97成人人妻一区二区三区 | 久久久久久久国产免费看 | 女人18片毛片60分钟翻译 |