c++服务端初始化阶段会将某二进制文件load 到内存中,每次请求都会访问该内存数据,c++服务端有持续请求,现在该二进制文件更新了,如何将该二进制文件热更新到内存中且不影响线上请求?内存空间足够
1
ysc3839 2022-03-31 20:44:23 +08:00 via Android 1
加载新的到另外一块内存里,加载完了 swap 指针不就好了吗?
|
2
Protocol 2022-03-31 20:53:06 +08:00
bitmap
|
3
JeromeCui 2022-03-31 20:56:59 +08:00
换个端口,启动后更新 nginx 上游,然后关掉老服务
|
4
Kasumi20 2022-03-31 20:59:40 +08:00 4
发一个通告, 停服 2 小时进行更新
|
5
fishCatcher 2022-03-31 21:09:59 +08:00 via iPhone
共享内存实现 jit 吧
|
6
OSDI 2022-03-31 21:48:32 +08:00 3
为内存中二进制文件维护一个 epoch 和 counter ,更新版本 epoch 加 1 ,请求使用该内存,对应 epoch 的 counter 加 1 ,用完 counter 减 1 。二进制文件组成单链表,head 指向最新的二级制文件,用 cas 的方式更新表头,请求使用当前 head 。gc 线程检查链表中除 head 外 epoch 的 counter 值,如果为 0 ,垃圾回收。
|
7
nonwill 2022-03-31 21:54:32 +08:00
楼上说的很具体了,再具体到 C++,智能指针保护二进制文件数据,每个请求处理暂存指针副本即可
|
8
ipwx 2022-03-31 22:17:44 +08:00 2
// 全局或者某个单例
std::shared_ptr<...> yourData; // 载入数据的函数 void loadData() { std::shared_ptr<...> newData; // 载入 yourData = newData; } // 使用数据的函数 void useData() { std::shared_ptr<...> theData = yourData; // 使用 yourData } |
9
xuanbg 2022-03-31 22:18:26 +08:00
一开始还以为是 C ,刚想说把指针指向新内存地址就完了。。。仔细一看艹!更简单,把新对象赋值给变量就好了嘛。
|
10
ysc3839 2022-03-31 22:38:43 +08:00 via Android 1
@ysc3839 补充一下,因为楼主没说具体情况,一些很复杂的情况下这种做法可能也会出问题。
假设一个完整的“请求”是每隔一分钟读取这段数据中的某个字节发送给客户端,要发送一百次才算完成请求。此时如果代码里访问数据是直接用全局的指针,比如 send(globalPointer[x]) 这样,就会出现问题。 需要更完整的细节才能确定正确做法。 |
11
3dwelcome 2022-03-31 23:19:37 +08:00 1
|
12
feather12315 2022-04-01 00:10:05 +08:00 via Android
我想到一个方法(未尝试):
1. 使用 mmap 建立文件映射 2. 文件更新后使用 madvice ( MADV_DONTNEED )释放映射 理论基础: 文件映射会将文件映射入内存,只有访问了内存才会触发缺页中断载入数据,madvice dontneed 释放了页表,访问相同的内存会再次触发缺页中断载入数据。 |
13
feather12315 2022-04-01 00:11:34 +08:00 via Android
疑惑:
mmap 载入的数据会不会随文件的更新而自动更新?毕竟 vma 的 backend 是 file ,能够确认的是 msync 这个系统调用可以将内存中的数据 flush 到文件。 |
14
Inn0Vat10n 2022-04-01 00:25:40 +08:00
直接分批停机 rolling
|
15
exch4nge 2022-04-01 00:26:37 +08:00
@ipwx 多线程情况下修改 shared_ptr 应该是不安全的,建议用 std::atomic_load 与 std::atomic_save 替代修改与读的操作。
|
16
icylogic 2022-04-01 02:49:13 +08:00
读到另一块内存里然后 std::atomic<Content*> 一改不就完了……如果要自动释放原来的内存,套一层 shared_ptr ,或者 https://github.com/facebook/folly/blob/main/folly/concurrency/AtomicSharedPtr.h
除非你有其他没说出来的需求,比如同一时刻所有 reader 访问的内容必须一致什么的 …… |
17
documentzhangx66 2022-04-01 04:17:39 +08:00 1
@ysc3839 大佬说的对。
前面的楼层说直接进行新旧内存区域的替换,我觉得业务上可能会有问题。比如直接替换后,新旧内存区域中的数据结构与偏移量都不一样了,如果替换后,还以以前的方式继续访问,很有可能会出问题。 我觉得,应该是以事务的方式,进行新旧内存区域的替换,并且替换后还要重置数据结构与偏移量会更稳,但这就需要把很多业务,改成支持事务的接口调用,方便抽象,有一定的工作量。具体设计方法,可以参考数据库原理与设计相关书籍。 |
18
LifStge 2022-04-01 08:31:58 +08:00
面试题啊 这就..... 想考察啥呢 结果肯定是没有的 不都是要分各自情况吗 能保证直接替换没问题的 加载了 直接替换就是了 ... 关键问题还是要保证对前面调用是否出问题...
说个方法 业务上对于这块数据的获取肯定有指定的几个接口吧 直接热更加载补丁代码 加载完数据 暂停相关线程 把获取数据相关的接口 直接 hook 到补丁代码上 直接返回新数据地址就好了 然后根据自己的业务逻辑 在可以判断前面的数据没有相关引用的情况下 释放掉就好了 再或者强迫症下 就把原本接口的相关数据的地址替换成热更的 再把 hook 的接口恢复过来.... |
19
iceheart 2022-04-01 08:44:21 +08:00 via Android
这不像面试题,是来要方案了
|
20
sfqtsh 2022-04-01 09:13:11 +08:00 via Android 1
还以为说的二进制[可执行]文件呢
|
21
encro 2022-04-01 09:32:06 +08:00
其实最安全可靠的是提供接口,让别人调用,而不是拿文件做交互,
既然是文件交互,那么不可避免的存在实时性问题。 如果不关心实时性问题,那么就如一楼所说,是一个内存指针 swap 的问题了。 |
22
anonymousar 2022-04-01 10:22:02 +08:00
2 个 shared ptr swap 。 其实就是 double buffer
|
23
zhangchongjie 2022-04-01 10:24:53 +08:00
@iceheart 🤫别说出来
|
24
jones2000 2022-04-01 10:29:05 +08:00
2 个内存块,奇偶切换
|
25
java253738191 OP @iceheart @zhangchongjie 你们这格局太小了,动不动说要方案,这种实现方法千千万又不是什么难题
|
26
boaofCHIAN 2022-04-01 10:53:30 +08:00
内存够的话 double buffer 双内存块奇偶切换 切换的指针用 atmoi 保护起来
|
27
undef404 2022-04-01 11:02:45 +08:00
是给服务端程序添加一段热更新的代码? 还是用另外的程序热更新服务端的内存数据?
|
28
java253738191 OP @boaofCHIAN 切换都简单,不考虑性能下 std::atomic<SharedPtr>或者 AtomicSharedPtr 都可以做,关键要考虑 old 的释放时机,old 内存可能请求正在调用
|
29
java253738191 OP @LifStge 面试题上面还有一题忘记说了,redis 的渐进式 rehash 如何实现的,其中有个 ht[2]如何切换的,衍生出该题
|
30
ipwx 2022-04-01 11:39:06 +08:00
@exch4nge atomic_load 要自己维护引用计数。
不过你说得对,多线程写同一个 shared_ptr 并不安全。但是多线程读是安全的,因此我们可以这样: std::shared_ptr<...> yourData; std::mutex yourDataLock; // 载入数据的函数 void loadData() { // 载入 std::shared_ptr<...> newData; // 更新 { std::unique_lock<...> lockGuard(yourDataLock); yourData = newData; } } // 使用数据的函数 void useData() { std::shared_ptr<...> theData = yourData; // 使用 yourData } |
31
ipwx 2022-04-01 11:42:02 +08:00
|