1. 在 tokio::main
宏中指定 runtime 的线程个数
默认是多线程 runtime,worker 个数为 CPU 的核心数。可以通过在 tokio::main
中增加如下内容来制定 worker 个数:
#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
或者切换成单线程 runtime:
#[tokio::main(flavor = "current_thread")]
其实鼠标 hover 到 tokio::main
上然后读注释就行了,这个在 tokio 的注释里写清楚了。
2. 在 release 版本里生成 debuginfo
在 Cargo.toml 里加上:
[profile.release]
debug = true
strip = false
第一行会在 release 版本的二进制里添加 debuginfo (文件名、行数、函数名等);第二行会关闭 release 编译时对符号的裁剪。
二者结合起来,可以让 release 版本的二进制程序更容易通过 gdb 观察调用栈等方式 debug,也能够通过 flamegraph 等生成火焰图。
2.1 题外话之 flamegraph 使用
https://github.com/brendangregg/FlameGraph
sudo perf record -e cpu-clock -g --call-graph dwarf -p 28591
perf script -i perf.data &> perf.unfold
./stackcollapse-perf.pl perf.unfold &> perf.folded
./flamegraph.pl perf.folded > perf.svg
注意如果抓出来的没有 debuginfo 可能是因为没有带 dwarf
参考:
https://www.cnblogs.com/happyliu/p/6142929.html
https://stackoverflow.com/questions/10933408/how-can-i-get-perf-to-find-symbols-in-my-program
3. AsyncRead & AsyncWrite 统计问题
在业务代码中对 TCP 做收发字节数的统计时,会发现一个比较诡异的问题:AsyncRead
没有返回读了多少字节的接口,但是 AsyncWrite
却是原生支持统计的。
pub trait AsyncWrite {
// 返回的 Ok(usize) 就代表成功写入了多少字节到缓冲中
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, Error>>;
// .....
}
pub trait AsyncRead {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<Result<()>>;
}
这个设计本身是很正常的,毕竟 poll_read
要求我们传入一个 ReadBuf
,ReadBuf
的所有方自然可以进行读取字节数的统计。但是,对于一些高度封装的库,这种统计接口并没有暴露出来,例如先用 tokio 创建 TcpStream
,然后用 native-tls 进行 tls 握手的场景。
在这种情况下,就只能在 TcpStream
上面再封一层 Buffer 了,需要为这个新的 Buffer 实现 AsyncRead
和 AsyncWrite
。后者的实现很简单,直接调用底层 TcpStream
的接口即可;前者稍微复杂些,需要在读写前后分别调用 ReadBuf
的 filled
方法,做减法来判断读了多少字节。