Ranch模块的作用是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:申请域名、雅安服务器托管、营销软件、网站建设、富蕴网站维护、网站推广。
此模块是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进程必须先启动
接受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:对错误写日志记录
设置监听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.erl do_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
此模块用来接收外部的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密匙,客户端是如何发送的,并且服务器又是如何验证的
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注创新互联行业资讯频道,感谢您对创新互联的支持。