Unicorn+Nginx并发与数据去重
Unicorn+Nginx concurrency and data duplication
我有 4 个 Nginx worker 和 4 个 unicorn worker。我们在一些验证唯一名称的模型中遇到了并发问题。当我们在同一个资源上同时发送多个请求时,我们会得到重复的名称。例如,如果我们发送大约 10 个创建许可证的请求,我们会得到重复 serial_numbers...
这是一些背景信息:
模型(简化)
class License < ActiveRecord::Base
validates :serial_number, :uniqueness => true
end
Unicorn.rb
APP_PATH = '.../manager'
worker_processes 4
working_directory APP_PATH # available in 0.94.0+
listen ".../manager/tmp/sockets/manager_rails.sock", backlog: 1024
listen 8080, :tcp_nopush => true # uncomment to listen to TCP port as well
timeout 600
pid "#{APP_PATH}/tmp/pids/unicorn.pid"
stderr_path "#{APP_PATH}/log/unicorn.stderr.log"
stdout_path "#{APP_PATH}/log/unicorn.stdout.log"
preload_app true
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
check_client_connection false
run_once = true
before_fork do |server, worker|
ActiveRecord::Base.connection.disconnect! if defined?(ActiveRecord::Base)
MESSAGE_QUEUE.close
end
after_fork do |server, worker|
ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base)
end
Nginx.conf(简体)
worker_processes 4;
events {
multi_accept off;
worker_connections 1024;
use epoll;
accept_mutex off;
}
upstream app_server {
server unix:/home/blueserver/symphony/manager/tmp/sockets/manager_rails_write.sock fail_timeout=0;
}
try_files $uri @app;
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
proxy_pass http://app_server;
}
每次我发送多个请求(超过 4 个)来创建许可证时,我都会得到一些副本。我明白为什么。这是因为每个 unicorn 进程还没有创建 serial_number 的资源。因此,它允许多次创建它...
ActiveRecord 正在进程级别而不是数据库级别验证字段的唯一性。一种解决方法是将验证移至数据库(但这将非常麻烦且难以维护)。
另一种解决方法是将写入请求 (POST/PUT/DELETE) 限制为仅一个独角兽,并让多个独角兽回复读取请求 (GET)。在 Nginx 的位置中有这样的东西...
# 4 unicorn workers for GET requests
proxy_pass http://app_read_server;
# 1 unicorn worker for POST/PUT/DELETE requests
limit_except GET {
proxy_pass http://app_write_server;
}
我们目前正在使用它。它修复了并发问题。但是,一个写入服务器不足以在高峰时间进行回复,这会造成瓶颈。
有什么办法解决Nginx+Unicorn的并发和扩展问题吗?
看看事务隔离。例如,PostgreSQL - http://www.postgresql.org/docs/current/static/transaction-iso.html.
通常情况下,您可以采用两种方式:
对唯一键列使用唯一索引(通过迁移)并捕获适当的异常;
以描述的方式维护数据库约束 here 并捕获适当的异常。
或使用具有隔离级别 "serialised" 的 PostureSQL 事务,这基本上是将并行翻译转换为连续翻译,正如 Andrey Kryachkov 早期所描述的那样。
我有 4 个 Nginx worker 和 4 个 unicorn worker。我们在一些验证唯一名称的模型中遇到了并发问题。当我们在同一个资源上同时发送多个请求时,我们会得到重复的名称。例如,如果我们发送大约 10 个创建许可证的请求,我们会得到重复 serial_numbers...
这是一些背景信息:
模型(简化)
class License < ActiveRecord::Base
validates :serial_number, :uniqueness => true
end
Unicorn.rb
APP_PATH = '.../manager'
worker_processes 4
working_directory APP_PATH # available in 0.94.0+
listen ".../manager/tmp/sockets/manager_rails.sock", backlog: 1024
listen 8080, :tcp_nopush => true # uncomment to listen to TCP port as well
timeout 600
pid "#{APP_PATH}/tmp/pids/unicorn.pid"
stderr_path "#{APP_PATH}/log/unicorn.stderr.log"
stdout_path "#{APP_PATH}/log/unicorn.stdout.log"
preload_app true
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
check_client_connection false
run_once = true
before_fork do |server, worker|
ActiveRecord::Base.connection.disconnect! if defined?(ActiveRecord::Base)
MESSAGE_QUEUE.close
end
after_fork do |server, worker|
ActiveRecord::Base.establish_connection if defined?(ActiveRecord::Base)
end
Nginx.conf(简体)
worker_processes 4;
events {
multi_accept off;
worker_connections 1024;
use epoll;
accept_mutex off;
}
upstream app_server {
server unix:/home/blueserver/symphony/manager/tmp/sockets/manager_rails_write.sock fail_timeout=0;
}
try_files $uri @app;
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
proxy_pass http://app_server;
}
每次我发送多个请求(超过 4 个)来创建许可证时,我都会得到一些副本。我明白为什么。这是因为每个 unicorn 进程还没有创建 serial_number 的资源。因此,它允许多次创建它...
ActiveRecord 正在进程级别而不是数据库级别验证字段的唯一性。一种解决方法是将验证移至数据库(但这将非常麻烦且难以维护)。
另一种解决方法是将写入请求 (POST/PUT/DELETE) 限制为仅一个独角兽,并让多个独角兽回复读取请求 (GET)。在 Nginx 的位置中有这样的东西...
# 4 unicorn workers for GET requests
proxy_pass http://app_read_server;
# 1 unicorn worker for POST/PUT/DELETE requests
limit_except GET {
proxy_pass http://app_write_server;
}
我们目前正在使用它。它修复了并发问题。但是,一个写入服务器不足以在高峰时间进行回复,这会造成瓶颈。
有什么办法解决Nginx+Unicorn的并发和扩展问题吗?
看看事务隔离。例如,PostgreSQL - http://www.postgresql.org/docs/current/static/transaction-iso.html.
通常情况下,您可以采用两种方式:
对唯一键列使用唯一索引(通过迁移)并捕获适当的异常;
以描述的方式维护数据库约束 here 并捕获适当的异常。
或使用具有隔离级别 "serialised" 的 PostureSQL 事务,这基本上是将并行翻译转换为连续翻译,正如 Andrey Kryachkov 早期所描述的那样。