0829

1、epoll、poll、select
2、分佈式鎖
3、kakfa
4、並查集
5、有窮狀態機

1、epoll、poll、select

I/O多路複用機制,通過一種機制來監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進行相應的讀寫操作。但select,poll,epoll本質上都是同步I/O,因爲他們都需要在讀寫事件就緒後自己負責進行讀寫,也就是說這個讀寫過程是阻塞的,而異步I/O則無需自己負責進行讀寫,異步I/O的實現會負責把數據從內核拷貝到用戶空間。

select

(1)每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大----每次調用 select 都需要將進程加入到所有監視 socket 的等待隊列,每次喚醒都需要從每個隊列中移除。這裏涉及了兩次遍歷(遍歷監聽的FD_SET)
(3)進程被喚醒後,程序並不知道哪些 socket 收到數據,還需要遍歷一次FD_SET。
(4)select支持的文件描述符數量太小了,默認是1024

poll

同select,一個進程可監控的文件描述符數量理論上無限。

epoll:

epoll既然是對select和poll的改進,就應該能避免上述的三個缺點。那epoll都是怎麼解決的呢?在此之前,我們先看一下epoll和select和poll的調用接口上的不同,select和poll都只提供了一個函數——select或者poll函數。而epoll提供了三個函數,epoll_create,epoll_ctl和epoll_wait,epoll_create是創建一個epoll句柄;epoll_ctl是註冊要監聽的事件類型;epoll_wait則是等待事件的產生。
  對於第一個缺點,epoll的解決方案在epoll_ctl函數中。每次註冊新的事件到epoll句柄中時(在epoll_ctl中指定EPOLL_CTL_ADD),會把所有的fd拷貝進內核,而不是在epoll_wait的時候重複拷貝。epoll保證了每個fd在整個過程中只會拷貝一次,因爲epoll是通過內核於用戶空間mmap同一塊內存實現的
  對於第二個、第三個缺點,epoll的解決方案不像select或poll一樣每次都把current輪流加入fd對應的設備等待隊列中,而只在epoll_ctl時把current掛一遍(這一遍必不可少)併爲每個fd指定一個回調函數,當設備就緒,喚醒等待隊列上的等待者時,就會調用這個回調函數,而這個回調函數會把就緒的fd加入一個就緒鏈表)。epoll_wait的工作實際上就是在這個就緒鏈表是否爲空,不爲空則返回活躍的連接(用戶態只需要遍歷活躍的連接即可)。

對於第四個缺點,epoll沒有這個限制,它所支持的FD上限是最大可以打開文件的數目,在1GB內存的機器上大約是10萬左右,具體數目可以cat /proc/sys/fs/file-max察看,一般來說這個數目和系統內存關係很大。

2、分佈式鎖

基於數據庫

方法一

加鎖:insert into methodLock(method_name,desc) values (‘method_name’,‘desc’);
解鎖:delete from methodLock where method_name =‘method_name’;

方法二
public boolean lock(){
        connection.setAutoCommit(false)
        while(true){
            try{
                result = select * from methodLock where method_name=xxx for update;
                if(result==null){
                    return true;
                }
            }catch(Exception e){
     
            }
            sleep(1000);
        }
        return false;
    }
public void unlock(){
    connection.commit();
}

優點:基於數據自身的鎖,易於理解
缺點:併發量較低,擴展較麻煩
1、這把鎖強依賴數據庫的可用性,數據庫是一個單點,一旦數據庫掛掉,會導致業務系統不可用。
2、這把鎖沒有失效時間,一旦解鎖操作失敗,就會導致鎖記錄一直在數據庫中,其他線程無法再獲得到鎖。
3、這把鎖是非重入的,同一個線程在沒有釋放鎖之前無法再次獲得該鎖。因爲數據中數據已經存在了。

基於Redis

方法一

①加鎖:SET my_key 隨機值 NX PX milliseconds
②若加鎖失敗,則無法set
③san釋放鎖:判斷value(隨機值)相同,再使用lua腳本刪除。(判斷和刪除非原子性)

方法二-RedLock

基於Redis集羣—可重入的
①在每個master節點創建鎖,若(n/2+1)大部分創建成功,且建立鎖的時間未超時,則建立成功。

基於zookeeper

實現相對簡單、可靠性強、使用臨時節點、失效時間容易控制
臨時節點:客戶端可以建立一個臨時節點,在會話結束或者會話超時後,zookeeper會自動刪除該節點。
①客戶端連接zookeeper,並在/lock下創建臨時有序節點。
②客戶端獲取/lock下的子節點列表,判斷自己是否序號最小,若是,則獲取鎖;若否,則監聽自己前一位的子節點;
若監聽到刪除消息,重複此步驟
③執行業務代碼
④完成之後,刪除對應子節點釋放鎖

比較

  • zookeeper分佈式鎖實現簡單,集羣自己來保證數據一致性,但是會存在建立無用節點且多節點之間需要同步數據的問題,因此一般適合於併發量小的場景使用,例如定時任務的運行等。
  • redis分佈式鎖(非redlock)由於redis自己的高性能原因,因此適用於高併發的場景。
  • database分佈式鎖由於數據庫本身的限制:性能不高且不滿足高可用(即是存在備份,也會導致數據不一致)

3、kakfa
4、並查集
5、有窮狀態機

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章