国产+高潮+在线,国产 av 仑乱内谢,www国产亚洲精品久久,51国产偷自视频区视频,成人午夜精品网站在线观看

使用gateway-worker綁定了client和uid,如何判斷uid是否已離線?

命中水水水

問題描述

截圖

我有一個場景,像是車場道閘那種
1.gateway-worker作為服務端,然后道閘系統(tǒng)是客戶端,與服務端建立tcp長連接。(客戶端會向服務端發(fā)送心跳,5s/次)
2.用戶請求服務端,獲取道閘上的金額,進行支付

假設道閘客戶端連接到gateway-worker的client_id是001,車場的客戶端編號是A,那在道閘連接上服務端的時候,會進行client_id 001和編號A的綁定Gateway::bindUid(001, A)。那后續(xù)有用戶發(fā)起http請求獲取金額的時候,請求中也會傳遞車場編號A,那服務端會向客戶端A發(fā)送tcp請求獲取金額。

現(xiàn)在有一種情況,就是道閘系統(tǒng)有時候網(wǎng)絡不好,老是斷網(wǎng),斷網(wǎng)情況下,道閘客戶端就沒辦法向服務端及時發(fā)送心跳包,服務端也不知道這個客戶端A離線了,這時候如果有http請求進來,服務端會繼續(xù)向客戶端A發(fā)送請求獲取金額,這時候就會導致進程阻塞,導致http請求一直在請求中,直接影響到后續(xù)的http請求也進不來

想問下,這種情況應該如何處理呢?

為此你搜索到了哪些方案及不適用的原因

1、在向客戶端A發(fā)送請求前,檢測客戶端A是否離線
使用Gateway::isOnline(string $client_id)先檢測客戶端A狀態(tài)。但是這種方法建立在client_id觸發(fā)了onClose回調(diào),像斷網(wǎng)這種情況,客戶端是沒辦法觸發(fā)onClose回調(diào)

2、服務端向客戶端發(fā)送心跳包檢測客戶端狀態(tài)
我看官方文檔可以通過服務端向客戶端發(fā)送心跳包檢測客戶端狀態(tài),假設我設置心跳包3s/次,但是這種也會存在心跳包剛檢測客戶端正常,過了1s客戶端因為斷網(wǎng)離線了,這時候有http請求進來,服務端還不知道客戶端連接不上了,還是會出現(xiàn)上面我所說的情況

想問下大家有更合適的方案嗎?

1061 2 5
2個回答

walkor 打賞

http請求可以直接用workerman或者webman自定義進程來做,做成非阻塞http服務,包括服務端會繼續(xù)向客戶端A發(fā)送請求獲取金額這個過程做成非阻塞的請求。
如果你不會做,也可以把服務端會繼續(xù)向客戶端A發(fā)送請求獲取金額這個過程設置個超時時間,比如1秒。

walkor 打賞

準備工作

安裝channel

composer require workerman/channel

代碼

start.php

<?php
use Workerman\Worker;
define('GLOBAL_START', 1);
require_once __DIR__ . '/vendor/autoload.php';

// 加載所有Applications/*/start.php,以便啟動所有服務
foreach(glob(__DIR__.'/Applications/*/start*.php') as $start_file)
{
    require_once $start_file;
}

// channel服務用來多進程或者跨服務器通訊
$channel_server = new Channel\Server('0.0.0.0', 2206);

// 保存設備到connection的映射
global $connection_maps;
$connection_maps = [];
$http_worker = new Worker('http://0.0.0.0:1234');
$http_worker->onWorkerStart = function () {
    // Channel客戶端連接到Channel服務端
    Channel\Client::connect('127.0.0.1', 2206);
    Channel\Client::on('get_amount_result', function($data)  {
        global $connection_maps;
        $device_id = $data['device_id'];
        foreach ($connection_maps[$device_id]??[] as $connection) {
            $connection->close($data['amount']);
        }
    });
};
$http_worker->onMessage = function (\Workerman\Connection\TcpConnection $connection, \Workerman\Protocols\Http\Request $request) {
    global $connection_maps;
    $device_id = $request->get('device_id');
    if (!$device_id) {
        $connection->send('not found');
        return;
    }
    $connection->device_id = $device_id;
    $connection_maps[$device_id][$connection->id] = $connection;
    // 通過channel向gatewayWorker咨詢客戶端金額
    Channel\Client::publish('get_amount', [
        'device_id' => $device_id
    ]);
};
$http_worker->onClose = function (\Workerman\Connection\TcpConnection $connection) {
    if (empty($connection->device_id)) {
        return;
    }
    // 刪除$connection_maps對應的連接,避免內(nèi)存泄漏
    $device_id = $connection->device_id;
    global $connection_maps;
    unset($connection_maps[$device_id][$connection->id]);
    if (empty($connection_maps[$device_id])) {
        unset($connection_maps[$device_id]);
    }
};

// 運行所有服務
Worker::runAll();

Events.php

<?php
use \GatewayWorker\Lib\Gateway;
class Events
{
    public static function onWorkerStart($worker)
    {
        // Channel客戶端連接到Channel服務端
        Channel\Client::connect('127.0.0.1', 2206);
        // 只需要在0號進程上開啟get_amount監(jiān)聽
        if ($worker->id !== 0) {
            Channel\Client::on('get_amount', function($data)  {
                $device_id = $data['device_id'];
                Gateway::sendToUid($device_id, 'get_amount');
            });
        }
    }
   public static function onMessage($client_id, $message)
   {
       // 忽略客戶端心跳
       if ($message === '{"type":"ping"}') {
           return;
       }
       // 假設客戶端發(fā)的第一個消息當作device_id
       if (empty($_SESSION['device_id'])) {
           $device_id = trim($message);
           Gateway::bindUid($client_id, $device_id);
           $_SESSION['device_id'] = $device_id;
           return;
       }
       // 客戶端后續(xù)發(fā)的消息當作金額
       $amount = $message;
       // 通知http進程得到device_id的金額
       Channel\Client::publish('get_amount_result', [
           'device_id' => $_SESSION['device_id'],
           'amount' => $amount
       ]);
   }
}

啟動

php start.php start

測試

瀏覽器打開頁面 http://127.0.0.1:1234/?device_id=d2

開啟一個終端,輸入

telnet 127.0.0.1 8282
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
d2
800

提示
d2代表設備id
800代表金額

瀏覽器顯示 800
截圖

原理

啟動一個channel server作為多進程或者跨服務器通訊組件
businessWorker的Events里onWorkerStart連接channelserver并監(jiān)聽http進程發(fā)來的get_amount請求(請求中包含設備id)
啟動一個httpworker作為http接口,onWorkerStart里連接channelserver并監(jiān)聽get_amount_result結(jié)果(結(jié)果中包含設備id)
瀏覽器向httpworker發(fā)起請求,httpworker獲得要查詢的設備id,并將連接保存到connection_maps中,然后通過channel發(fā)布一個get_amount事件給businessWorke的Events.php
設備返回金額后在Events里的onMessage里通過channel發(fā)送get_amount_result事件通知http進程對應的設備返回了金額
httpworker獲得金額后查找本地連接里($connection_maps)是否有查詢對應設備金額的連接,有的話返回金額

  • walkor 2023-11-19

    整個過程是非阻塞的,可以承受很大的設備在線數(shù)

  • 命中水水水 2023-11-19

    謝謝大佬,受教了~ 我研究一下,謝謝~

  • 小W 2023-11-20

    Events.php中“ // 只需要在0號進程上開啟get_amount監(jiān)聽 if ($worker->id !== 0) { ” 應該是 $worker->id == 0吧。

  • 小W 2023-11-20

    如果設備不在線,http請求會不會一直等待響應?

  • walkor 2023-11-20

    對,應該是 $worker->id === 0 會。設備不在線會一直等,服務端可以設置定時器去返回數(shù)據(jù),也可以客戶端設置超時

  • 第六人 2023-11-21

    學習了

年代過于久遠,無法發(fā)表回答
??