最近有个项目需求是这样的:软件端会向服务器发送请求,获取一些信息。然后对获取过来的信息进行处理,再返回信息的id
方式一:软件端那边调用接口,一分钟请求一次。
问题出现:如果有几万个人同时用软件,就会产生大量的http请求,导致负载过高。因此需求考虑到第二种方式。
方式二:使用WebSocket,进行一次长连接,这样就减少了大量的请求。
因为对于webscoket要求不高,因此就选择了Workerman,这也是thinkphp5封装好的扩展,直接下载下来用。
参考手册:thinkphp5.1和workerman开发手册
下面开始:
使用composer下载:
composer require topthink/think-worker
下载下来后会有一些文件,重点看config里面的worker.php和worker_server.php。这两个是不同的监听配置,我使用的是worker_server.php
它的默认配置是这样的
return [
// 扩展自身需要的配置
'protocol' => 'websocket', // 协议 支持 tcp udp unix http websocket text
'host' => '0.0.0.0', // 监听地址
'port' => 2346, // 监听端口
'socket' => 'hd.com:2346', // 完整监听地址
'context' => [], // socket 上下文选项
'worker_class' => '', // 自定义Workerman服务类名 支持数组定义多个服务
// 支持workerman的所有配置参数
'name' => 'thinkphp',
'count' => 4,
'daemonize' => false,
'pidFile' => Env::get('runtime_path') . 'worker.pid',
// 支持事件回调
// onWorkerStart
'onWorkerStart' => function ($worker) {
},
// onWorkerReload
'onWorkerReload' => function ($worker) {
},
// onConnect
'onConnect' => function ($connection) {
},
// onMessage
'onMessage' => function ($connection, $data) {
$connection->send('你成功了');
},
// onClose
'onClose' => function ($connection) {
},
// onError
'onError' => function ($connection, $code, $msg) {
echo "error [ $code ] $msg\n";
},
];
这时你打开命令行(cmd),切换到你项目根目录下
E:\wamp64\www\hd\v2018>E:\wamp64\bin\php\php7.0.4\php.exe think worker:server
因为我没有配置php的环境变量,所有就时候这种方式了。这时会看到这样的结果说明服务启动成功
打开chrome浏览器,按F12打开调试控制台,在Console一栏输入(或者把下面代码放入到html页面用js运行)
// 假设服务端ip为127.0.0.1
ws = new WebSocket("ws://127.0.0.1:2000");
ws.onopen = function() {
alert("连接成功");
};
ws.onmessage = function(e) {
alert("收到服务端的消息:" + e.data);
};
这是你会收到“你成功了”的字样。
接下来就是真正的开发流程了。
一般情况下我们会创建一个服务类(必须要继承 think\worker\Server
),然后设置属性和添加回调方法,这是我写的一个类
<?php
// +----------------------------------------------------------------------
// | websocket
// +----------------------------------------------------------------------
// | Author: myh
// +----------------------------------------------------------------------
namespace app\worker;
use think\worker\Server;
use app\common\api\BaseApi;
class Worker extends Server
{
protected $socket = 'http://0.0.0.0:2346';
public function onMessage($connection,$data)
{
$data = explode('|', $data);
switch ($data[0]) {
case 'software_send_msg':
if(!isset($data[1]) || empty($data[1])) {
$list = json_encode(['result'=>'error','message'=>'参数为空']);
}else{
$account = $data[1];
$uid = BaseApi::findData(8,2,[['user_login','=',$account]],'id');
if($uid){
list($data) = BaseApi::getListOne(16,[['user_id','=',$uid],['status','=',0]]);//16-software_send_msg
if($data){
$list = [];
foreach ($data as $k => $v) {
$list[$k]['id'] = $v['id'];
$list[$k]['type'] = $v['type'];
$list[$k]['msg'] = $v['msg'];
}
$list = json_encode(['result'=>'success','data'=>json_encode($list)]);
}else{
$list = json_encode(['result'=>'error', 'message'=>'数据为空']);
}
}else{
$list = json_encode(['result'=>'error', 'message'=>'找不到该账号信息']);
}
}
break;
case 'software_send_msg2':
$msg = 3333;
break;
default:
$list = json_encode(['result'=>'error']);
break;
}
$connection->send($list);//这里把数据发送给客户端
}
}
这时要对worker_server.php进行修改,删掉之前的配置,只要加上这个即可
return [
'worker_class' => 'app\worker\Worker',
];
控制器:
public function ws()
{
return $this->fetch(':ws');
}
模板:
<!DOCTYPE html>
<html xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ThinkCMF WebSocket Demo</title>
<script src="__STATIC__/js/jquery-2.0.3.min.js"></script>
</head>
<body>
<div id="app">
软件模拟获取最新消息:
<button class="get-msg">请求</button>
<div class="return-msg">
</div>
</div>
<script>
$(document).on('click','.get-msg',function(){
ws = new WebSocket("ws://127.0.0.1:2346");
//组织参数
var param = new Array();
param.push('software_send_msg');
param.push('wydyhdzh');
ws.onopen = function() {
ws.send(param.join('|'));//向服务器返送信息
};
ws.onmessage = function(e) {//服务器返回信息
var data = JSON.parse(e.data);
if(data.result == 'success'){
var list = JSON.parse(data.data);
$('.return-msg').html(data.data);
}
};
})
</script>
</body>
</html>
整个流程就是这样,不明白可以留言