gen_server port 调用receive_match 问题
问题由来
前些天对系统做了一个优化,将原来从queue 轮询刷出数据后每条消息一个 spawn 进程单独处理,修改为批量刷出。一条一条刷轮询刷 queue 存在问题:刷queue 进程太多时,占用CPU,刷queue 少时容易受网络延时影响。修改后,queue 中数据越多,batch 数越大,提高刷queue速度。
但问题随之而来:erlang节点,进程数蹭蹭往上涨,CPU 占用很低,直到 outofmemory, 节点remsh 无法连接。
问题排查
通过kill -SIGUSR1 PID 创建crashdump 发现,我们使用的一个一致性hash gen_server 里面的message_queue_len 30w+,堵塞。
存在问题代码: https://github.com/chrismoos/hash-ring 使用计算一致性hash 节点的gen_server
猜想原因:
1. hash_ring 效率低?测试单程 30w/s +, 排除
2. 进程优先级不高
erlang 所有进程优先级一样,平等执行,大量进程情况下如果CPU较高,热点进程消息往往得不到处理,恶性顺序,将这些进程优先级process_flag(priority, hight) 这个设置曾经帮忙解决过问题,不过这次无效
3. Port 进程优先级
实际业务由Port 完成,Port 调度也是平等的,但是没有找到设置Port优先级的方式
4. 从问题产生代码修改
与原来相比 就是spawn 进程变得一波一波 速度比较快,产生大量进程。也就是 gen_server:call 数量较多,gen_server 和Port 交互的时候往往receive match 方式,看下代码果然。
问题原因分析:
gen_server:call虽然都是同步调用,但是如果有10w 进程都是调用,那么gen_server 的message_queue 将是10w;
gen_server 内部处理消息:
Port ! Msg,
receive
Match -> dostring
end
这个时候receive, Match 操作非常费时,因为Match 需要因为不断新的gen_server:call 对10w 的消息一遍又一遍的遍历匹配执行,能快才怪呢。
解决方案
参考文章:http://wqtn22.iteye.com/blog/1572258
我们选用第二种方法
当然也想过使用gen_server2 处理消息前将message_queue 倒入state 中自己队列,那样消息reply 会更及时一点(Port 的reply会放到队尾,最后处理),不过消耗还是有一些的,handle_call 期间可以有很大消息进来。
测试 并行 spwan 10w 进程,老版本执行2分钟以上,改造后1s内执行完成。
修改如下:https://github.com/chrismoos/hash-ring/pull/11/files
@@ -97,14 +97,16 @@ set_mode(Ring, Mode) when is_integer(Mode) -> |
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
||
-record(state, {
|
||
port, |
||
- rings |
||
+ rings, |
||
+ queue |
||
}). |
||
init(Drv) -> |
||
Port = open_port({spawn, Drv}, [binary]),
|
||
{ok, #state{
|
||
port = Port, |
||
- rings = dict:new() }}. |
||
+ rings = dict:new(), |
||
+ queue = queue:new() }}. |
||
@@ -160,21 +162,12 @@ handle_call({remove_node, {Ring, Node}}, _From, #state{ port = Port, rings = Rin
|
||
{reply, {error, ring_not_found}, State}
|
||
end; |
||
-handle_call({find_node, {Ring, Key}}, _From, #state{ port = Port, rings = Rings } = State) ->
|
||
+handle_call({find_node, {Ring, Key}}, From, #state{ port = Port, rings = Rings, queue = Queue } = State) ->
|
||
case dict:find(Ring, Rings) of |
||
{ok, Index} ->
|
||
KeySize = size(Key), |
||
Port ! {self(), {command, <<5:8, Index:32, KeySize:32, Key/binary>>}},
|
||
- receive |
||
- {Port, {data, <<3:8>>}} ->
|
||
- {reply, {error, invalid_ring}, State};
|
||
- {Port, {data, <<2:8>>}} ->
|
||
- {reply, {error, node_not_found}, State};
|
||
- {Port, {data, <<1:8>>}} ->
|
||
- {reply, {error, unknown_error}, State};
|
||
- {Port, {data, <<Node/binary>>}} ->
|
||
- {reply, {ok, Node}, State}
|
||
- end; |
||
+ {noreply, State#state{queue = queue:in(From, Queue)}};
|
||
_ -> |
||
{reply, {error, ring_not_found}, State}
|
||
end; |
||
@@ -198,6 +191,21 @@ handle_call(stop, _From, State) -> |
||
handle_cast(_, State) -> |
||
{noreply, State}.
|
||
+handle_info({Port, {data, Data}}, #state{port = Port, queue = Queue} = State) ->
|
||
+ R = |
||
+ case Data of |
||
+ <<3:8>> -> |
||
+ {error, invalid_ring};
|
||
+ <<2:8>> -> |
||
+ {error, node_not_found};
|
||
+ <<1:8>> -> |
||
+ {error, unknown_error};
|
||
+ <<Node/binary>> -> |
||
+ {ok, Node}
|
||
+ end, |
||
+ {{value, Pid}, QTail} = queue:out(Queue),
|
||
+ safe_reply(Pid, R), |
||
+ {noreply, State#state{queue = QTail}};
|
||
handle_info({'EXIT', Port, _Reason} = PortExit, #state{ port = Port } = State ) ->
|
||
{stop, PortExit, State}.
|
||
@@ -214,6 +222,10 @@ terminate(_, #state{ port = Port }) ->
|
||
code_change(_OldVsn, State, _Extra) -> |
||
{ok, State}.
|
||
+safe_reply(undefined, _Value) -> |
||
+ ok; |
||
+safe_reply(From, Value) -> |
||
+ gen_server:reply(From, Value). |
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
||
%% Tests |
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
||
gen_server port 调用receive_match 问题的更多相关文章
- erlang 健壮性
erlang 提供了简单易用的并发编程模型,基本不需要再考虑多线程并发问题.但实际应用中并不是那么的完美,很多地方需要注意,就算标准库也有不少问题.很多在多线程编程中很多很容易解决的事情,在erlan ...
- Gen_server行为分析与实践
1.简介 Gen_server实现了通用服务器client_server原理,几个不同的客户端去分享服务端管理的资源(如图),gen_server提供标准的接口函数和包含追踪功能以及错误报告来实现通用 ...
- 如何设置gen_server在退出时执行相关操作
如果gen_server在监控树中不需要stop函数,gen_server会由其supervisor根据shutdown策略自动终止掉.如果要在进程终止之前执行清理,shutdown策略必须设定一个t ...
- Erlang模块gen_server翻译
gen_server 概要: 通用服务器行为描述: 行为模块实现服务器的客户端-服务器关系.一个通用的服务器进程使用这个模块将实现一组标准的接口功能,包括跟踪和错误报告功能.它也符合OTP进程监控树. ...
- Erlang generic standard behaviours -- gen_server terminate
gen_server 主体 module 已经分析完了(http://www.cnblogs.com/--00/p/4271982.html),接着,分析下gen_server 中的terminate ...
- gen_server的enter_loop分析
http://my.oschina.net/astute/blog/119250?p=1 在看ranch user guide的过程中,发现实现protocol handler需要使用特殊的gen_s ...
- Erlang/OTP设计原则(文档翻译)
http://erlang.org/doc/design_principles/des_princ.html 图和代码皆源自以上链接中Erlang官方文档,翻译时的版本为20.1. 这个设计原则,其实 ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- [Erlang 0104] 当Erlang遇到Solr
Joe Armstrong的访谈中有一段关于"打开黑盒子"的阐述,给我留下很深的印象:Joe Armstrong在做XWindows开发时没有使用对应的类库,而是在了解XW ...
随机推荐
- SD卡的监听
摘要:在一般应用中,如果需要对占用空间比较大的文件操作,需要监听SD卡的状态,Android中对SD卡的监听状态操作步骤如下: 一.创建一个类继承于BroadcastReceiver public c ...
- Python NaN
NaN, Not a Number, 非数. 它即不是无穷大, 也不是无穷小, 而是python/numpy/... 觉得无法计算时返回的一个符号(自己的推测, 未考证(TODO)). import ...
- Sqlserver内置函数实现MD5
16位: SELECT substring(sys.fn_sqlvarbasetostr(HashBytes('MD5', '需要加密字符串')),3,16) 32位 SELECT substring ...
- python笔记
# coding=utf-8 定义后可使用中文#var定义输出i=1print(i)#ifif i>0: print(i)else: print("x")#循环+字符串拼接f ...
- sublime 插件推荐: Nettuts+ Fetch
Nettuts+ Fetch github地址:Nettuts-Fetch 在sublime中直接用 ctrl+shift+P -> pci -> Nettuts-Fetch 即可下载 这 ...
- 终端指令操作创建Django项目
需求:通过Django创建一个用户表和权限表. 用户表包括:用户名,邮箱,密码,管理权限. 权限表包括:普通用户,管理用户,超级用户. 权限表和用户表有一对多的关系,即用户表中的每条数据对应权限表中的 ...
- AngularJS +HTML Demo
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
- CGI, FastCGI, WSGI, uWSGI, uwsgi简述
CGI 通用网关接口(Common Gateway Interface/CGI)是一种重要的互联网技术,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据.CGI描述了服务器和请求处理程 ...
- 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(二)
Log4j 这个东西,大家都熟悉,就简单的介绍一下,算是一个抛砖引玉,因为我自己在Log日志的搭建方面,没有什么经验,但这东西确实是非常重要的,日后调Bug没有它基本不可能,如果有朋友有什么比较好的L ...
- PostgreSQL
PostgreSQL新手入门 作者: 阮一峰 日期: 2013年12月22日 自从MySQL被Oracle收购以后,PostgreSQL逐渐成为开源关系型数据库的首选. 本文介绍PostgreSQ ...