什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

作者:中華石杉,十餘年BAT架構經驗傾囊相授。個人微信公眾號:石杉的架構筆記(ID:shishan100)

一、讀寫鎖的介紹

本文我們來聊一下讀寫鎖。所謂的讀寫鎖,就是將一個鎖拆分為讀鎖和寫鎖兩個鎖,然後加鎖的時候,可以加寫鎖,也可以加讀鎖。

如下面代碼所示:

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

如果有一個線程加了寫鎖,那麼其他線程就不能加寫鎖了,同一時間只能允許一個線程加寫鎖

因為加了寫鎖就意味著有人要寫一個共享數據,那同時就不能讓其他人來寫這個數據了。

如果有線程加了寫鎖,其他線程就不能加讀鎖了,因為既然都有人在寫數據了,你其他人當然不能來讀數據了!

如果有一個線程加了讀鎖,別的線程是可以同時加讀鎖的,因為只是有線程在讀數據而已,此時別的線程也是可以來讀數據的!

同理,如果一個線程加了讀鎖,此時其他線程是不可以加寫鎖的,因為既然有人在讀數據,那就不能讓你隨意來寫數據了!

好了!這個就是初步介紹一下讀寫鎖的使用方法,相信很多同學應該之前都知道了,因為這個是java開發中非常基礎的一塊知識。

二、微服務註冊中心的讀寫鎖優化

現在進入主題,我們主要聊一下微服務註冊中心裡面的讀寫鎖優化。

為什麼要聊這個問題?

因為如果你出去面試,很可能被問到讀寫鎖的問題,此時你可以自然的帶出來,你之前瞭解過Spring Cloud微服務技術架構,同時對裡面的微服務註冊中心的註冊表讀寫鎖優化有一些自己的感悟和看法。

這樣的話,相比於你簡單的給面試官聊聊讀寫鎖的基本概念和使用方法,要增色不少!

首先,大家需要了解一點微服務的整體架構知識,可以參考之前寫過的一篇文章拜託,面試請不要再問我Spring Cloud底層原理!。

同時還需要了解一下Spring Cloud Eureka(即微服務註冊中心)的核心原理。

這個可以參考之前寫過的一篇文章【雙11狂歡的背後】微服務註冊中心是如何承載大型系統千萬級訪問的

瞭解了這些前置知識之後,我們正式開始。

先來看看下面的圖,現在我們知道一個微服務註冊中心(可以是Eureka或者Consul或者你自己寫的一個微服務註冊中心)肯定會在內存中有一個服務註冊表的概念。

這個服務註冊表中存放了各個微服務註冊時發送過來的自己的地址信息,裡面保存了每個服務有多少個服務實例,每個服務實例部署在哪臺機器上監聽哪個端口號,主要是這樣的一些信息,如下圖所示:

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

那現在問題來了,這個服務註冊表的數據,其實是有人讀也有人寫的,對不對?

舉個例子,比如有的服務啟動的時候會來註冊,此時就會修改服務註冊表的數據,這個就是寫的過程。

接著,別的服務也會來讀這個服務註冊表的數據,因為每個服務都需要感知到其他服務在哪些機器上部署。

所以,這個內存裡的服務註冊表數據,天然就是有讀寫併發問題的!可能會有多個線程來寫,也可能會有多個線程來讀!

如果你對同一份內存中的註冊表數據不加任何保護措施,那麼可能會有多線程併發修改共享數據的問題,可能導致數據錯亂,對吧?

上述過程,大家看看下面的圖,就明白了。

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

此時你可能會想,如果對服務註冊表的服務註冊和讀取服務註冊表的方法,都加一個synchronized關鍵字,不就可以了?

你也許會說,加上synchronized,直接讓所有線程對服務註冊表的讀寫操作,全部串行化。那不就可以保證內存中的服務註冊表數據安全了嗎?

下面是一段偽代碼,大家來感受一下:

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

在上面的代碼中直接給寫(服務註冊)讀(讀取服務註冊表)兩個方法,都暴力的加上了synchronized關鍵字,確實是可以保證服務註冊表的數據不錯亂,但是這樣肯定是不太合適的。

因為這麼搞的話,相當於是所有的線程讀寫服務註冊表數據,全部串行化了。

大家讀到這裡,先暫停一下,思考思考我們想要的效果是什麼?

其實不就是在有人往服務註冊表裡寫數據的時候,就不讓其他人寫了,同時也不讓其他人讀!

然後,有人在讀服務註冊表的數據的時候,其他人都可以隨便同時讀,但是此時不允許別人寫服務註冊表數據了!

對吧,我們想要的,其實不就是這個效果嗎?

想清楚了這點,我們就不應該暴力的加一個synchronized,讓所有讀寫線程全部串行化,因為那樣會導致併發性非常的低。

大家看看下面的圖,我們想要的

第一個效果:一旦有人在寫服務註冊表數據,我們加個寫鎖,此時別人不能寫,也不能讀。

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

如果有人在讀數據呢?此時就可以讓別人都可以讀,但是不允許任何人寫。

大家看下面的圖:

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?

關鍵點來了,這樣做有什麼好處呢?

其實大部分時候都是讀操作,所以使用讀鎖可以讓大量的線程同時來讀數據,不需要阻塞不需要排隊,保證高併發讀的性能是比較高的。

然後少量的時候是有服務上線要註冊數據,寫數據的場景是比較少的

此時寫數據的時候,只能一個一個的加寫鎖然後寫數據,同時寫數據的時候就不允許別人來讀數據了。

因此讀寫鎖非常適合這種讀多寫少的場景的。

另外,我們能不能儘量在寫數據期間還保證可以繼續讀數據呢?大量加讀鎖的時候,會阻塞人家寫數據加寫鎖過長時間,這種情況能否避免呢?

可以的,採用多級緩存的機制,具體可以參加之前的一篇文章:【雙11狂歡的背後】微服務註冊中心是如何承載大型系統千萬級訪問的》。裡面分析了Spring Cloud Eureka微服務註冊中心裡的多級緩存機制。

最後看一下,上面那段偽代碼如果用讀寫鎖來優化是怎麼樣的?

什麼是讀寫鎖?微服務註冊中心是如何進行讀寫鎖優化的?


分享到:


相關文章: