Ruby Nginx->;独角兽-资源暂时不可用

Ruby Nginx->;独角兽-资源暂时不可用,ruby,nginx,sinatra,unicorn,Ruby,Nginx,Sinatra,Unicorn,我们在生产中使用了Sinatra 应用程序有后端和前端部分。 后端和前端紧密耦合,代表整体。 Sinatra应用程序的后端大多是Java/Sping/Mongo上编写的“真实”后端的简单代理。“真正的”后端可以通过一组REST访问 因此,请求从最终用户到后端的完整路径如下: UI->Nginx->Unicorn->Sinatra->Java/Spring-back-end->Sinatra->Unicorn->Nginx->UI 最近,我们引入了一项功能,允许用户在浏览器打开时每隔5秒从服务器接

我们在生产中使用了Sinatra

应用程序有后端和前端部分。
后端和前端紧密耦合,代表整体。
Sinatra应用程序的后端大多是Java/Sping/Mongo上编写的“真实”后端的简单代理。“真正的”后端可以通过一组REST访问

因此,请求从最终用户到后端的完整路径如下:
UI->Nginx->Unicorn->Sinatra->Java/Spring-back-end->Sinatra->Unicorn->Nginx->UI

最近,我们引入了一项功能,允许用户在浏览器打开时每隔5秒从服务器接收消息。该功能通过以下方式实现:

    setInterval(function() {
        if (self.tab_visibility() == 'visible') {
            if (!self.osdDisplayed) {
                self.getOsdMessage();
            }
        }
    }, 5000);
这种方法非常愚蠢,应该重写,以便将通知从服务器推送到前端。但目前它通过setInterval工作

因此,我们开始遇到的问题是Nginx无法获得到Unicorn的开放式套接字连接

在Nginx日志文件中出现错误:

unix:/tmp/unicorn.sock失败(11:资源暂时不可用) 连接到上游时

最终用户无法打开站点-错误503
但与此同时,在Unicorn日志中,我看到了许多成功完成的请求,响应代码为200

问题:当其他用户根据Unicorn日志成功处理时,为什么我无法打开该站点

Nginx.conf

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;

    index   index.html index.htm;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  localhost;
        root         /usr/share/nginx/html;

        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
}
/etc/nginx/conf.d/web\u client.conf

log_format compression  '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $bytes_sent '
                        '"$http_referer" "$http_user_agent" "$gzip_ratio"';

limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=1000r/s;

limit_conn_status 405;
limit_req_status 405;

upstream v-web {
  server unix:/tmp/unicorn.sock fail_timeout=0;
}

server {
  listen       8008;
  set_real_ip_from  10.0.0.0/8;
  real_ip_header    X-Forwarded-For;
  real_ip_recursive on;
  root /opt/v/Web-Client-http_auth/public;
  access_log  /var/log/nginx/nginx.access.log compression buffer=32k;
  error_log  /var/log/nginx/nginx.error.log warn;

  limit_conn conn_limit_per_ip 10;
  limit_req zone=req_limit_per_ip burst=1200 nodelay;

  keepalive_timeout  75;
  sendfile        on;
  tcp_nopush     on;
  open_file_cache           max=1000 inactive=20s;
  open_file_cache_valid     30s;
  open_file_cache_min_uses  2;
  open_file_cache_errors    on;
  gzip              on;
  gzip_http_version 1.0;
  gzip_disable      "MSIE [1-6]\.(?!.*SV1)";
  gzip_buffers 4 16k;
  gzip_comp_level 2;
  gzip_min_length 0;
  gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_proxied expired no-cache no-store private auth;

  location / {
    try_files $uri @v-web;
    index  index.html index.htm;
  }

  location @v-web {
    proxy_pass http://v-web;
    proxy_redirect     off;
    proxy_set_header   Host             $http_host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    client_max_body_size       10m;
    client_body_buffer_size    128k;

    proxy_connect_timeout      60;
    proxy_send_timeout         60;
    proxy_read_timeout         60;

    proxy_buffer_size          16k;
    proxy_buffers              8 64k;
    proxy_busy_buffers_size    128k;
    proxy_temp_file_write_size 128k;
  }

  error_page  404              /404.html;
  location = /404.html {
    root   /usr/share/nginx/html;
  }
  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }
  error_page   405 /custom.html;
  location = /custom.html {
    root   /usr/share/nginx/html;
  }


  location /nginx_status {
       stub_status on;
       access_log   off;
       allow 127.0.0.1;
       deny all;
  }
}
Sinatra应用程序与Java“real”后端的交互通过以下代码段进行处理:

连接池.rb

######################################################################
# ConnectionPool
######################################################################
class ConnectionPool
  include Singleton

  ######################################################################
  def initialize
    @mutex = Mutex.new
    @condition = ConditionVariable.new

    @connections = []
    @limit = settings.api_pool_limit
  end

  ######################################################################
  def new_connection
    handle = HTTPClient.new
    handle.follow_redirect_count = settings.api_redirect_limit
    handle.connect_timeout = settings.api_timeout
    handle.send_timeout = settings.api_timeout
    handle.receive_timeout = settings.api_timeout
    handle.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
    {
      :handle => handle,
      :locked => false
    }
  end

  ######################################################################
  def get_connection
    @mutex.synchronize do
      connection = get_connection_from_pool
      return connection if connection

      if @connections.length < @limit
        connection = new_connection
        connection[:locked] = true

        @connections.push(connection)

        return connection
      end

      loop do
        @condition.wait(@mutex)

        connection = get_connection_from_pool
        return connection if connection
      end
    end
  end

  ######################################################################
  def get_connection_from_pool
    @connections.each do |connection|
      next if connection[:locked]

      connection[:locked] = true
      return connection
    end
    return nil
  end


  ######################################################################
  def start
    return unless block_given?

    connection = get_connection
    begin
      yield connection[:handle]
    ensure
      @mutex.synchronize do
        connection[:locked] = false
        @condition.signal
      end
    end
  end
end
######################################################################
# Connection
######################################################################
class Connection
  include ::CustomLogger
  include ::Sinatra::Helpers
  attr_reader :session

  ######################################################################
  def initialize(request, session)
    @session = SessionStore.new(session)
    @session.read_credentials_from_cookies(request)
    @cookieManager = WebAgent::CookieManager.new
  rescue Dalli::RingError, Timeout::Error => e
    log_error("Connection initialize: memcache is unreachable #{e.message} \n")
  end

  ######################################################################
  def send(type, relative_url, options = {}, cache)
    locale_for_cache = begin
      options[:headers]["x-vidmind-locale"]
    rescue
      ''
    end
    url = "#{settings.wildfire_base_url}#{relative_url}"
    result = nil
    if cache and !development?
      result = get_from_cache(relative_url, locale_for_cache)
      return result unless result.blank?
    end
    ConnectionPool.instance.start do |http|
      if @session.api_cookie

        cookie = find_session_cookie(@cookieManager.cookies)
        unless cookie
          cookie = WebAgent::Cookie.new
          cookie.name = 'JSESSIONID'
          cookie.url = URI.parse(settings.wildfire_base_url)
          cookie.domain = cookie.url.host
          cookie.path = cookie.url.path
        end
        cookie.value = @session.api_cookie

        @cookieManager.cookies = []
        @cookieManager.add(cookie)
        http.cookie_manager = @cookieManager
        request_cookies = http.cookies.map{ |i| {i.name => i.value} }
      else
        http.cookie_manager = WebAgent::CookieManager.new
        request_cookies = [{ 'JSESSIONID' => '' }]
      end

      headers = {}
      headers.merge!(options[:headers]) if options[:headers]
      headers.merge!({ 'Content-Type' => options[:data_type] }) if options[:data_type]
      headers['X-Forwarded-For'] = settings.forwarded_for if settings.forwarded_for

      time_start = Time.now
      response = http.request(type, url, {
        :query => options[:query],
        :body => options[:data],
        :header => headers,
        :follow_redirect => false
      })
      # httpclient doesn't know Post/Redirect/Get pattern
      if invalid_http_status?(response.status)
        url = response.header['location'][0]

        response = http.request(:get, url, {
          :header => options[:headers],
          :follow_redirect => true
        })
      end

      time_end = Time.now
      json = process_response(response.content)

      log({
        request_cookies: request_cookies,
        response_cookies: http.cookies.map{ |i| {i.name => i.value}},
        time_start: time_start,
        time_end: time_end,
        url: relative_url,
        type: type,
        status: response.status,
        body: json,
        options: options,
        rk: response.content,
        encoding: response.body_encoding,
        fingerprint: headers['x-vidmind-device-id']
      })

      result = {
        success: response_success?(response.status, json),
        code: response.status,
        json: json
      }
      cookie = find_session_cookie(http.cookies)
      @session.api_cookie = cookie.value if cookie
      count_api_requests(time_end, time_start)
    end
    if cache and result[:success] and !development?
      unless result.blank?
        set_cache(relative_url, locale_for_cache, result)
      end
    end
    result
    rescue Exception => e
      log_error " raised for url: #{relative_url} #{e} "
      log_error   " \r\nrequest_cookies #{@session.api_cookie} \r\n"
      raise e if e.is_a?(WildfireError)
  end

  ######################################################################
  def find_session_cookie(cookies)
    cookies.each do |cookie|
      return cookie if 'JSESSIONID'.casecmp(cookie.name) == 0
    end if cookies
    nil
  end

  ######################################################################
  protected

    def process_response(response)
      begin
        response.empty? ? {} : JSON.parse(response)
      rescue
        {'error' => response}
      end
    end

    def response_error?(json)
      res = (json.kind_of?(Hash) && json.has_key?('error')) ||
      (json.kind_of?(Array) && json.first && json.first.has_key?('error')) || json.kind_of?(String)
      res
    end

    def response_success?(status, json)
      !(status < 200 or status >= 300) && !response_error?(json)
    end

    def invalid_http_status?(status)
      if status == 503
        raise WildfireConnectionError
      elsif status == 403
        raise WFSessionExpirationException
      end
      status >= 300 and status < 400
    end
end
######################################################################
#连接池
######################################################################
类连接池
包括独生子女
######################################################################
def初始化
@mutex=mutex.new
@condition=ConditionVariable.new
@连接=[]
@limit=settings.api\u pool\u limit
结束
######################################################################
def新连接
handle=HTTPClient.new
handle.follow\u redirect\u count=settings.api\u redirect\u limit
handle.connect\u timeout=settings.api\u timeout
handle.send\u timeout=settings.api\u timeout
handle.receive\u timeout=settings.api\u timeout
handle.ssl\u config.verify\u mode=OpenSSL::ssl::verify\u NONE
{
:handle=>handle,
:locked=>false
}
结束
######################################################################
def get_连接
@mutex.do
连接=从\u池中获取\u连接\u
如果是连接,则返回连接
如果@connections.length<@限制
连接=新连接
连接[:锁定]=真
@连接。推(连接)
回路连接
结束
环道
@条件。等待(@mutex)
连接=从\u池中获取\u连接\u
如果是连接,则返回连接
结束
结束
结束
######################################################################
def从_池获取_连接_
@连接。每个do |连接|
下一个if连接[:锁定]
连接[:锁定]=真
回路连接
结束
归零
结束
######################################################################
def启动
返回,除非给出block_?
连接=获取连接
开始
屈服连接[:手柄]
确保
@mutex.do
连接[:锁定]=错误
@条件信号
结束
结束
结束
结束
连接.rb

######################################################################
# ConnectionPool
######################################################################
class ConnectionPool
  include Singleton

  ######################################################################
  def initialize
    @mutex = Mutex.new
    @condition = ConditionVariable.new

    @connections = []
    @limit = settings.api_pool_limit
  end

  ######################################################################
  def new_connection
    handle = HTTPClient.new
    handle.follow_redirect_count = settings.api_redirect_limit
    handle.connect_timeout = settings.api_timeout
    handle.send_timeout = settings.api_timeout
    handle.receive_timeout = settings.api_timeout
    handle.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
    {
      :handle => handle,
      :locked => false
    }
  end

  ######################################################################
  def get_connection
    @mutex.synchronize do
      connection = get_connection_from_pool
      return connection if connection

      if @connections.length < @limit
        connection = new_connection
        connection[:locked] = true

        @connections.push(connection)

        return connection
      end

      loop do
        @condition.wait(@mutex)

        connection = get_connection_from_pool
        return connection if connection
      end
    end
  end

  ######################################################################
  def get_connection_from_pool
    @connections.each do |connection|
      next if connection[:locked]

      connection[:locked] = true
      return connection
    end
    return nil
  end


  ######################################################################
  def start
    return unless block_given?

    connection = get_connection
    begin
      yield connection[:handle]
    ensure
      @mutex.synchronize do
        connection[:locked] = false
        @condition.signal
      end
    end
  end
end
######################################################################
# Connection
######################################################################
class Connection
  include ::CustomLogger
  include ::Sinatra::Helpers
  attr_reader :session

  ######################################################################
  def initialize(request, session)
    @session = SessionStore.new(session)
    @session.read_credentials_from_cookies(request)
    @cookieManager = WebAgent::CookieManager.new
  rescue Dalli::RingError, Timeout::Error => e
    log_error("Connection initialize: memcache is unreachable #{e.message} \n")
  end

  ######################################################################
  def send(type, relative_url, options = {}, cache)
    locale_for_cache = begin
      options[:headers]["x-vidmind-locale"]
    rescue
      ''
    end
    url = "#{settings.wildfire_base_url}#{relative_url}"
    result = nil
    if cache and !development?
      result = get_from_cache(relative_url, locale_for_cache)
      return result unless result.blank?
    end
    ConnectionPool.instance.start do |http|
      if @session.api_cookie

        cookie = find_session_cookie(@cookieManager.cookies)
        unless cookie
          cookie = WebAgent::Cookie.new
          cookie.name = 'JSESSIONID'
          cookie.url = URI.parse(settings.wildfire_base_url)
          cookie.domain = cookie.url.host
          cookie.path = cookie.url.path
        end
        cookie.value = @session.api_cookie

        @cookieManager.cookies = []
        @cookieManager.add(cookie)
        http.cookie_manager = @cookieManager
        request_cookies = http.cookies.map{ |i| {i.name => i.value} }
      else
        http.cookie_manager = WebAgent::CookieManager.new
        request_cookies = [{ 'JSESSIONID' => '' }]
      end

      headers = {}
      headers.merge!(options[:headers]) if options[:headers]
      headers.merge!({ 'Content-Type' => options[:data_type] }) if options[:data_type]
      headers['X-Forwarded-For'] = settings.forwarded_for if settings.forwarded_for

      time_start = Time.now
      response = http.request(type, url, {
        :query => options[:query],
        :body => options[:data],
        :header => headers,
        :follow_redirect => false
      })
      # httpclient doesn't know Post/Redirect/Get pattern
      if invalid_http_status?(response.status)
        url = response.header['location'][0]

        response = http.request(:get, url, {
          :header => options[:headers],
          :follow_redirect => true
        })
      end

      time_end = Time.now
      json = process_response(response.content)

      log({
        request_cookies: request_cookies,
        response_cookies: http.cookies.map{ |i| {i.name => i.value}},
        time_start: time_start,
        time_end: time_end,
        url: relative_url,
        type: type,
        status: response.status,
        body: json,
        options: options,
        rk: response.content,
        encoding: response.body_encoding,
        fingerprint: headers['x-vidmind-device-id']
      })

      result = {
        success: response_success?(response.status, json),
        code: response.status,
        json: json
      }
      cookie = find_session_cookie(http.cookies)
      @session.api_cookie = cookie.value if cookie
      count_api_requests(time_end, time_start)
    end
    if cache and result[:success] and !development?
      unless result.blank?
        set_cache(relative_url, locale_for_cache, result)
      end
    end
    result
    rescue Exception => e
      log_error " raised for url: #{relative_url} #{e} "
      log_error   " \r\nrequest_cookies #{@session.api_cookie} \r\n"
      raise e if e.is_a?(WildfireError)
  end

  ######################################################################
  def find_session_cookie(cookies)
    cookies.each do |cookie|
      return cookie if 'JSESSIONID'.casecmp(cookie.name) == 0
    end if cookies
    nil
  end

  ######################################################################
  protected

    def process_response(response)
      begin
        response.empty? ? {} : JSON.parse(response)
      rescue
        {'error' => response}
      end
    end

    def response_error?(json)
      res = (json.kind_of?(Hash) && json.has_key?('error')) ||
      (json.kind_of?(Array) && json.first && json.first.has_key?('error')) || json.kind_of?(String)
      res
    end

    def response_success?(status, json)
      !(status < 200 or status >= 300) && !response_error?(json)
    end

    def invalid_http_status?(status)
      if status == 503
        raise WildfireConnectionError
      elsif status == 403
        raise WFSessionExpirationException
      end
      status >= 300 and status < 400
    end
end
######################################################################
#联系
######################################################################
类连接
include::CustomLogger
include::Sinatra::Helpers
属性读取器:会话
######################################################################
def初始化(请求、会话)
@session=SessionStore.new(会话)
@会话。从\u cookies读取\u凭据\u(请求)
@cookieManager=WebAgent::cookieManager.new
rescue Dalli::RingError,Timeout::Error=>e
日志错误(“连接初始化:memcache不可访问35;{e.message}\n”)
结束
######################################################################
def send(类型、相对url、选项={}、缓存)
\u缓存的区域设置\u=开始
选项[:标题][“x-vidmind-locale”]
营救
''
结束
url=“#{settings.wildfire_base_url}#{relative_url}”
结果=零
如果缓存和!发展?
结果=从\u缓存获取\u(相对\u url,用于\u缓存的区域设置\u)
返回结果,除非结果为空?
结束
ConnectionPool.instance.start do | http|
if@session.api\u cookie
cookie=查找会话cookie(@cookieManager.cookies)
除非是饼干
cookie=WebAgent::cookie.new
cookie.name='JSESSIONID'
cookie.url=URI.parse(settings.wildfire\u base\u url)
cookie.domain=cookie.url.host
cookie.path=cookie.url.path
结束
cookie.value=@session.api\u cookie
@cookieManager.cookies=[]
@cookieManager.add(cookie)
http.cookie_manager=@cookieManager
request|cookies=http.cookies.map{| i |{i.name=>i.value}
其他的
http.cookie\u manager=WebAgent::CookieManager.new
请求_cookies=[{'JSESSIONID'=>''}]
结束
标题={}
头,合并!(选项[:标题])如果选项[:标题]
头,合并!({'Content-Type'=>options[:data\u-Type]})如果选项[:data\u-Type]
标题['X-Forwarded-For']=settings.Forwarded\u For if settings.Forwarded\u For
time\u start=time.now
response=http.request(类型、url、{
:query=>options[:query],
:body=>options[:data],
:头