zoukankan      html  css  js  c++  java
  • Using Elixir Dynamic Supervisors

    转自: https://blog.smartlogic.io/elixir-dynamic-supervisors/

    I have been working on my side project Grapevine, a community site for text based multiplayer games (MUDs.) The existing games primarily have a telnet interface for playing. This generally involves using a local client or hoping the game provides a web client for you. So I've been working on a general purpose client that any game can use. You can see it in action for my game MidMUD.

    This involves a new web client that uses Phoenix Channels to spin up a backing :gen_tcp process. This process is started inside of a DynamicSupervisor, a new feature introduced in Elixir 1.6. We place the process inside of the supervisor to keep the process alive across page reloads. Lets see how it works!

    Basic Flow

    Supervision Tree

    When a new web client starts they join a channel named play:client. The channel ensures that the game they are trying to connect to exists and starts the backing process.

    {:ok, pid} = WebClient.connect(socket.assigns.user,
      game_id: socket.assigns.game.id,
      host: connection.host,
      port: connection.port,
      channel_pid: socket.channel_pid
    )
    

    This Client process starts itself inside of a DynamicSupervisor as a transientprocess and saves the calling channel PID in order to forward any received data to the front end. This uses the DynamicSupervisor.start_child/2 function.

    def start_client(callback_module, opts) do  
      spec = {Client, [module: callback_module] ++ opts}
      DynamicSupervisor.start_child(__MODULE__, spec)
    end  
    

    Once the Client process is alive it parses the telnet stream and forwards any text directly to the web front end.

    Handling Page Reloads

    The Client process is globally registered with the user's ID and the game's ID as part of the name, {:webclient, {1, 1}}. With this, we can check :global to see if the process already exists before starting a new process. If the process exists we overtake the Client process with our new channel.

    def connect(user, opts) do  
      case :global.whereis_name(pid(user, opts)) do
        :undefined ->
          ClientSupervisor.start_client(__MODULE__, opts ++ [name: {:global, pid(user, opts)}])
    
        pid ->
          set_channel(pid, opts[:channel_pid])
          {:ok, pid}
      end
    end  
    

    set_channel/2 sends a message to the process which sets the internal channel_pid state for forwarding text to the web client.

    Conclusion

    With this setup I'm able to have a place to supervise Client processes and allow for session "stickiness" on page reloads. The DynamicSupervisor will delete the child_spec of the Client process after it terminates with a :normal reason. Otherwise the process will be restarted allowing for minor hiccups in the network.

    All of this code is open source on GitHub, check out the Grapevine repo. I also worked on this during my Elixir live coding stream. I'll be back doing more live coding on that channel every Monday from 12PM-1PM Eastern.

  • 相关阅读:
    mybatis强化(二)Parameters和Result
    operator new 和 new operator
    hdu 1007 Quoit Design 分治求最近点对
    实现一个简单的shared_ptr
    bzoj 3224: Tyvj 1728 普通平衡树 替罪羊树
    bzoj 2648 SJY摆棋子 kd树
    hdu 2966 In case of failure k-d树
    codeforces 713D D. Animals and Puzzle 二分+二维rmq
    bzoj 1188 : [HNOI2007]分裂游戏 sg函数
    bzoj 1912 : [Apio2010]patrol 巡逻 树的直径
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/10413336.html
Copyright © 2011-2022 走看看