복붙노트

[REDIS] 연결은 - 레디 스 - 경쟁 조건에 대한 세션 개체를 보호하는 방법

REDIS

연결은 - 레디 스 - 경쟁 조건에 대한 세션 개체를 보호하는 방법

나는 세션 데이터를 저장하는 연결 - 레디 스에 nodejs를 사용하고 있습니다.

나는 세션에서 사용자 데이터를 저장하고, 세션 일생을 위해 사용하고있다.

나는 그것이 세션 데이터를 변경 두 요청 사이의 경쟁 조건을 가질 수 있다고 나타났습니다.

나는 세션을 잠그는 레디 스 잠금을 사용하려했지만, 나를 위해 약간의 문제가 있습니다.

나는 전체 세션을 잠 그려, 대신 특정 세션 변수를 고정하지 않습니다.

나는 그것이 불가능 발견, 나는 그것을 해결하는 방향에 대해 생각 :

사용자 데이터를 저장하는 세션 객체를 사용을 중지하고, 레디 스에서 직접 변수를 저장하고 잠금을 사용하기 전에.

나는 그것이 일 수 있다는 것을 알고 있지만, 그것은 나를 그냥 세션 객체를 통해 레디 스에 접근하는 대신 수동으로 모든 개체를 관리 할 필요합니다.

당신은 나와 함께 모범 사례 및 제안을 공유하시기 바랍니다 수 있습니까?

감사, 리 오르

해결법

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

    1.글쎄, 당신의 자신의 스토리지를 구현하는 당신을위한 옵션이 될 수 있습니다. 당신이 할 필요가 세 가지 방법을 구현하는 것입니다이 문서 쇼 : 갔지,은 .set 및 .destroy는 (마지막 단락 참조). 그것은이 같은 (노드 - 레디 스 라이브러리를 사용하여 원래의 연결 - 레디 스 조금을 저장 수정)을 것입니다 :

    글쎄, 당신의 자신의 스토리지를 구현하는 당신을위한 옵션이 될 수 있습니다. 당신이 할 필요가 세 가지 방법을 구현하는 것입니다이 문서 쇼 : 갔지,은 .set 및 .destroy는 (마지막 단락 참조). 그것은이 같은 (노드 - 레디 스 라이브러리를 사용하여 원래의 연결 - 레디 스 조금을 저장 수정)을 것입니다 :

    var redis = require("redis"),
        redis_client = redis.createClient(),
        session_prefix = 'session::',
        lock_suffix = '::lock',
        threshold = 5000,
        wait_time = 250,
        oneDay = 86400;
    
    /* If timeout is greater then threshold, then we assume that
       one of the Redis Clients is dead and he cannot realese
       the lock. */
    
    function CustomSessionStore(opts) {
        opts = opts || {};
        var self = this;
        self.ttl = opts.ttl; // <---- used for setting timeout on session
    
        self.lock = function(sid, callback) {
            callback = callback || function(){};
            var key = session_prefix + sid + lock_suffix;
            // try setting the lock with current Date
            redis_client.setnx(key, Date.now( ), function(err, res) {
                // some error handling?
                if (res) {
                    // Everything's fine, call callback.
                    callback();
                    return;
                }
    
                // setnx failed, look at timeout
                redis_client.get(key, function(err, res) {
                    // some error handling?
                    if (parseInt(res) + threshold > Date.now( )) {
                        // timeout, release the old lock and lock it
                        redis_client.getset(key, Date.now( ), function(err, date) {
                            if (parseInt(date) + threshold > Date.now()) {
                                // ups, some one else was faster in acquiring lock
                                setTimeout(function() {
                                    self.lock(sid, callback);
                                }, wait_time);
                                return;
                            }
                            callback();
                        });
                        return;
                    }
                    // it is not time yet, wait and try again later
                    setTimeout(function() {
                        self.lock(sid, callback);
                    }, wait_time);
                });
            });
        };
    
        self.unlock = function(sid, callback) {
            callback = callback || function(){};
            var key = session_prefix + sid + lock_suffix;
            redis_client.del(key, function(err) {
                // some error handling?
                callback();
            });
        };
    
        self.get = function(sid, callback) {
            callback = callback || function(){};
            var key = session_prefix + sid;
            // lock the session
            self.lock(sid, function() {
                redis_client.get(key, function(err, data) {
                    if (err) {
                        callback(err);
                        return;
                    }
                    try {
                        callback(null, JSON.parse(data));
                    } catch(e) {
                        callback(e);
                    }
                });
            });
        };
    
        self.set = function(sid, data, callback) {
            callback = callback || function(){};
            try {
                // ttl used for expiration of session
                var maxAge = sess.cookie.maxAge
                  , ttl = self.ttl
                  , sess = JSON.stringify(sess);
    
                ttl = ttl || ('number' == typeof maxAge
                      ? maxAge / 1000 | 0
                      : oneDay);
    
            } catch(e) {
                callback(e);
                return;
            }
            var key = session_prefix + sid;
            redis_client.setex(key, ttl, data, function(err) {
                // unlock the session
                self.unlock(sid, function(_err) {
                    callback(err || _err);
                });
            });
        };
    
        self.destroy = function(sid, callback) {
            var key = session_prefix + sid;
            redis_client.del(key, function(err) {
                redis_client.unlock(sid, function(_err) {
                    callback(err || _err);
                });
            });
        };
    }
    

    사이드 노트 : 나는 .lock 및 .unlock에 대한 오류 처리를 구현하지 않았다. 난 당신이 최대 떠날거야! : 약간의 실수가있을 수 있습니다 (I 순간에 NodeJS가없는 나는 내 기억에서 이것을 쓰고 있어요 : D),하지만 당신은 아이디어를 이해해야합니다. 여기 레디 스 잠금 해제 / 잠금에 대한 setnx을 사용하는 방법에 대한 논의를 포함하는 링크입니다.

    다른 참고 : 당신은 아마 어떤 경로 예외가 발생하는 경우, 다음 레디 스 세션 잠금이 해제되지 않기 때문에, 루트에 대한 처리를 일부 사용자 지정 오류를 해보고 싶어요. 은 .set 방법은 항상 마지막 경로에서 일이라고한다 - 익스프레스는 매우 경로의 시작에서 호출 갔지 방법과 반대 (즉, 내가은 .set에 갔지 및 해제에 잠금 이유의). 그것이 비록 문제가 될 필요가 없습니다, 그래서 아직 당신은 5 초 동안 만 잠 깁니다. 사용자의 요구 (특히 임계 값 및 WAIT_TIME 변수)에 조정을해야합니다.

    최종 참고 사항 :이 메커니즘을 요청 처리기는 사용자 당 잇달아 발사됩니다. 이 방법, 당신은 사용자 당 동시 핸들러를 실행할 수 없게됩니다. 다른 아이디어가 수동으로 잠금 해제 / 세션 및 핸들 잠금 외부 데이터를 개최하기 때문에 이러한 현상이 문제가 될 수 있습니다. 결국, 수동으로 처리해야 할 몇 가지가있다.

    나는 그것이 도움이되기를 바랍니다! 행운을 빕니다!

  2. from https://stackoverflow.com/questions/11420982/connect-redis-how-to-protect-the-session-object-against-race-condition by cc-by-sa and MIT license