EasySwoole訊息推送9 - 部署壓測
阿新 • • 發佈:2021-01-17
技術標籤: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']]);
}
}
}
}
}