<3>:阻塞IO的试验-echo程序

本文探讨了阻塞IO在实际应用中导致的问题,通过一个20MB数据传输的案例,详细分析了客户端和服务端之间的交互过程及存在的问题。文章还提出了改进方案,即采用分包措施,并给出了合理的协议设计建议。

阻塞意味着会阻塞,而且可能永远阻塞,
看下echo的原型,每个线程一个客户端。
代码位置recipes/tpc/{echo.cc, echo_client.cc}

测试

服务端
[root@bogon bin]# ./echo
客户端
[root@bogon bin]# ./echo_client localhost 1024(1024000,10240000,20488888(20M))

这里写图片描述

可以观察到20MB的时候已经阻塞掉了,why?
观察结果如下:

可以看到服务端的接受队列约有6MB,发送队列大概有4MB,于是有4MB的数据存在发送缓冲区里,接受缓冲区还有6MB的数据等待读取;客户接受缓冲区有900多K的数据,发送缓冲区有4MB的数据。

原因解释:

客户端发(send)完才会读取(receive),
客户端在发的时候,数据先进入内核缓冲区,缓冲区满了后就会阻塞掉;服务端读,并会发送,
客户端发完(send)20MB之前是不会读的(receive),
估算下,如果缓冲区10MB,则发送完10MB,服务端就send,阻塞在send上了,因为它等待client读取,但是client必须send完毕才会读取;所以显示server阻塞在send上,然后它就不会去收(receive)客户端的数据了,它不reveive,则client就阻塞在send上。同时阻塞在send上。

阻塞IO看起来简单,可是一旦阻塞了就没有办法解脱呀,20MB是本机测试的结果,如果用两台机器测试的数据会显著小于此机器(待测试。。。)这个和内核参数有关,主要是TCP的相关设置,如下

[david@localhost ~]$ sysctl -A | grep tcp.*mem
sysctl: permission denied on key 'fs.protected_hardlinks'
sysctl: permission denied on key 'fs.protected_symlinks'
sysctl: permission denied on key 'kernel.cad_pid'
sysctl: permission denied on key 'kernel.usermodehelper.bset'
sysctl: permission denied on key 'kernel.usermodehelper.inheritable'
sysctl: permission denied on key 'net.ipv4.tcp_fastopen_key'
net.ipv4.tcp_mem = 42339    56452   84678
//接受缓冲区,6M多
net.ipv4.tcp_rmem = 4096    87380   6291456
//发送缓冲区,4M多
net.ipv4.tcp_wmem = 4096    16384   4194304

哪里出问题呢?client还是server呢?首先client发送20MB的数据没问题,问题是出在服务端,看待echo服务器每次读4K的数据,读完就发送出去,问题在于没有完整的读取请求。

协议设计:

客户端先发送header,再发送payload。服务器在收到header的时候,先准备一个相应长度的缓冲区,接受到这么多的数据后,开始计算,算出一个响应;拿前面的例子来说,比如说15MB,算完之后将15MB数据发给客户端,这样的话server发的时候也发一个Header,再发送payload。字节流协议需要加上自己的分包措施才行。

这里写图片描述

参考陈硕[网络编程实践]课程

### 在Spring Boot项目中排除lettuce-core依赖的意义 在Spring Boot 2.x版本中,默认的Redis客户端是Lettuce,这是由`spring-boot-starter-data-redis`模块引入的[^3]。然而,在某些场景下,开发者可能更倾向于使用Jedis作为Redis客户端,而不是默认的Lettuce。为了实现这一点,需要从`spring-boot-starter-data-redis`中排除Lettuce依赖,并手动引入Jedis依赖。 排除`lettuce-core`依赖的主要原因是避免与Jedis客户端之间的冲突。Lettuce和Jedis是两种不同的Redis客户端实现,它们的工作机制存在显著差异。Lettuce基于Netty,支持异步非阻塞操作,而Jedis则是同步阻塞的客户端。如果同时引入这两种客户端,可能会导致配置复杂化以及潜在的运行时冲突[^1]。 ### 引入Jedis的同时排除Lettuce的原因 引入Jedis并排除Lettuce的核心原因在于满足特定的应用需求。例如,某些应用可能已经深度集成了Jedis客户端,或者开发者更熟悉Jedis的操作方式。此外,Jedis在一些简单的应用场景中表现良好,其同步阻塞特性对于某些不需要高并发的业务场景可能是更为简单和直观的选择[^3]。 以下是具体的步骤和配置: #### Maven配置示例 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <artifactId>lettuce-core</artifactId> <groupId>io.lettuce</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> ``` 上述配置中,通过`<exclusions>`标签排除了`lettuce-core`依赖,然后显式地引入了`jedis`和`commons-pool2`(用于连接池管理)[^3]。 #### YAML配置示例 在`application.yml`中,可以进一步配置Jedis客户端的相关参数: ```yaml spring: redis: host: localhost port: 6379 password: lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 ``` 需要注意的是,尽管这里仍然保留了`lettuce`相关的配置字段,但由于已经排除了`lettuce-core`依赖,这些配置将不会生效[^3]。 ### 总结 排除`lettuce-core`依赖并在Spring Boot项目中使用Jedis的主要目的是避免客户端冲突,并根据实际需求选择更适合的Redis客户端实现。这种做法不仅能够简化配置,还能确保应用的稳定性和性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值