本文由内部分享整理而成,如发现有错误的地方,请不吝指出
unicorn 是 Ruby Web 应用中的一款应用服务器,提供两个功能:
unicorn 工作在 Web 的应用层,直接调用后端 Handler 处理请求。
提供 HTTP 服务功能:
通过多进程提供高并发能力。由于 Ruby 全局解释锁阻碍的同一进程中的线程并行执行,Ruby 应用中的高并发必须通过多进程实现。
线程中如果有 IO 操作时,GIL 会自动释放,所以在线程中有 IO 操作的情况下,同一进程中的多个线程可以实现近似的并行执行。
通过 Master-Workers 进程结构提供服务:
此结构和 Nginx 多进程模式一致。Master 作为劳心者,不处理实际的事务,只做顶层调度。Workers 作为劳力者,只处理实际的请求,受制于 Master。
通过上图可总结如下:
另外:
由上图可以看出 unicorn 的 IO 模型有如下特点:
Puma 实现了 Reactor 模型,所谓 Reactor 模型就是将Socket的操作
和实际的请求处理
用不同的角色来做,不像 unicorn 那样用同一个角色 (线程) 来处理。避开了 Socket 操作时阻塞导致的进程资源浪费,实现了高效、分工、低耦合等。
同时引入多个工作线程来处理实际的请求。这样在某个线程调用外部接口或者查询数据库等 IO 操作时,其他工作线程可以并发执行,避免进程空闲。
整理特点如下:
Master
Worker
通信方式:
平滑关键点在于:
详细信息可以参考: Unicorn 进程如何保证平滑重启?
def process_client(client)
super(client) # Unicorn::HttpServer#process_client
return if @_worker_memory_limit_min == 0 && @_worker_memory_limit_max == 0
@_worker_process_start ||= Time.now
@_worker_memory_limit ||= @_worker_memory_limit_min + randomize(@_worker_memory_limit_max - @_worker_memory_limit_min + 1)
@_worker_check_count += 1
if @_worker_check_count % @_worker_check_cycle == 0
rss = GetProcessMem.new.bytes
logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
if rss > @_worker_memory_limit
logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start) # 关键点
end
@_worker_check_count = 0
end
end
通过 hack process_client
方法,在请求处理完后,检测进程内存消耗等参数,操作阈值,则将自己干掉,Master 会自动起新的 Worker 进程。
有关 unicorn 中 fork,exec,preload 等更细节的描述,可以参考 Unicorn 进程如何保证平滑重启?