使用的是官方的demo
客戶端代碼
require __DIR__ . '/workerman/Autoloader.php';
use Workerman\Worker;
use Workerman\Lib\Timer;
use Workerman\Connection\AsyncTcpConnection;
$worker = new Worker();
$worker->onWorkerStart = 'connect';
function connect(){
static $count = 0;
// 2000個鏈接
if ($count++ >= 10000) return;
// 建立異步鏈接
$con = new AsyncTcpConnection('ws://127.0.0.1:3458');
$con->onConnect = function($con) {
// 遞歸調(diào)用connect
connect();
};
$con->onMessage = function($con, $msg) {
//echo "recv $msg\n";
};
$con->onClose = function($con) {
echo "con close\n";
};
// 當前鏈接每10秒發(fā)個心跳包
Timer::add(10, function()use($con){
$con->send("ping");
});
$con->connect();
echo $count, " connections complete\n";
}
Worker::runAll();
服務端代碼
public static function onConnect($client_id)
{
// 向當前client_id發(fā)送數(shù)據(jù)
Gateway::sendToClient($client_id, "Hello $client_id\r\n");
// 向所有人發(fā)送
Gateway::sendToAll("$client_id login\r\n");
}
/**
* 當客戶端發(fā)來消息時觸發(fā)
* @param int $client_id 連接id
* @param mixed $message 具體消息
*/
public static function onMessage($client_id, $message)
{
// 向所有人發(fā)送
// Gateway::sendToAll("$client_id said $message\r\n");
}
服務器8核
客戶端與服務器在同一臺服務器上 本機訪問
gateaway進程8個 business24個
cpu占用一半 內(nèi)存幾乎沒用
event內(nèi)核已安裝 linux內(nèi)核已優(yōu)化
onMessage里的廣播沒屏蔽前跑到兩千多就卡了,屏蔽后1w連接需要6,7分鐘才能完成
想請教下是原本就是這樣還是我linux優(yōu)化不到位
/etc/sysctl.conf配置
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time=120
# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce=2
net.ipv4.conf.all.arp_announce=2
net.core.somaxconn= 65535
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_tw_recycle = 0
fs.file-max = 6815744
# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 20000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
kernel.sysrq=1
kernel.pid_max=3999999
ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 63456
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 102400
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 63456
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
廣播Gateway::sendToAll是很耗時的操作,需要和所有gateway通訊,并且將數(shù)據(jù)發(fā)給所有在線連接,如果在線人數(shù)多會占用大量的cpu資源和帶寬資源。所以不要輕易調(diào)用Gateway::sendToAll廣播數(shù)據(jù)。
一般來說普通服務器包量極限大概在10W-20W/S左右,超過這個量服務器負載就很高了,會很慢。如果是多隊列網(wǎng)卡可以成倍增加這個極限。
你的業(yè)務代碼是在onConnect也就是在有客戶端連接時調(diào)用Gateway::sendToAll廣播。這樣會導致隨著連接越來越多最后連接速度越來越慢。原因主要是當連接很多時廣播數(shù)據(jù)會造成大量的包量和流量,假設已經(jīng)有1w個連接,每秒有10個新連接連上來,那就是每秒廣播10次數(shù)據(jù)給1w個連接,每秒10W個數(shù)據(jù)包,假設每個數(shù)據(jù)包100字節(jié),那么每秒產(chǎn)生的流量大約為 80Mb/s的數(shù)據(jù)。這么大的數(shù)量和包量一般的服務器已經(jīng)是極限了。假設每個數(shù)據(jù)包是1000字節(jié),那么流量直接干到800Mb,需要服務器有G口的帶寬了。要知道一個普通機房出口帶寬才10G-20G左右。
如果你在onMessage里也用Gateway::sendToAll廣播,由于客戶端是每10秒發(fā)送一個ping,當服務器有2000個連接時,也就是服務端每秒收到200個ping,然后每秒廣播200次$client_id said $message
。則每秒產(chǎn)生的包量為2000*200=20W
個包,產(chǎn)生的流量大概150M左右。同樣普通服務器支撐不了這么大的包量。
所以不是隨便寫幾行代碼就能支撐萬人在線,當在線人數(shù)很多時要考慮的地方很多。最主要的是減少通訊請求量,尤其不要隨便廣播數(shù)據(jù)。
我第一感覺也是包太多,后臺看流量達到150m了 本還希冀于我優(yōu)化沒搞好。請問正常業(yè)務下統(tǒng)計在線人數(shù)給用戶 還有用戶發(fā)來消息,展示給所有人有什么好的方法么 直播間大概1w人吧
統(tǒng)計在線人數(shù)有專門的接口,但是你千萬別有個新連接就調(diào)用接口統(tǒng)計一次并廣播,那樣你會看到數(shù)字一直在跳,而且浪費cpu和帶寬。你可以在0號進程開個定時器10秒統(tǒng)計一次。如果人數(shù)很多,上萬人,實際上不需要100%精確統(tǒng)計,誤差1000都可以接受。超過1萬人你可以顯示為1.x萬人,如果本次統(tǒng)計1.x與上次統(tǒng)計1.x相等,則可以省略一次廣播推送。同理幾千人在線的話,100內(nèi)的誤差忽略不記,顯示為x千人。記住廣播很耗費資源,能省就省。