gen_tcp:unrecv是个未公开的函数,作用是往tcp的接收缓冲区里面填入指定的数据。别看这小小的函数,用起来很舒服的。
我们先看下它的代码实现,Erlang代码部分:
%%gen_tcp.erl:L299
unrecv(S, Data) when is_port(S) ->
case inet_db:lookup_socket(S) of
{ok, Mod} ->
Mod:unrecv(S, Data);
Error ->
Error
end.
%%inet_tcp.erl:L58
unrecv(Socket, Data) -> prim_inet:unrecv(Socket, Data).
%%prim_inet.erl:L983
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% UNRECV(insock(), data) -> ok | {error, Reason}
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
unrecv(S, Data) ->
case ctl_cmd(S, ?TCP_REQ_UNRECV, Data) of
{ok, _} -> ok;
Error -> Error
end.
运行期c代码部分:
Read more…
最近有同学在gmail上问关于gen_tcp发送进程被挂起的问题,问题描述的非常好,见底下:
第一个问题是关于port_command和gen_tcp:send的。从项目上线至今,我在tcp发送的地方遇到过两次问题,都跟port_command有关系。
起初程序的性能不好,我从各方面尝试分析和优化,还有部分是靠猜测,当初把全服广播消息的地方,换成了port_command,当时参考了hotwheels的代码和您的一遍相关博文。
根据您的分析,port_command应该比直接用gen_tcp:send高效的,并且没有阻塞。但是我却在这个地方遇到了阻塞,具体表现如下(两次,分别出现在项目不同阶段,下面分别描述)
项目上线初期:
当时玩家进程给玩家发消息用的是gen_tcp:send,广播进程为了高效率用了port_command。当活跃玩家到了一定数量以后,玩家无法进入游戏,分析原因,是全局发送广播消息的进程堵住了,从message_queue_len可以看出来,改为广播进程给玩家进程发消息再让玩家进程给玩家自己发消息后,状况排除。
最近一段时间:
这时候玩家进程的tcp发送数据,已经被我替换成了port_command并运行了一段时间都没问题。但是一些流量比较大的游戏服,活跃玩家到了一定数量以后,消息延迟很大(5-6秒),做任何操作都卡,在出现状况期间,服务器CPU、内存、负载各项指标并未异常,ssh连到服务器操作也很正常,没有任何卡顿现象。同服务器的其它游戏服也都正常,但是出问题的游戏服的整个erlang节点都进入一个“很卡”的状态,体现在我进入erlang shell中进行操作时,输入文字延迟很大。
起初我没怀疑过port_command有问题,所以我到处找原因和“优化”代码,这个优化是加了引号的。
但是最后,在一次服务器同样出现状况很卡的时候,我把tcp发送数据的代码改回了gen_tcp:send,并热更新了相关模块,服务器立即恢复正常。
我一直对上面的情况百思不得其解,我之前写的代码如下:
tcp_send (Socket, Bin) ->
try erlang:port_command(Socket, Bin, [force, nosuspend]) of
false ->
exit({game_tcp_send_error, busy});
true ->
true
catch
error : Error ->
exit({game_tcp_send_error, {error, einval, Error}})
end.
希望您能帮忙分析下是什么原因导致整个erlang节点都卡的,我想这对其他的erlang程序员也会有帮助!
关于这个问题我之前写了篇文章,系统的介绍了gen_tcp的行为,gen_tcp:send的深度解刨和使用指南(初稿)见 这里
Read more…
Erlang集群二个节点之间的通讯是通过一个tcp长连接进行的,而且是全联通的,一旦cookie论证通过了,任何一个节点就获得全集群的访问权,可以参考Erlang分布的核心技术浅析
。erlang的这个授权模式特定搞的这么简单,但是在实际使用中还是有安全性的问题。我们退而求其次,来个IP网段限制,这个功能Erlang是有的只是没有文档化。
Read more…
Erlang的集群是由各个节点组成的,一个节点有一个名字来标识,而不管这个节点在网络的物理位置,所以在部署Erlang集群的时候就很方便。只要在集群里新启动一个节点,给个相对固定的引导的节点,让新节点和这个引导节点取得联系,由引导节点把新节点介绍入集群就OK了。
在实践中,新采购的机器上通常配置好IP,以及ssh访问权限。 我们需要在新机器上手工安装Erlang系统,部署新应用,然后启动应用节点,加入集群服务,这个步骤很繁琐。我们希望能够自动化去做这个事情。common_test的ct_系列模块来救助了。
common_test是A framework for automated testing of arbitrary target nodes,它随带的ct_ssh可以透过ssh在远程机器上执行各种各样的shell命令,通过scp传输数据;而ct_slave非常方便的可以连接到远程机器启动一个erlang节点。
pool(www.erlang.org/doc/man/pool.htm)模块也可以远程启动节点,但是它要依赖于操纵系统的ssh工具,需要在机器之间做ssh互信,也就是说ssh targetip这样的不能出现任何的交互,比如说键入密码,很不方便。
我们首先来演示下如何在远程机器上执行ssh命令:
Read more…
前段时间我们在MYSQL调优上发现有瓶颈,怀疑是过多拷贝内存,导致内存带宽用完。在Linux下CPU的使用情况有top工具, IO设备的使用情况有iostat工具,就是没有内存使用情况的测量工具。 我们可以看到大量的memcpy和字符串拷贝(可以用systemtap来测量),但是像简单的数据移动操作就无法统计,我们希望在硬件层面有办法可以查到CPU在过去的一段时间内总共对主存系统发起了多少读写字节数。
所以我们内存测量的的目标就归结为二点:1. 目前我们这样的服务器真正的内存带宽是多少。 2. 我们的应用到底占用了多少带宽。
首先来看下我们的服务器配置情况:
$ sudo ~/aspersa/summary
# Aspersa System Summary Report ##############################
Date | 2011-09-12 11:23:11 UTC (local TZ: CST +0800)
Hostname | my031121.sqa.cm4
Uptime | 13 days, 3:52, 2 users, load average: 0.02, 0.01, 0.00
System | Dell Inc.; PowerEdge R710; vNot Specified (<OUT OF SPEC>)
Service Tag | DHY6S2X
Release | Red Hat Enterprise Linux Server release 5.4 (Tikanga)
Kernel | 2.6.18-164.el5
Architecture | CPU = 64-bit, OS = 64-bit
Threading | NPTL 2.5
Compiler | GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-44).
SELinux | Disabled
# Processor ##################################################
Processors | physical = 2, cores = 12, virtual = 24, hyperthreading = yes
Speeds | 24x2926.089
Models | 24xIntel(R) Xeon(R) CPU X5670 @ 2.93GHz
Caches | 24x12288 KB
# Memory #####################################################
Total | 94.40G
Free | 4.39G
Used | physical = 90.01G, swap = 928.00k, virtual = 90.01G
Buffers | 1.75G
Caches | 7.85G
Used | 78.74G
Swappiness | vm.swappiness = 0
DirtyPolicy | vm.dirty_ratio = 40, vm.dirty_background_ratio = 10
Locator Size Speed Form Factor Type Type Detail
========= ======== ================= ============= ============= ===========
DIMM_A1 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_A2 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_A3 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_A4 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_A5 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_A6 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_B1 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_B2 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_B3 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_B4 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_B5 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_B6 8192 MB 1333 MHz (0.8 ns) DIMM {OUT OF SPEC} Synchronous
DIMM_A7 {EMPTY} Unknown DIMM {OUT OF SPEC} Synchronous
DIMM_A8 {EMPTY} Unknown DIMM {OUT OF SPEC} Synchronous
DIMM_A9 {EMPTY} Unknown DIMM {OUT OF SPEC} Synchronous
DIMM_B7 {EMPTY} Unknown DIMM {OUT OF SPEC} Synchronous
DIMM_B8 {EMPTY} Unknown DIMM {OUT OF SPEC} Synchronous
DIMM_B9 {EMPTY} Unknown DIMM {OUT OF SPEC} Synchronous
...
DELL R710的机器上有2个X5670CPU,每个上面有6个core,超线程,所以共有24个逻辑CPU。上面插了12根 8192MB(1333 MHz)内存条。
我们的机器架构从之前的FSB总线结构变成现在的numa架构,谢谢@fcicq提供的信息,请参考下图(来源):
我们可以清楚的看到每个CPU都有自己的内存控制器直接连接到内存去,而且有3个通道, CPU直接通过QPI连接。 内存控制器和QPI上面都会流动数据。
Read more…
fio是个非常好用的io压力模拟工具,功能非常齐全, 有兴趣的同学参看 这里。
这里我用fio模拟我们线上mysql服务器的压力来为厂家送来的pci-ssd卡做压力测试,底下是脚本(已经测试正确),也许有的同学有用。
Read more…
今天有同学在gmail里面问了一个Erlang的问题,问题描述的非常好, 如下:
问题的背景是:
1、我开发了一个服务端程序,接收客户端的连接。同一时刻会有多个客户端来连接,连接后,接收客户端请求后,再发送响应消息,然后客户端主动断连。
2、服务端监听的socket属性设置如下:
[binary, {packet, raw},
{ip, IPAddr}, {backlog, 10000},
{active, false}, {reuseaddr, true},
{nodelay, false}, {delay_send, true},
{recbuf, 128 * 1024}, {sndbuf, 64 * 1024}]
3、服务器accept监听socket,接收客户端请求,发送响应消息分别是在3个不同的进程中进行。接收请求和发送响应的进程都是重复使用的,每次重新使用的时候传入一个新accept的socket。
问题的现象是:
1、单个用户发起呼叫的时候,流程是成功的,服务器能正常响应。但是多个用户一起呼,批量跑的时候,跑一段时间后,部分客户端会发现不能接收到服务器返回的响应。从抓包来看,客户端的请求是发送到服务器端了。
2、服务器这边发送响应的进程会收到一条{empty_out_q, #Port<0.25876>}这样的消息,而这条消息并不是我开发的代码产生的。
问题是:
1、为什么发消息的进程会收到{empty_out_q, #Port<0.25876>}这样的消息?
2、收到empty_out_q消息,是不是就说明调用gen_tcp:send发送失败?
3、是不是说设置了delay_send的属性,所以即使send失败,也是异步的,在调用send的时候会马上返回ok,但是后面真的发送失败后,则系统会给调用send方法的进程发送一条{empty_out_q, #Port<0.25876>}这样的消息。
这个问题非常有意思。 Read more…
Recent Comments