1. 程式人生 > >程式設計師修神之路--有狀態的服務其實可以做更多的事情

程式設計師修神之路--有狀態的服務其實可以做更多的事情


菜菜哥,你換形象啦?

這麼巧,你也換啦!聽說是不會畫畫的菜嫂經過九牛二虎之力的功勞哦!鼓掌......

前幾天我出去面試了,面試官問我微服務的知識,我回答的可好了

看來微服務你真的下功夫研究了呀

是呀是呀,但是碰到一個問題,有狀態的服務是什麼意思呢?

看來你又掛在這個問題上了,且聽這次分解

簡介

對於初學者,心裡對“有狀態服務”的理解可能比較模糊,但是從面向物件程式設計思想的角度去理解也許會明朗很多。面向物件程式設計思想提倡的是用程式語言去描述世間萬物,所以面向物件程式設計的語言都會提供描述物件的容器以及物件行為的表達方式。舉一個很簡單的栗子,在c#或者java中,表達物件的容器就是class,物件的行為通過一系列的介面或者函式來表達。更進一步,物件抽象出來之後,大多數物件都有自己的內部狀態,體現到程式碼上也就是常見的類的屬性。

面向物件程式設計的基本思想本質上是對現實世界的一種抽象,萬物皆可抽象。

根據業務把物件抽象出來之後,每一個例項化的物件其實都可以有自己的狀態,比如:在最常見的遊戲場景中,每一個玩家都是“玩家"這類物件的一個例項,每一個玩家都有自己的名字,性別,等級,HP等屬性,這些屬性本質上就是玩家的狀態,隨著時間的推移,每個玩家的HP,等級等屬性會隨之變化,這些變化其實就是這個玩家狀態的變化。對應到有狀態的服務也是如此,之所以稱之為有狀態,是因為服務內部的物件狀態會隨著業務有著對應的變動,而這些變動只發生在這個服務內部,在外界看來,這個服務好像是有狀態的。

有狀態的服務本質上是一些有狀態物件的集合,這些物件狀態的變化只發生在當前服務程序中。

優勢和劣勢

有狀態服務之所以被稱為有狀態,一個很大的原因是它可以追溯狀態的變化過程,也就是說一個有狀態的服務儲存著狀態變化的記錄,並可以根據這些歷史記錄恢復到指定的狀態,這在很多場景下非常有用。舉一個很簡單的栗子:我們平時玩的鬥地主遊戲,三個玩家,當有一個玩家因為網路原因掉線,經過一段時間,這個玩家又重新上線,需要根據某些記錄來恢復玩家掉線期間系統自動出牌的記錄,這些出牌記錄在這個業務中其實就是這個玩家的狀態變化記錄。在有狀態的服務中,很容易做到這一點。

其實實際開發中很多場景不需要記錄每個狀態的變化,只保留最新狀態即可,不單單是因為儲存每個狀態的變化需要大量的儲存和架構設計,更因為是很多業務根本不需要這些狀態變化記錄,業務需要的只是最新的狀態,所以大部分有狀態的服務只儲存著最新的狀態。

有狀態的服務在設計難度上比無狀態的服務要大很多,不僅僅是因為開發設計人員需要更好的抽象能力,更多的是一致性的設計問題。現代的分散式系統,都是由多個伺服器組成一個叢集來對外提供服務,當一個物件在伺服器A產生之後,如果請求被分配到了伺服器B上,這種情況下有狀態的服務毫無意義,為什麼呢?當一個相同的業務物件存在於不同的伺服器上的時候,本質上就違背了現實世界的規則,你能說一個人,即出生在中國,又出生在美國嗎? 所以有狀態的服務對於一致性問題有著天然的要求,這種思想和微服務設計理想不謀而合,舉個栗子:一個使用者資訊的服務,對外提供查詢修改能力,凡是使用者資訊的業務必須通過這個服務來實現。同理,一個物件狀態的查詢修改以及這個物件的行為,必須由這個物件的服務來完成。

有狀態的服務要求相同業務物件的請求必須被路由到同一個服務程序。

因此,有狀態的服務對於同一個物件的橫向擴容是做不到的,就算是做的到,多個相同物件之間的狀態同步工作也必然會花費更多的資源。在很多場景下,有狀態的服務要注意熱點問題,例如最常見的秒殺,這裡並非是說有狀態服務不適合大併發的場景,反而在高併發的場景下,有狀態的服務往往表現的比無狀態服務更加出色。

Actor模型



在眾多的併發模型中,最適合有狀態服務設計的莫過於Actor模型了,如果你對actor模型還不熟悉,可以擼一遍菜菜之前的文章:https://mp.weixin.qq.com/s/eEiypRysw5jsC7iYUp_yAg  actor模型天生就具備了一致性這種特點,讓我們在對業務進行抽象的時候,不必考慮一致性的問題,而且每一個請求都是非同步模式,在物件內部修改物件的狀態不必加鎖,這在傳統的架構中是做不到的。

基於actor模型,系統設計的難點在於抽象業務模型,一旦業務模型穩定,我們完全可以用記憶體方式來儲存物件狀態(也可以定時去持久化),記憶體方式比用其他網路儲存(例如redis)要快上幾個量級,菜菜也有一篇文章大家可以去擼一下:https://mp.weixin.qq.com/s/6YL3SnSriKEnpCyB5qkk0g  ,既滿足了一致性,又可以利用程序內物件狀態來應對高併發業務場景,何樂而不為呢?

有不少同學問過我,actor模型要避免出現熱點問題,就算有記憶體狀態為其加速,那併發數還是超過actor的處理能力怎麼辦呢? 其實和傳統做法類似,所有的高併發系統設計無非就是“分”一個字,無論是簡單的負載均衡,還是複雜的分庫分表策略,都是分治的一種體現。一臺伺服器不夠,我就上十臺,百臺.....

所有的高併發系統設計都是基於分治思想,把每一臺伺服器的能力發揮到極致,難度最大的還是其中的排程演算法。

用actor模型來應對高併發,我們可以採用讀寫分離的思想,主actor負責寫請求,並利用某種通訊機制把狀態的變化通知到多個從actor,從actor負責對外的讀請求,這個DB的讀寫分離思想一致,其中最難的當屬actor的狀態同步問題了,解決問題的方式千百種,總有一種適合你,歡迎你留言寫下你認為最好的解決方案。

案例(玩家資訊服務)

由於菜菜是c#出身,對c#的Actor服務框架Orleans比較熟悉,這裡就以Orleans為例,其他語言的coder不要見怪,Orleans是一個非常優秀的Actor模型框架,而且支援最新的netcore 3.0版本,地址為:https://github.com/dotnet/orleans  有興趣的同學可以去看一下,而且分散式事物已經出正式版,非常給力。其他語言的也非常出色java:https://github.com/akka/akka

golang:https://github.com/AsynkronIT/protoactor-go


1. 首先我們定義玩家的狀態資訊

//玩家的資訊,其實也就是玩家的狀態資訊
    public class Player {
        /// <summary>
        /// 玩家id,同時也是玩家這個服務的主鍵
        /// </summary>
        public long Id { get; set; }
        /// <summary>
        /// 玩家姓名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 玩家等級
        /// </summary>
        public int Level { get; set; }
    }

2. 接下來定義玩家的服務介面

 /// <summary>
    /// 玩家的服務介面
    /// </summary>
    interface IPlayerService: Orleans.IGrainWithIntegerKey
    {
        //獲取玩家名稱
        Task<string> GetName();
        //獲取玩家等級
        Task<int> GetLevel();
        //設定玩家等級,這個操作會改變玩家的狀態
        Task<int> SetLevel(int newLevel);
    }

3. 接下來實現玩家服務的介面

public class PlayerService : Grain, IPlayerService
    {
        //這裡可以用玩家的資訊來代表玩家的狀態資訊,而且這個狀態資訊又充當了程序內快取的作用
        Player playerInfo;
        public async Task<int> GetLevel()
        {
            return (await LoadPlayer()).Level;
        }

        public async Task<string> GetName()
        {
            return (await LoadPlayer()).Name;
        }

        public async Task<int> SetLevel(int newLevel)
        {
            var playerInfo =await LoadPlayer();
            if (playerInfo != null)
            {
                //先進行資料庫的更新,然後在更新快取的狀態, 程序內快取更新失敗的機率幾乎為0
                playerInfo.Level = newLevel;                
            }
            return 1;
        }

        private async Task< Player> LoadPlayer()
        {
            if (playerInfo == null)
            {
                var id = this.GetPrimaryKeyLong();
                //這裡模擬的資訊,真實環境完全可以從持久化裝置進行讀取
                playerInfo= new Player() { Id = id, Name = "玩家姓名", Level = 1 };
            }
            return playerInfo;
        }
    }

以上只是一個簡單案例,有狀態的服務還有更多的設計方案,以上只供參考完

相關推薦

程式設計師--狀態服務其實可以事情

菜菜哥,你換形象啦? 這麼巧,你也換啦!聽說是不會畫畫的菜嫂經過九牛二虎之力的功勞哦!鼓掌...... 前幾天我出去面試了,面試官問我微服務的知識,我回答的可好了 看來微服務你真的下功夫研究了呀 是呀是呀,但是碰到一個問題,有狀態的服務是什麼意思呢?

程式設計師--為什麼了SOA,我們還用微服務

菜菜哥,我最近需要做一個專案,老大讓我用微服務的方式來做 那挺好呀,微服務現在的確很流行 我以前在別的公司都是以SOA的方式,SOA也是面向服務的方式呀 的確,微服務和SOA有相同之處 面向服務的架構(SOA)是一個元件模型,它將應用程式的不同功能單元(稱為服務)進行拆分,並通過這些服務之間定義良好

程式設計師--高併發下如何縮短響應時間

菜菜哥,請你看電影呀,但是得幫我一個忙 好呀,看什麼? 哥斯拉2:怪獸之王 看過了~ X戰警:黑鳳凰 看過了 追龍2和黑衣人呢? 都看過了,你說幫什麼忙吧 我一個網站響應特別慢,你幫我優化一下唄,很簡單 你以為真的很簡單嗎? 你以為真的很簡單嗎? 定義 網站響應時間是指系統對請求作出響應

程式設計師--做好分庫分表其實很難之一(繼續送書)

菜哥,領導讓我開發新系統了 這麼說領導對你還是挺信任的呀~ 必須的,為了設計好這個新系統,資料庫設計我花了好多心思呢 做一個系統我覺得不應該從資料庫入手,應該從設計業務模型開始,先不說這個,說說你的資料庫設計的優勢 為了高效能我首先設計了分庫 分表策略,為以後打下基礎 那你的資料量將來會很大嗎?分庫

程式設計師--做好分庫分表其實很難二(繼續送書)

菜菜哥,上次聽你給我講了分庫的情況後,我明白了很多,能再給我講講分表嗎 有收穫就好,分表其實有很多情況和分庫類似 還有不一樣的情況嗎? 有呀,本來資料庫和表是不同層面的東西,肯定有差異 那你給講講唄 講可以,一杯coffee如何? 為什麼分 在正式開始之前,菜菜還是要強調一點,你的資料表是否應該

程式設計師--用NOSql給高併發系統加速(送書)

隨著網際網路大潮的到來,越來越多網站,應用系統需要海量資料的支撐,高併發、低延遲、高可用、高擴充套件等要求在傳統的關係型資料庫中已經得不到滿足,或者說關係型資料庫應對這些需求已經顯得力不從心了。關係型資料庫經過幾十年的發展已經很成熟,強大的sql語句支援,完美的ACID屬性的支援,使得關係型資料庫廣泛應用於

程式設計師--高併發系統設計負載均衡架構

菜菜哥,上次你給我講的分庫分表策略對我幫助很大 有幫助就好,上次請我的咖啡也很好喝~ 呵呵,不過隨著訪問量的不斷加大,網站我又加了nginx做負載均衡 好呀,看來要進階高階工程師啦~ 負載均衡也很簡單呀,一個nginx就搞定了,現在可以說我精通負載均衡了吧 其實負載均衡的內容還有很多 一個系統

程式設計師--設計一套RPC框架並非易事

菜菜哥,我最近終於把Socket通訊調通了 這麼底層的東西你現在都會了,恭喜你離漲薪又進一步呀 http協議不也是利用的Socket嗎 可以這麼說,http協議是基於TCP協議的,底層的資料傳輸可以說是利用的socket 既然Socket通訊會了,那一個rpc的框架不就很容易就能實現了嗎? 一個比較

程式設計師--要想做好微服務架構,並非易事!

菜菜哥,上次聽你講了微服務和SOA,明白了很多道理 看來面試用上了吧 呵呵,但是面試官問我微服務有什麼優點和缺點... 看來還得給你詳細講一講微服務 概念 微服務(Microservices Architecture)是一種架構風格,一個大型複雜軟體應用由一個或多個微服務組成。系統中的各個微服務可

程式設計師--kubernetes是微服務發展的必然產物

菜菜哥,我昨天又請假出去面試了 戰況如何呀? 多數面試題回答的還行,但是最後讓我介紹微服務和kubernetes的時候,掛了 話說微服務和kubernetes內容確實挺多的 那你給我大體介紹一下唄 可以呀,不過要請和coffee哦 ◆◆kubernetes介紹◆

程式設計師--容器技術為什麼會這麼流行

菜菜哥,你上次講的kubernetes我研究了一下,你再給我講講docker唄 docker可很流行呀 kubernetes是容器編排技術,容器不就是指的docker嗎? docker可不等於容器哦,docker只算是容器的一種吧,算了容器的典型代表 容器的誕生 在傳統的軟體部署方式中,程式設計師需

程式設計師--打通Docker映象釋出容器執行流程

菜菜哥,我看了一下docker相關的內容,但是還是有點迷糊 還有哪不明白呢? 如果我想用docker實現所謂的雲原生,我的專案該怎麼釋出呢? 這還是要詳細介紹一下docker了 Docker 是一個開源的應用容器引擎,基於 Go 語言 並遵從 Apache2.0 協議開源。Docker 可以讓開發者

程式設計師--分散式系統設計理念這麼難學?

### 分散式系統 身為二十一世紀的一名程式設計師,沒聽說過分散式系統就顯得自己好像沒有女票一樣尷尬。無論是出去面試跟面試官吹水,還是在工作中和同事吹水,分散式系統永遠是你顯得高人一等的籌碼。分散式系統已經誕生了好幾十年,說起來比我們八零後程序員好要老成,隨著現代網際網路的崛起,對於系統在效能,可靠性上的要求

程式設計師--簡約而不簡單的分散式通訊基石

![](https://img2020.cnblogs.com/blog/157718/202007/157718-20200721214806192-550792969.png) 分散式系統可以總結為是處於不同物理位置的多個程序組成的整體,為了確保這個整體有效並且高效的對外提供服務,每個節點之間都有可

程式設計師- CXO讓我一個計算器!!

菜菜呀,個稅最近改革了,我得重新計算你的工資呀,我需要個計算器,你開發一個吧 CEO,CTO,

程式設計師--優雅快速的統計千萬級別uv(留言送書)

菜菜,咱們網站現在有多少PV和UV了? Y總,咱們沒有統計pv和uv的系統,預估大約有一千萬uv吧 寫一個統計uv和pv的系統吧 網上有現成的,直接接入一個不行嗎? 別人的不太放心,畢竟自己寫的,自己擁有主動權。給你兩天時間,系統性能不要太差呀 好吧~~~ 定義PV是page view的縮寫,即頁

一張圖大帶你瞭解遊戲程式設計師的學習

葉勁峰(Milo Yip)現任騰訊 T4 專家、互動娛樂事業群魔方工作室群前臺技術總監。他獲得香港大學認知科學學士(BCogSc)、香港中文大學系統工程及工程管理哲學碩士(MPhil)。他是《遊戲引擎架構》譯者、《C++ Primer 中文版(第五版)》審校。他曾參與《天涯明月刀》、《鬥戰神》、《愛麗絲

一個Java程式設計師的阿里

前言 最近有些朋友在面試阿里,加上 Java-Interview 專案的原因也有小夥伴和我討論,近期也在負責部門的招牌,這讓我想起年初那段長達三個月的奇葩面試經歷。 本來沒想拿出來說的,畢竟最後也沒成。 但由於那幾個月的經歷讓我瞭解到了大廠的工作方式、對候選同學的考察重點以及面試官的套路