erlang 全局名字服務(wù)global_name_server

global_name_server

global_name_serverkernelcode_server,rex后第3個(gè)啟動(dòng)的gen_server

1. 主要用途:

  1. 注冊全局名稱服務(wù) register_name/2,register_name/3
  2. 全局鎖 set_lock/1,set_lock/2,set_lock/3
  3. 保證一個(gè)函數(shù)在加鎖情況下執(zhí)行trans/2,trans/3,trans/4

2. 申明

申明部分見kernel.erl

% kernel.erl
Global = #{id => global_name_server,
          start => {global, start_link, []},
          restart => permanent,
          shutdown => 2000,
          type => worker,
          modules => [global]},

3. global的自旋鎖set_lock過程

% global.erl
set_lock({_ResourceId, _LockRequesterId}, [], _Retries, _Times) ->
  true;
set_lock({_ResourceId, _LockRequesterId} = Id, Nodes, Retries, Times) -> {times, Times}}),
  case set_lock_on_nodes(Id, Nodes) of
    true ->
      ?trace({set_lock_true, Id}),
      true;
    false = Reply when Retries =:= 0 ->
      % 嘗試一段時(shí)間后鉴吹,直接返回結(jié)果
      Reply;
    false ->
      random_sleep(Times),
      % 不斷的進(jìn)行嘗試,自選鎖機(jī)制
      set_lock(Id, Nodes, dec(Retries), Times + 1)
  end.
  
set_lock_on_nodes(_Id, []) ->
  true;
set_lock_on_nodes(Id, Nodes) ->
  case local_lock_check(Id, Nodes) of
    true ->
      Msg = {set_lock, Id},
      % 使用gen_server來保證,鎖成功
      {Replies, _} = gen_server:multi_call(Nodes, global_name_server, Msg),
      ?trace({set_lock, {me, self()}, Id, {nodes, Nodes}, {replies, Replies}}),
      check_replies(Replies, Id, Replies);
    false = Reply ->
      Reply
  end.
  
% set_lock 的實(shí)現(xiàn)函數(shù)
handle_call({set_lock, Lock}, {Pid, _Tag}, S0) ->
    {Reply, S} = handle_set_lock(Lock, Pid, S0),
    {reply, Reply, S};

handle_set_lock(Id, Pid, S) ->
  ?trace({handle_set_lock, Id, Pid}),
  % step1 檢查鎖是否被占
  case can_set_lock(Id) of
    {true, PidRefs} ->
      % step2,檢查是否已經(jīng)鎖了服傍,沒有的話插入鎖
      case pid_is_locking(Pid, PidRefs) of
        true ->
          {true, S};
        false ->
          {true, insert_lock(Id, Pid, PidRefs, S)}
      end;
    false = Reply ->
      {Reply, S}
  end.

can_set_lock({ResourceId, LockRequesterId}) ->
  case ets:lookup(global_locks, ResourceId) of
    [{ResourceId, LockRequesterId, PidRefs}] ->
      % 這是一個(gè)可重入式鎖
      {true, PidRefs};
    [{ResourceId, _LockRequesterId2, _PidRefs}] ->
      false;
    [] ->
      {true, []}
  end.

4. register_name 過程

假設(shè)一個(gè)分布式系統(tǒng)有N個(gè)非hidden節(jié)點(diǎn)(erlang:length(nodes()) = N)

  1. boss節(jié)點(diǎn)加鎖gen_server:multi_call([Boss]], global_name_server, {set_lock,{global,pid()}})
  2. 在所有節(jié)點(diǎn)上加鎖gen_server:multi_call(Nodes, global_name_server, {set_lock,{global,pid()}})
  3. 在所有節(jié)點(diǎn)上注冊 gen_server:multi_call(Nodes,global_name_server,{register, Name, Pid, Method})
  4. 在其余N個(gè)節(jié)點(diǎn)刪除鎖 gen_server:multi_call(Nodes, global_name_server, {del_lock, Id})
  5. boss節(jié)點(diǎn)刪除鎖 gen_server:multi_call([Boss]], global_name_server, {del_lock, Id})

一共要gen_servre:call N x 3(加鎖外遇,注冊洛搀,刪鎖) 次扳抽,效率喜人

% global.erl
%% 函數(shù)入口
trans_all_known(Fun) ->
    Id = {?GLOBAL_RID, self()},
    % step 1 and step 2
    Nodes = set_lock_known(Id, 0),
    try
        % step 3
        Fun(Nodes)
    after
        % step 4 and step 5
        delete_global_lock(Id, Nodes)
    end.

set_lock_known(Id, Times) -> 
    Known = get_known(),
    Nodes = [node() | Known],
    Boss = the_boss(Nodes),
    %% Use the  same convention (a boss) as lock_nodes_safely. Optimization.
    % step 1,先在boss節(jié)點(diǎn)設(shè)置鎖 {?GLOBAL_RID, self()}
    case set_lock_on_nodes(Id, [Boss]) of
        true ->
            % step2,在所有節(jié)點(diǎn)設(shè)置鎖
            case lock_on_known_nodes(Id, Known, Nodes) of
                true ->
                    Nodes;
                false -> 
                    % 萬一不成功斤贰,還得先解除智哀,這下子IO次數(shù)多了去了。
                    del_lock(Id, [Boss]),
                    random_sleep(Times),
                    set_lock_known(Id, Times+1)
            end;
        false ->
            random_sleep(Times),
            set_lock_known(Id, Times+1)
    end.

register_name(Name, Pid, Method0) when is_pid(Pid) ->
    Method = allow_tuple_fun(Method0),
    Fun = fun(Nodes) ->
        % step3,在鎖成功設(shè)置之后荧恍,向所有節(jié)點(diǎn)注冊Name
        case (where(Name) =:= undefined) andalso check_dupname(Name, Pid) of
            true ->
                gen_server:multi_call(Nodes,
                                      global_name_server,
                                      {register, Name, Pid, Method}),
                yes;
            _ ->
                no
        end
    end,
    ?trace({register_name, self(), Name, Pid, Method}),
    gen_server:call(global_name_server, {registrar, Fun}, infinity).
    
delete_global_lock(LockId, Nodes) ->
    TheBoss = the_boss(Nodes),
    % step4 其余節(jié)點(diǎn)刪除鎖
    del_lock(LockId, lists:delete(TheBoss, Nodes)),
    % step5 Boss節(jié)點(diǎn)刪除鎖
    del_lock(LockId, [TheBoss]).
    

5. 優(yōu)缺點(diǎn)

缺點(diǎn)
  1. 不能對hidden節(jié)點(diǎn)進(jìn)行register_name加鎖操作瓷叫。
  2. 自旋鎖對多個(gè)節(jié)點(diǎn)操作,IO次數(shù)太高,不如redis分布式鎖來的輕巧摹菠。
優(yōu)點(diǎn)
  1. 語言層面的實(shí)現(xiàn)盒卸,不用借助第三方
  2. 分布式的存儲(chǔ),所有的節(jié)點(diǎn)上都保留一份辨嗽,所以在讀方面會(huì)占優(yōu)勢

6.總結(jié)

了解系統(tǒng)實(shí)現(xiàn)全局鎖的實(shí)現(xiàn)方式與缺陷世落,在使用當(dāng)中避免一些性能瓶頸。

7. 參考文獻(xiàn)

  1. https://github.com/erlang/otp/blob/master/lib/kernel/src/global.erl
  2. https://github.com/erlang/otp/blob/master/lib/kernel/src/kernel.erl
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糟需,一起剝皮案震驚了整個(gè)濱河市屉佳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洲押,老刑警劉巖武花,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異杈帐,居然都是意外死亡体箕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門挑童,熙熙樓的掌柜王于貴愁眉苦臉地迎上來累铅,“玉大人,你說我怎么就攤上這事站叼⊥奘蓿” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵尽楔,是天一觀的道長投储。 經(jīng)常有香客問我,道長阔馋,這世上最難降的妖魔是什么玛荞? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮呕寝,結(jié)果婚禮上勋眯,老公的妹妹穿的比我還像新娘。我一直安慰自己下梢,他們只是感情好客蹋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著怔球,像睡著了一般嚼酝。 火紅的嫁衣襯著肌膚如雪浮还。 梳的紋絲不亂的頭發(fā)上竟坛,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼担汤。 笑死涎跨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的崭歧。 我是一名探鬼主播隅很,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼率碾!你這毒婦竟也來了叔营?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤所宰,失蹤者是張志新(化名)和其女友劉穎绒尊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仔粥,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡婴谱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躯泰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谭羔。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖麦向,靈堂內(nèi)的尸體忽然破棺而出瘟裸,到底是詐尸還是另有隱情,我是刑警寧澤磕蛇,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布景描,位于F島的核電站,受9級特大地震影響秀撇,放射性物質(zhì)發(fā)生泄漏超棺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一呵燕、第九天 我趴在偏房一處隱蔽的房頂上張望棠绘。 院中可真熱鬧,春花似錦再扭、人聲如沸氧苍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽让虐。三九已至,卻和暖如春罢荡,著一層夾襖步出監(jiān)牢的瞬間赡突,已是汗流浹背对扶。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惭缰,地道東北人浪南。 一個(gè)月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像漱受,于是被迫代替她去往敵國和親络凿。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容