千家信息网

Ranch模块的作用是什么

发表于:2024-11-30 作者:千家信息网编辑
千家信息网最后更新 2024年11月30日,Ranch模块的作用是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。ranch_listener_sup模块介绍此模
千家信息网最后更新 2024年11月30日Ranch模块的作用是什么

Ranch模块的作用是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

ranch_listener_sup模块

介绍

此模块是ranch调用的一个模块,用来处理所有的监听和网络连接中心。
该模块的创建时由ranch:start_listener函数启动的基于监督者进程ranch_sup的子进程

功能函数介绍

  • erlang的map结构 #{},#{name =>"test"} 注意区分两种表达式: =>(可以用来更新映射和创建新的映射) :=(只能更新映射,在键不存在时会抛出异常)

  • start_link:创建supervisor进程,并且将创建这个进程的参数保存到ranch_server中。
    1.Ref 监听者实例别名
    2.Transport socket进程的创建者
    3.protocol 实际的功能执行者

  • init:ranch_listener_sup进程创建成功的调用函数。
    主要做了下面的事情:
    1.创建子进程ranch_conns_sup,传入的参数有Ref,Transport,Protocol三个。这个进程的功能是管理所有的外部连接。
    2.创建子进程ranch_acceptors_sup,传入参数是Ref,Transport(可选两种:tcp,ssl).这个进程就是实际的对外端口监听者
    3.将该进程的{Ref,self()(自己的进程pid)}存入到ranch_server
    4.进程是以reset_for_one的方式启动这两个进程,则表明这两个进程是有依赖关系的。如果ranch_conns_sup挂了,则一定也会挂掉ranch_acceptors_sup,然后重新一次重启这两个进程
    5.实际中,ranch_acceptors_sup的启动是依赖ranch_conns_sup进程的,所以,必须ranch_conns_sup进程必须先启动

ranch_conns_sup模块

介绍

接受socket连接的进程,创建Protocol进程,并且监控这些进程的退出

功能函数介绍

  • start_link:这个进程必须的,因为该进程是以supervisor创建的子进程,必须有此函数。
    该进程不是gen_server的标准,而是采用原始的proc_lib:start_link创建的进程,传入的参数包括 父进程id,Ref,Transport,Protocol

  • init:通过start_link函数调用本模块的init函数。
    该函数做了一下的功能:
    1.设置process_flag(trap_exit,true),标识是可以接受link模块的退出信息
    2.设置{Ref,self()}到ranch_server保存数据
    3.从ranch_server和TransOpts中获取相应的初始化信息,并保存到loop循环的#state中
    4.proc_lib:init_ack(Parent, {ok, self()}) 这是是内部的proc_lib的实现,标识告诉父进程,进程创建成功了,并会返回给父进程一个{ok,Pid(当前创建进程的pid)}

  • loop:执行主要的逻辑
    1.{?MODULE,start_protocol, To, Socket}:socket收到连接,发送该进程,创建Protocol进程。在此进程创建的时候需要注意,实例进程创建进程的返回值一定要注意,{ok, Pid}表示自己对自己进程关闭负责,而{ok,SupPid,ProtocolPid}则表示,该进程是有一个conns_sup进程来管理这些启动的进程。此处ranch是提供了两种方法来处理。
    2.{?MODULE,active_connections,To,Tag}:获取该进程激活的连接数
    3.{remove_connection, Ref,Pid}:移除连接,此处注意,当前只是计数减一,并没有真正的关闭进程 4.{set_max_conns}:重新设置最大的连接数,如果sleeper里面有数据,则会将这些链接重新启用 5.{'EXIT',Parent,Reason}:此处Parent指的是ranch_listener_sup进程,如果父进程关闭,则无条件的关闭启动的所有连接进程
    6.{'EXIT',Pid,Reason}:link的进程关闭了,则清理pid.此处要注意,该进程link的pid不能设置process_flag(trap_exit,true),负责就不会收到此断开信息。如果有睡眠的连接进程等待,则激活这些连接进程。
    7.{system,From,Request}:调用系统的指令.gen_server的terminate,code_change函数都和此有关系。目前看到的支持3中大类,1.suspended 挂起 2.running 获取一些模块的信息 3.terminating 关闭进程
    8.{'$gen_call', {To, Tag}, Request(which_children,count_children)}:To:标识是From,即发起者的进程pid,Tag=erlang:monitor(process,Process)即发起者监听接受方的MRef信息,在收到信息后,取消monitor,并返回信息。并且此种请求时通过同步的方式请求。具体的调用方法是:gen_server:call(SupPid,which_children)的方式请求

  • start_protocol:此方法由socket进程发起,同步创建实例,执行loop的第一种情况

  • active_connections:同步请求活跃的连接数,和loop的第8中情况相似,也可以已第8种情况替代

  • handshake:此方法大调用于loop的第1中情况,将启动的实例进程pid和socket进行绑定。并且验证,启动实例。如果当前的连接数已满,则阻塞住socket进程。如果条件满足,则确认绑定成功。

  • terminate(#stat{shutdown=brutal_kill},Reason,):关闭此进程,此函数是有system触发的,system_terminate()函数调用terminate,直接结束。brutal_kill标识是直接kill掉。

  • terminate(#state{shutdown=integer()}):表示是非直接关闭,等待进程的自我了断。

  • kill_children:直接杀死进程,杀死前unlinke(Pid),避免收到进程杀死的信息

  • shutdown_children,wait_children:这两个函数时相互配合使用的,在exit(P,shutdown)成功时,会收到wait_children的{'DOWN,,process,Pid,'}的信息,显示的知道有多少个进程被中断。

  • system_continue,system_terminate,system_code_change:这三个函数,是配合system的命令在sys模块默认调用的,目前来看,主要有3个功能,system_continue:用来获取一些进程的信息;system_terminate:用来系统中断进程;system_code_change:用来进程一些数据的更新处理

  • report_error:对错误写日志记录

ranch_acceptors_sup 连接接受监督者模块

介绍

设置监听socket,将此socket同时分发给ranch_accpetor 接受者,监听连接

函数分析

  • start_link:启动进程的入口,使用supervisor的模式启动进程

  • init:1.获取连接监督者,获取Transport的配置信息,更具配置的监听连接数,通过one_for_one的方式创建一定数量的监听连接进程,来监听外部连接

  • recevie 的机制

%% 通过设置receive after 0,优先处理{alarm,X}的消息。因为在超时时间为0的receive中,会立即触发一个超时,但是在此之前,系统会尝试对邮箱进行模式匹配,所以此方法,可以对消息优先处理,可以用于清空邮箱中的所有消息。1.优先匹配例子priority_receive()->      receive      {alarm,X}->    after 0->            receive        Any->         Any      end    end.2.清空邮箱所有消息flush(Logger) ->        receive Msg ->                ranch:log(warning,                        "Ranch acceptor received unexpected message: ~p~n",                        [Msg], Logger),                flush(Logger)        after 0 ->                ok        end.
  • 拓展:为什么在call一个进程时,timeout了,但是call 的进程也会执行完消息处理?

节选自gen.erldo_call(Process, Label, Request, Timeout) ->    try erlang:monitor(process, Process) of        Mref ->            %% If the monitor/2 call failed to set up a connection to a            %% remote node, we don't want the '!' operator to attempt            %% to set up the connection again. (If the monitor/2 call            %% failed due to an expired timeout, '!' too would probably            %% have to wait for the timeout to expire.) Therefore,            %% use erlang:send/3 with the 'noconnect' option so that it            %% will fail immediately if there is no connection to the            %% remote node.            catch erlang:send(Process, {Label, {self(), Mref}, Request},                  [noconnect]),            receive                {Mref, Reply} ->                    erlang:demonitor(Mref, [flush]),                    {ok, Reply};                {'DOWN', Mref, _, _, noconnection} ->                    Node = get_node(Process),                    exit({nodedown, Node});                {'DOWN', Mref, _, _, Reason} ->                    exit(Reason)            after Timeout ->                    erlang:demonitor(Mref, [flush]),                    exit(timeout)            end    catch        error:_ ->            %% Node (C/Java?) is not supporting the monitor.            %% The other possible case -- this node is not distributed            %% -- should have been handled earlier.            %% Do the best possible with monitor_node/2.            %% This code may hang indefinitely if the Process             %% does not exist. It is only used for featureweak remote nodes.            Node = get_node(Process),            monitor_node(Node, true),            receive                {nodedown, Node} ->                     monitor_node(Node, false),                    exit({nodedown, Node})            after 0 ->                     Tag = make_ref(),                    Process ! {Label, {self(), Tag}, Request},                    wait_resp(Node, Tag, Timeout)            end    end.

从上面代码中可以看到,是在本地进程monitor了被call的进程,并且erlang:send的方式发送信息,此时再receive一个阻塞等待结果返回,然后再TimeOut的时候,如果还没有返回,则返回给进程exit(timeout)的错误信息。所以,就会出现,虽然timeout了,被调的进程还是会处理完,而不能当作被调进程没有收到处理。
如何避免?:1,从源头避免,确保call的进程处理信息足够简单,不超时 2.采用cast,或 ! 替代处理
参考网址:Erlang中带超时的receive

ranch_acceptor模块

简介

此模块用来接收外部的socket连接。并通知ranch_conns_sup通知业务进程启动并且绑定到该socket进程

函数介绍

  • start_link:次数采用原始的spawn_link函数启动一个进程,返回{ok, Pid}

  • loop:1.通过Transport:accept阻塞进程,直到接收到一个连接进来,然后绑定该连接的socket先到connsSup上,然后调用ranch_conns_sup:start_protocol启动一个实例进程,进行绑定。
    {ok, CSocket}:标识连接成功,并且创建了一个连接的socket,进行启动和绑定操作
    {error,emfile}:大量的并发操作调用,导致操作系统的文件描述符数量被瞬间用完,抛出emfile.此处应为会同时创建多个连接进程,和多个外部的socket连接,则必然会出现此种情况。在晚上看到过,有人在使用了1024个连接,https之后,就会出现问题,导致连接不成功。贴上网址如下:http://erlang.org/pipermail/erlang-questions/2015-January/082446.html
    {error,closed}:表明listening socket 关闭了
    ?MODULE:loop(_):此方法是可以保证,在版本更新时,总是调用到最新的模块函数

  • flush:每一次循环是,都清空掉该进程接收到的其他非accepotr消息

  • 关于连接出现{error,emfile}的问题:
    1.此错误标识同时创建了多个链接进程,导致操作系统的文件描述符数量被瞬间用完导致。
    2.有两种I/O处理,一个异步和一个同步。异步I/O下,这个会比较容易实现,需要给予一定的过载保护,防止过分压榨底层系统的性能。
    3.两种方式:1、设置 ulimit -n 10480 2.修改 /etc/security/limit.conf文件的句柄数量

        limit.conf     #                     #    #*               soft    core            0    #*               hard    rss             10000    #@student        hard    nproc           20    #@faculty        soft    nproc           20    #@faculty        hard    nproc           50    #ftp             hard    nproc           0    #@student        -       maxlogins       4    * soft nproc 65535    * hard nproc 65535    * soft nofile 65535    * hard nofile 65535    # End of file


4.参考网址:
[erlang-questions] {error,emfile}
从EMFILE和ENFILE说起,fd limit的问题(一)
EMFILE,too many open files的解决方案

此处记录问题:

  • 1.如果设置cert密匙,客户端是如何发送的,并且服务器又是如何验证的

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

0