1. 程式人生 > 其它 >EasySwoole訊息推送9 - 部署壓測

EasySwoole訊息推送9 - 部署壓測

技術標籤:easyswoole

概述
在所有的開發準備就緒的時候,開始準備和上線有關的事情:測試和服務部署,測試要注意程式碼的邏輯嚴
謹、程式碼執行的正確,服務穩定,所有的一切都需要用資料做指標,所有的計算機程式設計歸根結底都是數學。
0.系統初始化
0.1 Mysql 資料表初始化

需要如果在自定義程序或者自定義命令中需要使用到連線池,否則會報錯:Fatal error: Uncaught Swoole\Error: API must be called in the coroutine

//建立一個協程排程器
$scheduler = new \Swoole\Coroutine\Scheduler
(); $scheduler->add(function () { $builder = new \EasySwoole\Mysqli\QueryBuilder(); $builder->raw('select version()'); \EasySwoole\ORM\DbManager::getInstance()->query($builder, true); //這邊重置ORM連線池的pool,避免連結被克隆到子程序,造成連結跨程序公用。 //DbManager如果有註冊多庫連結,請記得一併getConnection($name)獲取全部的pool去執行reset
//其他的連線池請獲取到對應的pool,然後執行reset()方法 \EasySwoole\ORM\DbManager::getInstance()->getConnection()->getClientPool()->reset(); }); //執行排程器內註冊的全部回撥 $scheduler->start(); //清理排程器內可能註冊的定時器,不要影響到swoole server 的event loop \Swoole\Timer::clearAll();
0.2 分離配置檔案

bootstrap.php中定義開發環境定義開關,分離載入檔案,在載入時使用。

//環境變數開關 0本地,1測試 2線上
define('PUSHENV', 0);

PUSHENV使用

/**
 * 載入自定義配置檔案
 */
public static function loadConf()
{
    if(0 == PUSHENV){
        $ConfPath = EASYSWOOLE_ROOT . '/App/Conf/Dev';
    }else if(1 == PUSHENV){
        $ConfPath = EASYSWOOLE_ROOT . '/App/Conf/Test';
    }else if(2 == PUSHENV){
        $ConfPath = EASYSWOOLE_ROOT . '/App/Conf/Online';
    }
    $Conf  = Config::getInstance();
    $files = File::scanDirectory($ConfPath);
    if (!is_array($files['files'])) {
        return;
    }
    foreach ($files['files'] as $file) {
        $data = require_once $file;
        $Conf->setConf(strtolower(basename($file, '.php')), (array)$data);
    }
}
1.Nginx 部署服務

和部署普通的服務一樣,監聽9501埠,用IP雜湊的演算法去代理服務。

# 配置EasySwoole節點 至少需要一個
upstream stark {
    # 將負載均衡模式設定為IP hash,作用:不同的客戶端每次請求都會與同一節點進行互動。
    ip_hash;
    server 127.0.0.1:9501;
}

server {
    listen 80;
    server_name websocket.stark.com;

    location / {
        # websocket的header
        proxy_http_version 1.1;
        # 升級http1.1到websocket協議
        proxy_set_header Upgrade websocket;
        proxy_set_header Connection "Upgrade";

        # 將客戶端host及ip資訊轉發到對應節點  
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;

        # 客戶端與服務端60s之內無互動,將自動斷開連線。
        proxy_read_timeout 60s ;

        # 代理訪問真實伺服器
        proxy_pass http://stark;
    }
}
2.基於Jmeter的WebSocket壓力測試

下載Jmeter需要的WebSocket元件,進行壓測,根據下面的步驟,在這裡我就不做贅述了。

操作的時候有點心急,還是犯了一點點小錯誤,遇到問題首先做到的是要冷靜,然後想方法,不能煩躁和蠻幹,切記!

參考Url:

https://help.aliyun.com/document_detail/93627.html#title-kz3-0y7-7lr
https://help.aliyun.com/document_detail/91788.html?spm=a2c4g.11186623.2.30.681e1058OW8OxH
模組化使用、隔離和解耦

微服務之前被好多人提及,但是仔細想想它又是為了解決一個什麼問題而出現的呢?

在處理中我們使用瞭如下幾個步驟:

1.當有新增訊息時,遠端請求Api服務介面,接收資料寫入快取,非同步寫入資料庫

2.在Crontab中推送訊息,當推送成功後將訊息移除佇列

下面話不多說,看程式碼:

public function message(){
    $method = $this->request()->getMethod();
    if( 'post' !== strtolower($method) ){
        return $this->writeJson(Status::CODE_METHOD_NOT_ALLOWED,[],Status::getReasonPhrase(Status::CODE_METHOD_NOT_ALLOWED));
    }
    if( isset($this->params['uid']) && !empty($this->params['uid']) &&
    isset($this->params['msg']) && !empty($this->params['msg'])
    ){
        $noce_ack = $this->getNoceAck();
        $pushMsg = [
            'noce_ack' => $noce_ack,
            'to_uid' => $this->params['uid'],
            'is_sent' => 1,
            'is_read' => 0, #訊息未讀
            'msg_type' => 4,
            'client_type' => 0,
            'msg_extend' => $this->params['msg'],
            'create_time' => time(),
            'update_time' => 0
        ];
        $redis = $this->getRedisObject();
        $redis->lPush(self::PUSH_MSG_COMMENT_LISTS, json_encode($pushMsg));
        $this->addAsyncMysql($pushMsg,$this->params['uid']);
        return $this->writeJson(Status::CODE_OK,[],Status::getReasonPhrase(Status::CODE_OK));
    }else{
        return $this->writeJson(Status::CODE_REQUESTED_RANGE_NOT_SATISFIABLE,[],Status::getReasonPhrase(Status::CODE_REQUESTED_RANGE_NOT_SATISFIABLE));
    }
}

2.Crontab 消費程式碼

$number = 300;
$data = $redis->lRange(self::PUSH_MSG_COMMENT_LISTS, 0, $number );
if (!empty($data) && is_array($data)){
    $pushLists = [];
    $lRemList = [];
    foreach ($data as $value){
        $msgPushInfo = json_decode($value,true);
        var_dump('msgPushInfo:'.$msgPushInfo);
        if(isset($msgPushInfo['to_uid']) && !empty($msgPushInfo['to_uid'])){
            $lRemList[$msgPushInfo['to_uid']] = $value;
            $pushLists[$msgPushInfo['to_uid']]['uid'] = $msgPushInfo['to_uid'];
            $pushLists[$msgPushInfo['to_uid']]['noce_ack'] = $msgPushInfo['noce_ack'];
            $pushLists[$msgPushInfo['to_uid']]['msg_extend'] = json_decode($msgPushInfo['msg_extend'],true);
        }
    }
    if(isset($pushLists) && !empty($pushLists)){
        foreach ($pushLists as $value){
            $comment_msg = [
                'msg_type' => 4,
                'code' => 200,
                'body'  => json_encode($value['msg_extend']),
                'msg' => 'SUCCESS',
                'noce_ack' => $value['noce_ack']
            ];
            $fd = $redis->hGet(self::PUSH_MSG_USER_LOGIN,$value['uid']);
            if(isset($fd) && !empty($fd)){
                $ret = $server->push($fd,json_encode($comment_msg));
                //推送成功後,移除元素
                if($ret){
                    $redis->lRem(self::PUSH_MSG_COMMENT_LISTS,1,$lRemList[$value['uid']]);
                }
            }
        }
    }
}