복붙노트

[REDIS] 수평 서버에서 socket.io 사용자를 계산

REDIS

수평 서버에서 socket.io 사용자를 계산

나는 여러 socket.io 서버가 redisstore를 사용하여 수평으로 확장해야합니다. 나는 효과적으로 객실 설정을 가지고 지금은 상태 페이지와 내가 단지를 가로 질러 연결된 사용자의 수를 계산하는 방법입니다 파악에 실패하고있어 구축을 위해 노력하고있어 등 서버에서 객실로 방송을 성공적으로 수 있어요했습니다 모든 서버.

io.sockets.clients ( '방')와 io.sockets.sockets는 해당 하나 개의 서버가 아닌 같은 RedisStore에 연결된 모든 서버에 당신에게 연결된 클라이언트의 수를 알려줍니다.

제안?

감사.

해결법

  1. ==============================

    1.사용자가 채팅방에 연결하면 원자 당신의 RedisStore에서 사용자 카운터를 증가 할 수 있습니다. 사용자의 연결을 끊습니다, 당신은 값이 감소합니다. 이 방법 레디 스 사용자 수를 유지하고 모든 서버에 액세스 할 수 있습니다.

    사용자가 채팅방에 연결하면 원자 당신의 RedisStore에서 사용자 카운터를 증가 할 수 있습니다. 사용자의 연결을 끊습니다, 당신은 값이 감소합니다. 이 방법 레디 스 사용자 수를 유지하고 모든 서버에 액세스 할 수 있습니다.

    참조 INCR과 DECR

    SET userCount = "0"
    

    때 사용자가 연결 :

    INCR userCount
    

    때 사용자의 연결을 끊습니다 :

    DECR userCount
    
  2. ==============================

    2.여기에 내가 레디 스 스크립팅을 사용하여 해결하는 방법입니다. 이 때문에 가장 가능성이 여전히 지금 자신의 인스턴스를 컴파일 할 필요 버전 2.6 이상이 필요합니다.

    여기에 내가 레디 스 스크립팅을 사용하여 해결하는 방법입니다. 이 때문에 가장 가능성이 여전히 지금 자신의 인스턴스를 컴파일 할 필요 버전 2.6 이상이 필요합니다.

    프로세스가 시작될 때마다, 나는 새로운 UUID를 생성하고 전역에 둡니다. 나는 PID를 사용할 수 있지만이 좀 더 안전을 느낀다.

    # Pardon my coffeescript
    processId = require('node-uuid').v4()
    

    사용자 커넥트합니다 (socket.io 연결 이벤트), 나는 그 프로세스 ID를 기반으로 사용자의 목록에 해당 사용자의 ID를 누르면. 또한 30 초 해당 키의 만료를 설정합니다.

    RedisClient.lpush "process:#{processId}", user._id
    RedisClient.expire "process:#{processId}", 30
    

    때 사용자의 연결을 끊습니다 (연결 해제 이벤트), 나는 그것을 제거하고 만료를 업데이트합니다.

    RedisClient.lrem "process:#{processId}", 1, user._id
    RedisClient.expire "process:#{processId}", 30
    

    나는 키가 있도록이를 본질적으로 "핑 (ping)"을 30 초 간격으로 실행 유지한다는 것 또한 설치 기능. 프로세스가 실수로 죽을 않는 경우에 따라서, 모든 사용자 세션은 본질적으로 사라집니다.

    setInterval ->
      RedisClient.expire "process:#{processId}", 30
    , 30 * 1000
    

    이제 마법. 레디 스 2.6 본질적 기능의 저장 순서 정렬을 제공 LUA 스크립트를 포함한다. 정말 빠르고되지 매우 프로세서 집약적 인 (그들은 "거의"C 코드를 실행에 비교)입니다.

    내 저장 프로 시저는 기본적으로 프로세스 목록의 모든을 통해 루프, 및 사용자 생성 : 현재 로그인의 총 카운트 USER_ID 키를 누릅니다. 그들은 등 두 개의 브라우저로 로그인하는 경우 여전히 나를 그들의 세션 중 하나를 완전히 분리, 아니면 그냥 한 경우 알려 논리를 사용할 수 있도록거야이 의미합니다.

    내 모든 프로세스에 15 초마다, 또한 연결 / 분리 이벤트 후이 기능을 실행합니다. 내 사용자 수가 가장 가능성이 두 번째로 정확하고보다 15 ~ 30 초 동안 결코 잘못된 것이라고이 의미합니다.

    그를 생성하는 코드는 함수 외모처럼 레디 스 :

    def = require("promised-io/promise").Deferred
    
    reconcileSha = ->
      reconcileFunction = "
        local keys_to_remove = redis.call('KEYS', 'user:*')
        for i=1, #keys_to_remove do
          redis.call('DEL', keys_to_remove[i])
        end
    
        local processes = redis.call('KEYS', 'process:*')
        for i=1, #processes do
          local users_in_process = redis.call('LRANGE', processes[i], 0, -1)
          for j=1, #users_in_process do
            redis.call('INCR', 'user:' .. users_in_process[j])
          end
        end
      "
    
      dfd = new def()
      RedisClient.script 'load', reconcileFunction, (err, res) ->
        dfd.resolve(res)
      dfd.promise
    

    그리고 내가 가진 나중에 내 스크립트에 그것을 사용할 수 있습니다 :

    reconcileSha().then (sha) ->
      RedisClient.evalsha sha, 0, (err, res) ->
        # do stuff
    

    내가 할 마지막 일은 시도하고 프로세스가이 (가) 시간 제한 실제로 닫힌다 아래로 우아를 레디 스에 의존하지하는 것이 가장 좋습니다 시도 있는지 확인하기 위해 일부 종료 이벤트를 처리합니다.

    gracefulShutdown = (callback) ->
      console.log "shutdown"
      reconcileSha().then (sha) ->
        RedisClient.del("process:#{processId}")
        RedisClient.evalsha sha, 0, (err, res) ->
          callback() if callback?
    
    # For ctrl-c
    process.once 'SIGINT', ->
      gracefulShutdown ->
        process.kill(process.pid, 'SIGINT')
    
    # For nodemon
    process.once 'SIGUSR2', ->
      gracefulShutdown ->
        process.kill(process.pid, 'SIGUSR2')
    

    지금까지 큰 노력하고 있어요.

    나는 아직도하고 싶은 한가지는 그래서 레디 스가 반환 작동하는지 확인 자신의 값을 변경 한 모든 키. (프로세스 죽으면 같은) 카운트가 서버의없이 특정 사용자에 대한 변경 한 경우 실제로 이벤트를 보낼 수있는이 방법을 적극적으로 알고. * 값이 다시이 변경된 것을 알고 : 지금, 나는 폴링에 사용자를 의존해야합니다. 그것은 작동하지만, 더 좋을 수 ...

  3. ==============================

    3.나는 각 서버는 주기적으로 자신의 PID를 포함 만료와 레디 스에서 사용자 수를 설정함으로써이 문제를 해결 :

    나는 각 서버는 주기적으로 자신의 PID를 포함 만료와 레디 스에서 사용자 수를 설정함으로써이 문제를 해결 :

    모든는 SETEX USERCOUNT을 <간격 + 10> <수>

    다음 상태의 서버는 이러한 각 키를 조회 할 수 있으며, 각 키에 대한 값을 얻을 :

    각 키에 대한 USERCOUNT * 할 총 + = GET <키>

    서버가 중단되거나 그래서 경우 종료 후 그 수는 간격 후 레디 스 밖으로 떨어질 것 + 10

    죄송 추악한 의사에 대해. :)

  4. ==============================

    4.당신은 값을 저장하는 해시 키를 사용할 수 있습니다.

    당신은 값을 저장하는 해시 키를 사용할 수 있습니다.

    서버 1에 사용자 커넥트 당신은 "userCounts"이라는 키에 "SRV1"라는 필드를 설정할 수 있습니다합니다. 다만 현재의 카운트가 HSET을 사용하고 무엇에 가치를 우선합니다. 증가 / 감소 할 필요가 없습니다. 그냥 socket.io으로 알려져 현재의 값을 설정합니다.

    HSET userCounts srv1 "5"
    

    다른 사용자가 다른 서버에 연결하면 다른 필드를 설정합니다.

    HSET userCounts srv2 "10"
    

    그런 다음 모든 서버는 "userCounts"에서 모든 필드를 반환하고 값 목록을 반환 HVALS를 사용하여 함께 추가하여 총을 얻을 수 있습니다.

    HVALS userCounts
    

    서버가 충돌 할 때 충돌이 제거됩니다 userCounts 또는 HSET에서 서버의 현장을 "0"으로 응답 스크립트를 실행해야합니다.

    당신은 서버를 다시 시작 자동화 영원히 볼 수 있습니다.

  5. from https://stackoverflow.com/questions/12164783/counting-socket-io-users-across-horizontal-servers by cc-by-sa and MIT license