PO Box简介
使用Erlang写程序的时候,经常会碰到一种情况:因为Erlang进程的mailbox是没有大小限制的,所以它会一直接受消息,直到Erlang节点内存溢出。在大多数情况下,我们可以通过限制消息生产者的频率来解决这个问题,而且也应该实现这一点。但是,有时候不太可能完全限制发给一个进程的所有消息,这时候,开发人员就需要通过丢弃消息来减轻负载。
PO Box就是一个可以减轻负载的工具。
设计原则
PO Box是一个实现了消息缓冲区的功能的库。因为Erlang进程需要同时接收消息并做自身的工作,所以很有可能因为消息过多,而使内存消耗急剧上升。
这是Erlang现在实现的消息处理逻辑,所有的消息都是放在mailbox中的,当一条消息处理卡顿时,会导致后面的消息一直在排队,消耗内存。
messages
|
V
+-----[Pid or Name]-----+
| | | |
| | mailbox | |
| +---------+ |
| | |
| receive |
+-----------------------+
PO Box实现了一个消息代理:
messages
|
V
+---------[Pid]---------+ +--------[POBox]--------+
| |<-- got mail ---| | | |
| | | | mailbox | |
| <important stuff> |--- send it! -->| +---------+ |
| | | | |
| |<---<messages>--|<---buffer |
+-----------------------+ +-----------------------+
可以将一个PO Box进程看成是一个消息代理,它通过实现一个缓冲区,对消息进行缓存,并在缓冲区满了的时候丢弃消息。PO Box可以通知你有新的数据,这样你可以请求这些数据;或者你可以通知它直接把数据发给你,不用通知
更具体一些,就是PO Box是一个有owner进程的状态机,共有3种状态:
- Active
- Notify
- Passive
Passive状态基本上除了接收消息保存在缓冲区和必要时丢弃消息,不做什么事。
用户可以通过调用PO Box的api来进入Notify状态。该状态的唯一任务是检查缓冲区内有没有消息。如果有,它会发一个{mail, new_data}
给owner进程。如果没有,PO Box会一直在notify状态等待,直到有新消息。在发送通知后,PO Box会返回passive状态。
只有active状态可以将真实数据发给owner进程。用户同样可以通过调用PO Box的api告知它进入active状态。如果缓冲区内有消息,所有的消息会以一个list的状态发给owner进程。如果没有消息,PO Box会等待直到有消息。转发完消息后,PO Box会回到passive状态。
,---->[passive]------(user makes active)----->[active]
| | ^ | ^ |
| | '---(sends message to user)--<-----' | |
| (user makes notify) | |
| | | |
(user is notified) | | |
| V | |
'-----[notify]---------(user makes active)--------' |
^----------(user makes notify)<----------'
缓存类型
PO Box实现了消息的缓存机制,当前支持的缓存方式包括三种:queue
、stack
和keep_old queue
。
queue
按消息到达的顺序保存消息,当缓存满时,会丢弃最老的消息。例如,有6条消息,a,b,c,d,e,消息缓存的大小是3,最后会保留的消息是[c, d, e]。
keep_old queue
也是一种queue
,不过当缓存满时,会阻止新的消息的到达。例如,有6条消息,a,b,c,d,e,缓存大小是3,最后会保留的消息是[a, b, c]。
stack
并不能保证消息的顺序呢。当缓存满时,会丢弃栈顶的消息。对于前面两个例子,stack
最后保存的消息是[e, b, a]。
当考虑采取哪一种缓存类型时,要关注的地方是:
- 是否需要消息保持有序
- 是保留最新到达的消息,还是最先到达的消息
- 对时间上有没有要求?如果要求最低的时间延迟,选择stack。
当然,也可以自己开发自己想要的缓存类型。
使用用例
PO Box进程启动函数:
start_link(OwnerPid, MaxSize, BufferType)
start_link(OwnerPid, MaxSize, BufferType, InitialState)
start_link(Name, OwnerPid, MaxSize, BufferType)
start_link(Name, OwnerPid, MaxSize, BufferType, InitialState)
Name
就是Po Box进程注册的名字OwnerPid
就是PO Box的owner进程的Pid。只有owner进程可以读取该PO Box进程的消息,也只有这个owner进程可以设置PO Box进程的state。OwnerPid
也可以是原子。两个进程之间会建立link关系,PO Box 不会trap exits。所以如果想要PO Box进程独立存活,应该手动取消link。MaxSize
就是缓冲区的大小BufferType
就是上面所说缓存类型InitialState
可以是passive
或者notify
。缺省是notify
。
将PO Box转入active
状态
pobox:active(BoxPid, FilterFun, FilterState)
FilterFun
就是消息的读取过滤函数,返回值如下:
ok, Message, NewState
这条消息会被发送到owner进程{drop, NewState}
这条消息会被丢弃skip
这条消息会被留在缓冲区,之前被遍历过的会被发送
消息发送的格式是:
{mail, BoxPid, Messages, MessageCount, MessageDropCount}
转换成notify状态:
pobox:notify(BoxPid)
发送消息:
pobox:post(BoxPid, Msg)
或者
BoxPid ! {post, Msg}
注意
FilterFun
必须是轻量级,尤其是在处理消息到达速度非常快的时候。因为发送到PO Box进程的消息还是会先保存在Po Box自己的Mailbox中.- 一个进程可以有多个PO Box进程
- 可以看到,如果使用
keep_old queue
类型,一次处理一条消息,等价于拥有一个受限制的mailbox。
FilterFun/2
FilterFun/2的两个参数是message和state。
会把所有消息都发送到owner进程的函数可以这样写
fun(Msg, _ ) -> {{ok, Msg}, nostate}
end.
限制二进制消息大小的函数可以这样写:
fun(Msg, Allowed) ->
case Allowed - byte_size(Msg) of
N when N < 0 -> skip;
N -> {{ok, Msg}, N}
end
end
丢掉空消息:
fun(<<>>, State) -> {drop, State};
(Msg, State) -> {{ok, Msg}, State}
end.
只读取一条消息:
fun(Msg, 0) -> {{ok, Msg}, 1};
(_, _) -> skip
end.
PO Box简介的更多相关文章
- Linux监控工具介绍系列——OSWatcher Black Box
OSWatcher Balck Box简介 OSWatcher Black Box (oswbb)是Oracle开发.提供的一个小巧,但是实用.强大的系统工具,它可以用来抓取操作系统的性能指标,用 ...
- OSWatcher Black Box
Linux监控工具介绍系列--OSWatcher Black Box OSWatcher Balck Box简介 OSWatcher Black Box (oswbb)是Oracle开发.提供的一个小 ...
- UI自动化测试框架:PO模式+数据驱动
1. PO 设计模式简介 2. 工程结构说明 3. 工程代码实现 page 包 action 包 business_process 包 util 包 conf 包 test_data 目录 log 目 ...
- 二维码名片的格式 - vcard(非常好,可直接添加到手机通讯录)
分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 登录|注册 ...
- 适合于图像处理方向的SCI期刊杂志列表【转】
适合于图像处理方向的SCI期刊杂志列表[转] 表1. 适合于图像处理方向的SCI期刊杂志列表 ISSN 期刊名 出版周期 1057-7149 IEEE TRANSACTIONS ON IMAGE ...
- QT实现单个EXE文件
有时候发布用Qt写的软件是件令人烦恼的事情,明明发布的只是一个简单功能的小软件,非得再附上一堆超大的动态链接库,实在让人觉得汗颜 . 在可执行文件单文件化方面,有多种方法.常用的是编译并使用静态 Qt ...
- 转载 C# 序列化与反序列化意义详解
C# 序列化与反序列化意义详解 总结: ①序列化基本是指把一个对象保存到文件或流中,比如可以把文件序列化以保存到Xml中,或一个磁盘文件中②序列化以某种存储形式使自定义对象持久化: ③将对象从一个地方 ...
- REST API设计指导——译自Microsoft REST API Guidelines(二)
由于文章内容较长,只能拆开发布.翻译的不对之处,请多多指教. 另外:最近团队在做一些技术何架构的研究,视频教程只能争取周末多录制一点,同时预计在下周我们会展开一次直播活动,内容围绕容器技术这块. 所有 ...
- 使用OpenCV通过摄像头捕获实时视频并探测人脸
在Opencv初接触,图片的基本操作这篇手记中,我介绍了一些图片的基本操作,视频可以看作是一帧一帧的图片,因此图片操作其实是视频操作的基础,这篇手记就来讲讲OpenCV中的视频操作,并实现一个用笔记本 ...
随机推荐
- mysql大写和小写问题
曾经做企业项目的时候,用的都是oracle数据库,在新公司项目用的是mysql,有关mysql大写和小写的问题 1 windows下默认mysql是不区分大写和小写的,要想让其支持大写和小写.更改 ...
- metaq spring
spring metaq spring bean 配置 <bean id="sessionFactory" class="com.taobao.metamorpho ...
- HDU——B-number(数字DP)
标题效果: 要了解1至n如何号码之间有许多含有13,并13可分 记忆化搜索: dp[pos][pre][mod][statu],pos位数,pre前一位,mod余数,statu状态 有2个状态:含13 ...
- Android 设计模式Template Method模式
自定义模板方法模式:定义的算法的骨架中的方法,虽然某些步骤推迟到子类中,下模板方法允许子类不能改变在的情况下,该算法的结构.算法重新定义某些步骤. 设计原则:不要给我们打电话.我会打电话给你.(像猎头 ...
- struts2-dojo-plugin-2.3.1.2.jar!/struts-plugin.xml:29:119
Unable to load configuration. - bean - jar:file:/D:/code_workspace/SSHWorkSpace3/.metadata/.plugins/ ...
- 于XAML导入命名空间的代码
例如,下面的代码到指定的命名空间.不仅导入的命名空间,并且还为指定的命名空间前缀local.当然,你也可以指定一个前缀为另一个名称,这可以定义.导入后,市民可以在命名当前空间XAML使用代码.例如,在 ...
- HDU1069_Monkey and Banana【LCS】
Monkey and Banana Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- NSIS:在注册表中记录安装路径以便重装或升级时读取
原文 NSIS:在注册表中记录安装路径以便重装或升级时读取 在NSIS中,这个功能是非常有用的,可以避免用户把程序安装到多个位置的尴尬. 第1步:在“安装目录选择页面”前面加入以下代码: 1 !def ...
- 百度地图 Android SDK - 个性化地图
什么是百度个性化地图Android SDK? 百度个性化地图Android SDK是一套基于Android 2.2及以上版本号设备的应用程序接口,您能够通过该套接口实现主要的地图功能,而且能够定制地图 ...
- oracle PL/SQL(procedure language/SQL)程序设计之函数+过程+包(转)
匿名PL/SQL块回顾 DECLARE (可选) 定义在PL/SQL块中要使用的对象 BEGIN (必须) 执行语句 EXCEPTION (可选) 错误处理语句 END; (必 ...