关于如何部署一个Node应用,Google有很多很棒的文章,现在最流行的两种方案
- PM2直接部署
- Docker部署
本文想站高一点点,聊聊如何部署一个高性能、高可用的Node服务(Java/Python/Go服务也一样),主要围绕部署服务的发展历史以及Node应用的负载均衡,不单单是部署成功这件事,也不会涉及Node语言本身的特性,比如异步IO、事件回调、单线程等
本文不讨论Node在高并发、I/O 密集场景的性能问题
从就部署一个没什么流量的博客应用开始吧,假设这个博客的流量会慢慢暴涨,对高并发高可用带来了挑战
只有一台机器,有很强的单点隐患
单机有风险,那就部署多几台机器
但是,前端怎么知道该访问哪台服务呢?
架构设计经典共识:没有什么事加一层解决不了的,如果有,那就再加一层
在这N台服务面前加一层LB (Load Balance 负载均衡),一般由Nginx担任这个角色,LB统一接受请求,再分发到某一台机器
而且Nginx这一层可以拿到用户的信息,能做一些动态分发、简单的逻辑处理等
博客流量开始慢慢增加,开始有爬虫、恶意大流量打进来,三台服务器也快撑不住了
在流量到达真正的服务器之前,加一层网关,用来鉴权、防爬、限流等,保证打到服务器的流量是安全可控的
在现代Web架构中会有很多静态文件请求,比如html、js、css等,这些流量其实可以不用每次都打进服务器或者读取服务器,可以利用 Nginx 对静态资源的缓存能力,或者直接把静态资源部署到CDN上去(最后再补上CDN的图)
上门的架构中,单点风险从服务器转移到了Nginx上,所以Nginx也需要以主备的形式部署两台机器,备Nginx通过 keepalived 心跳机制感知主机状态,如果主机宕机,则备Nginx自动切换为主机的角色
Nginx是七层负载均衡器,面向应用层的HTTP,转发流量需要client和上游server各自建立TCP连接,并放入内存缓冲区
建立TCP连接与机器I/O、CPU内存等相关,所以Nginx的抗负载能力在更大的流量面前(百万)也会扛不住
能否不建立TCP连接,直接转发流量包呢?
LVS建立在传输层(TCP/UDP),只转发包(修改地址),不需要和上下游建立连接,相对于Nginx的抗负载、性能更好
同样的道理,单个LVS也存在单点风险,再加几台?那么如何访问多加的几台呢?
利用DNS来做域名解析的负载均衡
补上CDN,这已经是非常大型的现代Web架构了,主要分层
- CDN处理静态文件
- 通过DNS负载均衡解析域名,随机打到其中一台机器
- LVS集群做负载均衡,转发流量
- Nginx作为接入层,做流量区分
- 网关层做统一鉴权、限流等,还可以做服务的发现和注册
从上面的大型架构发展历史,可以看到基本每一层都有负载均衡的存在,再回到Node应用本身,也有负载均衡的存在,主要有两个
- 进程服务负载均衡
- Node通过集群(cluster)模式实现了多进程
- RPC调用负载均衡
- 主要是解决不同服务之间的调用问题,包含传输协议、序列号协议等
关于Node的多进程可参考我这篇 blog/多进程与多线程 — GitHub
具体的负载均衡算法主要有三种
- Round Robin :依次轮询,调用非常均衡,缺点是无法考虑服务器的真实情况
- Weighted round robin:基于轮询加权重判断,但可能会导致某个节点的负载突然上涨
- source IP hash
- 服务存储状态session,通过一致性hash+虚拟节点算法实现
- 可以保证用户session,缓存命中率高,但是负载均衡性能比较差
关于负载均衡的算法这里不展开,可参考