PHP: Как проверить, будет ли session_start заблокирован или истечет время ожидания - PullRequest
0 голосов
/ 07 марта 2019

В определенном случае я хочу отменить вызовы пользователей, у которых уже есть открытый сеанс.Я использую session_start, чтобы убедиться, что вошедший в систему пользователь может выполнять только один запрос за раз, и это прекрасно работает.Но все последующие вызовы будут просто блокироваться на неопределенный срок до тех пор, пока не будут выполнены все предыдущие вызовы, что неудовлетворительно при определенных обстоятельствах, например, неправильном поведении пользователей.

Обычно все известные мне блокирующие вызовы имеют параметр тайм-аута, который вы передаете им.Есть ли что-то подобное для start_session?

Или есть вызов в духе session_opened_by_other_script, который я могу сделать перед вызовом session_start?

1 Ответ

0 голосов
/ 12 марта 2019

На данный момент мое решение состоит в том, чтобы проверить, есть ли уже блокировка файла сеанса, используя exec сценарии оболочки. Я не рекомендую использовать его тем, кто не полностью понимает его.

В основном он пытается получить блокировку файла сеанса для указанного значения времени ожидания, используя flock.Если это не удается сделать, он существует с тайм-аутом 408 запроса.(или 429 Слишком много запросов, если доступно)

Чтобы это работало, вам необходимо ...

  1. знать свой идентификатор сеанса на тот момент времени
  2. есть файловые сессии

Обратите внимание, что это не атомарное .Все еще может случиться так, что несколько запросов в конечном итоге ожидают в session_start.Но это должно быть редкое событие.Большинство вызовов должны быть отменены правильно, что было моей повесткой дня.

class Session {

    static public function openWhenClosed() {

        if (session_status() == PHP_SESSION_NONE) {

            $sessionId = session_id();
            if ($sessionId == null)
                $sessionId = $_COOKIE[session_name()];

            if ($sessionId != null) {
                $sessFile = session_save_path()."/sess_".$sessionId;

                if (file_exists($sessFile)) {
                    $timeout = 30; //How long to try to get hold of the session
                    $fd = 9; //File descriptor to use to try locking the session file.

                    /*
                    * This 'trick' is not atomic!!
                    * After exec returned and session_start() is called there is a time window
                    * where it can happen that other waiting calls get a successful lock and also
                    * proceed and get then blocked by session_start(). The longer the sleep value
                    * the less likely this is to happen. But also the longer the extra delay
                    * for the call
                    */
                    $sleep = "0.01"; //10ms

                    //Check if session file is already locked by trying to get a lock on it.
                    //If it is, try again for $timeout seconds every $sleep seconds
                    exec("
                                exec $fd>>$sessFile;
                                while [ \$SECONDS -lt $timeout ]; do
                                    flock -n $fd;
                                    if [ \$? -eq 0 ]; then exit 0; fi;
                                    sleep $sleep;
                                done;
                                exit 1;
                            ", $null, $timedOut);



                    if ($timedOut) {
                        http_response_code(408); //408: Request Timeout. Or even better 429 if your apache supports it

                        die("Request canceled because another request is still running");
                    }
                }
            }

            session_start();
        }
    }
}

Дополнительные мысли:

  1. Заманчиво использовать flock -w <timeout> но такгораздо больше ожидающих в очереди вызовов смогут использовать время между exec и start_session для получения блокировки и в конечном итоге блокировать в session_start
  2. Если вы используете браузер для тестирования этого, имейте в виду, чтобольшинство браузеров делают очереди команд и повторно используют ограниченное количество соединений.Поэтому они не начинают отправлять ваш запрос раньше, чем другие завершат.Это может привести к, казалось бы, странным результатам, если вы не знаете об этом.Вы можете более надежно тестировать, используя несколько параллельных команд wget.
  3. Я не рекомендую активировать это для обычного запроса браузера.Как упомянуто в 2), в большинстве случаев это уже обрабатывается браузером.Я использую его только для защиты моего API от реализаций румян, которые не ждут ответа перед отправкой следующего запроса.
  4. В моих тестах для моей общей нагрузки снижение производительности было незначительным.Но я бы посоветовал проверить себя в вашей среде, используя microtime() звонки
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...