如题。现在公司的产品服务端程序偶尔需要更新,但是由用户在上面跑。目前我想到的最简单粗暴的方法,就是:
- 找一个夜深人静的时刻(你懂的)
- 更新代码
- 重启服务 更新代码并重启服务的时候,因为用户可能还在上面跑,可能会对用户产生一些影响,甚至对数据的完整性造成潜在的威胁。不知道大家用Node.js做后台,一般都是怎么更新,有没有比较平滑的方案或工具? 提前感谢 m( _ _ )m
要看你什么要求,最简单粗暴的当然就像你这样,顶多弄个工具pm2,forever什么的,但这些东西都只是表面高雅,其实和你手工没什么两样。要处理细节的话,javascript现在有点小困难,技术上是一方面,编程习惯什么的都要改一改,因为每次更新的时候不仅仅有代码还有数据,如何保证更新后数据保持才是热更新最大的问题。这个问题太难了,能够完美解决的要大神级别了,linux4.0里搞了个不重启内核热更新,4.0才搞了出来,可见这个东西有多难。
多机器热备啊,
@captainblue2013 感谢回复!能说一些具体的方案或工具吗?不胜感激!
@coordcn 嗯,确实,主要就是担心数据的问题。难道现在都没有什么方案吗?
@crystaldust 没有一个好的框架和约束,没有好的团队,这个东西极有可能是个大坑,会出现莫名其妙的问题,发现困难,追踪困难,因为不知道哪个人破坏了编码规则,局部变量和闭包烂用将是致命的。
erlang这样的就自己实现了,因为他的变量是不变的,不存在局部变量的问题。我有个折中的办法,就是全面禁止局部变量和闭包,这样在更新局部代码的时候就没有局部变量和闭包的烦恼。但是这样一来编码的难度又增加了。
云风的skynet用的是局部更新,就是约定好了哪些部位可以热更新,这里绝对不能存在局部变量和闭包,其他地方代码不变。javascript和lua做到完美都有不少的困难,我对这个问题也很感兴趣,期待大神的分享。
@captainblue2013 首先 蓝神说的对
GUAPI思路 程序接受shell输入更新指令。路由表全指向404。动态重载CONFIG / ROUTER。重指向路由表。
- 启动至少2个 app server: srv1, srv2, …
- 在 app server 前,放个 http proxy,并用 consistent hashring 动态添加 删除 app server
- 更新服务时,把 其中 一个 app server,如 srv1 从 http proxy consistent hashring 拿掉
- 泡杯茶等 srv1 pending request 处理完成后,更新 srv1 code,重启 srv1, 再把 srv1 加到 http proxy hashring
- 重复 3-4,更新 其他 app srv
@sequoiar 你这个方法和楼主的没什么两样,对不保持状态的网页服务还可以,对网络游戏之类的保持状态的服务是行不通的,进程重启进程内保存的状态也没有了。网页服务器可以把状态暂存在redis等内存数据库内,网络游戏这么做就不现实了。
所以 楼主提出的问题是很难的。
- put all session data in shared db, like redis
- no matter connection lose, just implement a reconnection mechanism on client side
@sequoiar 我讲的是进程内维护的状态,类似局部变量和闭包等问题。你的方法可以很好的解决网页服务问题,但是对其他形式的恐怕无能为力。至少我现在还没找到可以完美解决这个问题的方案。如有大神贡献,那是极好的。
但是如果没有洁癖的话,暴力重启也不是什么大事情,凌晨2点做就是了。
pomelo里也有类似的功能,没仔细研究,大神可以分享下原理。
目前我也在做一个即时通讯app,每次更新通信服务器逻辑也面临这个问题,而且处于安全性考虑,虽然短暂的重启socket可以重连,但是token验证不过还需要重新登录,当然最矛盾的还是重启时间,每天的峰值都是晚上10点左右,所以除非凌晨时间人少些。 不过客观的分析下,对于无状态的服务可以通过依次更新的方法,避免用户的影响;但是有状态的服务好像就比较难做了。
我的方法很简单,删除已经被修改的文件的缓存require里面的缓存 当缓存正在使用的时候,不影响,垃圾回收不回收对象,等待旧的对象都使用完毕之后, 新的对象就会重新从文件系统加载的新代码去生成对象, 这样就做到了平滑切换 切换过程中,已经在线的用户不会掉线,继续使用旧的代码,直至对象解除引用, 新的用户登录进来,使用的是新的代码
感谢大家的回复,非常受教。我现在重启时用户并不是实时通讯,是从客户端发送http请求到服务器,重启时有可能会请求失败。最担心的是比如去操作数据库,还未返回,这时候重启了,对于数据库操作的状态也就无法保证了。特别是用MongoDB,不能做到表的联合查询等操作,需要分别去操作,如果某个操作成功,另一个失败,对数据的完整性会产生一定的影响。
最高端的方法是Lua或者其他在Nginx中維護一個連接隊列 自豪地采用 CNodeJS ionic
额,没有关注这个。。
@iwillwen 嗯,这个确实高端了。要是有nginx插件就好了
pm2 reload 不是可以热部署吗?
@iamcc 我先Mark然后研究一下,正在试图改到pm2(目前用forever)
@crystaldust pm2 start app.js -i 0 //cluster mode
然后pm2 reload all 就可以了。
@iamcc 嗯,看到文档了,确实号称0 downtime,研究实践一下先,多谢仁兄~
@crystaldust 0 代表原来的max,原来是-i max,现在改为-i 0 了
你可以看看meteor的代码热替换