您的位置:首页 > 数据库 > Redis

借助redis主从复制来了解系统的发送和接收缓冲区

2017-12-26 18:41 435 查看


为了能够实现在两个主机之间基于tcp的方式建立通信,在两个主机的内核之间,其tcp中必须建立可靠的连接并进行通信的,以tcp协议为例,为了能够完成通信双方需要基于网络进程通信,通信过程是全双工的。

每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区,TCP的全双工的工作模式以及TCP的流量(拥塞)控制便是依赖于这两个独立的buffer以及buffer的填充状态。接收缓冲区把数据缓存入内核,应用进程一直没有调用recv()进行读取的话,此数据会一直缓存在相应socket的接收缓冲区内。recv()所做的工作,就是把内核缓冲区中的数据拷贝到应用层用户的buffer里面,并返回,仅此而已。进程调用send()发送的数据的时候,是将数据拷贝进入socket的内核发送缓冲区之中,然后send便会在上层返回。

换句话说,send()返回之时,数据不一定会发送到对端去,send()仅仅是把应用层buffer的数据拷贝进socket的内核发送buffer中,发送是TCP的事情,和send其实没有太大关系。接收缓冲区被TCP用来缓存网络上来的数据,一直保存到应用进程读走为止。对于TCP,如果应用进程一直没有读取,接收缓冲区满了之后,发生的动作是:收端通知发端,接收窗口关闭(win=0)。这个便是滑动窗口的实现。保证TCP套接口接收缓冲区不会溢出,从而保证了TCP是可靠传输。因为对方不允许发出超过所通告窗口大小的数据。
这就是TCP的流量控制,如果对方无视窗口大小而发出了超过窗口大小的数据,则接收方TCP将丢弃它。

可以通过如下命令来查看系统的发送缓冲区和接收缓冲区大小(单位是字节)

cat/proc/sys/net/ipv4/tcp_wmem

4096    16384  4194304

为自动调优定义socket使用的内存。第一个值是为socket发送缓冲区分配的最少字节数;第二个值是默认值(该值会被wmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是发送缓冲区空间的最大字节数(该值会被wmem_max覆盖)。

cat/proc/sys/net/ipv4/tcp_rmem

4096    87380  4194304

为自动调优定义socket使用的内存。第一个值是为socket接收缓冲区分配的最少字节数;第二个值是默认值(该值会被rmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是接收缓冲区空间的最大字节数(该值会被rmem_max覆盖)。

Redis主库会把接收到的特定请求,“实时”的同步给从库,我们可以借助redis的主从复制,来观察发送缓冲区和接收缓冲区。

我们先将实例B作为实例A的从库,B向A建立tcp连接

TCP连接建立之时的收端的初始接受窗口大小是14600,B是从库,作为接收端。通知发端(A为主库),我目前的接收能力是14600字节。

此时,我们在B从库上,进行调试,让其sleep 60秒,也就是说,让从库B不要调用recv()方法,把内核缓冲区中的数据拷贝到应用层用户的buffer里面,这样数据就会挤压在内核接收缓冲区当中。

同时,向A主库中开始写入数据,A将请求同步给B,B服务器的接收缓冲区就会被填满

此时,接收端B一共接收了大约1208834(此数不准确,因为我在做实验之前,B先同步了A的全量数据,所以,知道这个ACK的含义即可),全部被压在接收缓冲区之中,win=0接收窗口关闭,接收缓冲区满,无法再接收任何数据。

用ss命令查看B服务器的接收缓冲区,可以看到其接收缓冲区中的数据大小

那么当B的接收缓冲区被填满后,A还会继续发送数据,因为调用send()方法,并不知道数据是否已经发出,而是将数据拷贝进内核的发送缓冲区,直到发送缓冲区也被填满。

我们再观察A服务器的发送缓冲区中,同样积压了一些数据。

当B从库的sleep 60秒到期后,B的接收缓冲区和A的发送缓冲区都会被清零,因为程序处理速度很快,很难观察到发送和接收缓冲区中的积压数据。

在日常运维工作中,如果发现接收缓冲区中有大量数据,比如php-fpm的接收缓冲区,那么可能就是程序处理不过来,出现了请求排队现象了,需要及时排查原因或者扩容。

 更多Linux知识,尽在千锋教育!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Linux 运维 云计算