我有4名Nginx工作人员和4名独角兽工人。我们在一些验证唯一名称的模型中遇到了并发问题。当我们在同一资源上同时发送多个请求时,我们会获得重复的名称。例如,如果我们发送大约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个)以创建许可证时,我都会得到一些重复项。我理解为什么。这是因为每个独角兽进程都没有创建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的并发性和可伸缩性问题吗?
答案 0 :(得分:2)
看一下事务隔离。例如,PostgreSQL - http://www.postgresql.org/docs/current/static/transaction-iso.html。
答案 1 :(得分:1)
通常情况下,您可以采取两种方式:
对唯一键列使用唯一索引(通过迁移)并捕获适当的异常;
以here描述的方式维护数据库约束,并捕获适当的异常。
或使用具有隔离级别的PostureSQL事务&#34;序列化&#34;这基本上是将平行翻译转换为连续翻译,正如Andrey Kryachkov早期描述的那样。