0%

在 linux 系统中,线程是最小的调度单元,主线程与进程内的其余线程享受公平调度,同时也允许主线程退出后进程内其余线程继续运行。但是在编程中,当 main 函数 return 之后,所有的线程均会退出。这是因为 main 函数中的 return 和 exit 系统调用是等效的。如果 main 函数正常结束,那么进程内的所有内存、线程描述符等都会被回收,因此其余线程无法继续运行。

但有时候,我们并不希望后台线程直接退出,而是想让其完成一些善后工作。例如在进行图形化编程时,后台有线程正在执行某些操作(如重写文件、使用缓冲区向系统写入数据),我们希望这个操作不存在中间态,而直接阻塞主线程退出会导致图形界面退出时卡住。这种场景下,我们可以使用系统调用pthread_exit函数使主线程退出时并不会调用 exit,从而避免进程退出。

本文记录在部署 MongoDB 复制集时遇到的一些小坑和解决方法。

docker 环境下 ip 设置错误导致无法访问

在第一次部署 MongoDB 时,为了方便而选择了使用 docker 容器进行部署。部署过程中完全依照官方的教程,但是最终部署的集群却无法在 docker 网络外以副本集模式访问。在调试过程中发现在 docker 网络内部可以进行访问,因此猜测可能是 DNS 或者监听的问题。

最终定位到问题在于初始化时选择的 host 错误。官方教程在初始化时,使用的命令如下:

1
2
3
4
5
6
7
8
9
10
// 使用 container name 初始化
// 只能够在 docker 网络内部访问
rs.initiate( {
_id : "rs0",
members: [
{ _id: 0, host: "mongodb0.example.net:27017" },
{ _id: 1, host: "mongodb1.example.net:27017" },
{ _id: 2, host: "mongodb2.example.net:27017" }
]
})

官方的教程在部署过程中直接使用了 container name 作为 MongoDB 实例的 HostName,由于所有的副本集是部署在同一个 docker 网络中,所以可以通过 docker DNS 功能来实现服务发现。而在 docker 网络外部,无法使用 docker DNS 服务,因此不能够正确地访问集群。

经过修改后,将集群初始化命令修改为以下形式,可以正常访问:

1
2
3
4
5
6
7
8
9
10
11
// 容器使用 publish 端口
// 集群使用宿主机 ip 初始化
// 使其可以接受容器外部访问
rs.initiate( {
_id : "rs0",
members: [
{ _id: 0, host: "宿主机ip:port" },
{ _id: 1, host: "宿主机ip:port" },
{ _id: 2, host: "宿主机ip:port" }
]
})

宿主机 ip 变更,集群失效

上述部署方式,虽然解决了容器外部访问的问题,但是在一次网关配置的修改后,集群所在的宿主机 ip 发生了变化,导致之前部署的 ip 无法访问,需要修改集群的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 部分容器 ip 改变,集群中仍有 master
rs.reconfig( {
_id : "rs0",
members: [
{ _id: 0, host: "mongodb0.example.net:27017" },
{ _id: 1, host: "mongodb1.example.net:27017" },
{ _id: 2, host: "mongodb2.example.net:27017" }
]
})

// 全部容器 ip 改变
// 部分容器 ip 改变,集群中仍有 master
rs.reconfig( {
_id : "rs0",
members: [
{ _id: 0, host: "mongodb0.example.net:27017" },
{ _id: 1, host: "mongodb1.example.net:27017" },
{ _id: 2, host: "mongodb2.example.net:27017" }
]
},{force:true})

最好的部署方式还是在网关处部署一个 DNS 服务,用于集群的域名管理。

网络吞吐量过大,查询不稳定

由于在部署中利用了 MongoDB Gridfs 功能作为基础架构作为深度学习的标记、训练、测试的数据集来源,在我们的使用场景中,经常会需要从 MongoDB 集群中批量访问 500 张以上 15M 左右的图像。在最初的本地测试中,我测试了批量从 MongoDB 中读取 1000 张图像,所以运行速度一直非常稳定,保持在一张图像 150ms 左右的速度,这对我们来说是比较满意的性能。

但是将 MongoDB 集群投入到使用中,我们发现对图像的获取速度非常不稳定,有时获取一张图像只需要 200ms 左右,有时获取一张图像却需要 3~4 s。大部分情况下获取一张图像需要 1s 左右,而且 MongoDB 性能的波动非常严重,不符合我们最初的测试效果。

最初猜测是由于 MongoDB 中数据存储不合理,导致数据集查询较慢。但查询 MongoDB 的慢查询日志后,发现所有的查询操作均不是慢查询操作。通过 top 命令查询 MongoDB 部署的主机进行监控,发现在查询过程中,CPU 的占用率一直在 20%左右。正在一筹莫展时,突然发现主机的网卡占用率非常高,猜测是网络的问题。

利用 wireshark 抓包后,发现在查询过程中有大量 TCP 重传,可能是由于网络阻塞比较严重。经过排查后,发现是实验室的网关性能过差。

最终,更换了实验室网关,并且将需要批量查询图像的主机通过网线直接与 MongoDB 部署主机相连,最终批量查询的时间稳定在 200ms 左右。