Redis + ActionController::Live threads not dying

前端 未结 6 1325
自闭症患者
自闭症患者 2020-11-28 04:09

Background: We\'ve built a chat feature in to one of our existing Rails applications. We\'re using the new ActionController::Live module and ru

6条回答
  •  执笔经年
    2020-11-28 04:17

    Here you are solution with timeout that will exit blocking Redis.(p)subscribe call and kill unused connection tread.

    class Stream::FixedController < StreamController
      def events
        # Rails reserve a db connection from connection pool for
        # each request, lets put it back into connection pool.
        ActiveRecord::Base.clear_active_connections!
    
        # Last time of any (except heartbeat) activity on stream
        # it mean last time of any message was send from server to client
        # or time of setting new connection
        @last_active = Time.zone.now
    
        # Redis (p)subscribe is blocking request so we need do some trick
        # to prevent it freeze request forever.
        redis.psubscribe("messages:*", 'heartbeat') do |on|
          on.pmessage do |pattern, event, data|
            # capture heartbeat from Redis pub/sub
            if event == 'heartbeat'
              # calculate idle time (in secounds) for this stream connection
              idle_time = (Time.zone.now - @last_active).to_i
    
              # Now we need to relase connection with Redis.(p)subscribe
              # chanel to allow go of any Exception (like connection closed)
              if idle_time > 4.minutes
                # unsubscribe from Redis because of idle time was to long
                # that's all - fix in (almost)one line :)
                redis.punsubscribe
              end
            else
              # save time of this (last) activity
              @last_active = Time.zone.now
            end
            # write to stream - even heartbeat - it's sometimes chance to
            # capture dissconection error before idle_time
            response.stream.write("event: #{event}\ndata: #{data}\n\n")
          end
        end
        # blicking end (no chance to get below this line without unsubscribe)
      rescue IOError
        Logs::Stream.info "Stream closed"
      rescue ClientDisconnected
        Logs::Stream.info "ClientDisconnected"
      rescue ActionController::Live::ClientDisconnected
        Logs::Stream.info "Live::ClientDisconnected"
      ensure
        Logs::Stream.info "Stream ensure close"
        redis.quit
        response.stream.close
      end
    end
    

    You have to use reds.(p)unsubscribe to end this blocking call. No exception can break this.

    My simple app with information about this fix: https://github.com/piotr-kedziak/redis-subscribe-stream-puma-fix

提交回复
热议问题