192.168.31.62 Swoole+PHP7+Redis
192.168.31.63 Swoole+PHP7
192.168.31.64 Nginx+HTML
Nginx配置
upstream liveChartWS {
server 192.168.31.62:8080;
server 192.168.31.63:8080;
}
server {
listen 80;
server_name live.zhou.com;
#charset koi8-r;
access_log logs/host.access.log main;
location ~ \/(live|chart)$ {
proxy_pass http://liveChartWS;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
root /home/thinkPHP/public/static;
index index.html index.htm;
if (!-e $request_filename){
proxy_pass http://liveChartWS;
}
}
}
启动swoole
netstat -anp 2>/dev/null | grep 8080 | grep LISTEN |wc -l
nohup /usr/local/bin/php /home/thinkPHP/server/ws.php >> /home/thinkPHP/server/logs/SERVER-`date +%Y-%m-%d`.log 2>&1 &
目录结构
reload.sh
#! /usr/bin/env bash
echo "Loading..."
pid=`pidof LIVE_MASTER`
echo $pid
kill -USR1 $pid
echo "Loading Success"
HTTP WEBSOCKET SERVER
<?php
/**
* WebSocket
* netstat -anp 2>/dev/null | grep 8080 | grep LISTEN |wc -l
*
* nohup /usr/local/bin/php /home/thinkPHP/server/ws.php >> /home/thinkPHP/server/logs/SERVER-`date +%Y-%m-%d`.log 2>&1 &
*/
class Ws
{
CONST HOST = "0.0.0.0";
CONST PORT = 8080;
public $ws = null;
public $redis = null;
public function __construct(){
//创建websocket服务器对象,监听0.0.0.0:9502端口
$this->ws = new swoole_websocket_server(self::HOST,self::PORT);
$this->ws->set(
[
'worker_num'=>4,
'task_worker_num'=>4,
'enable_static_handler'=>true,
'document_root'=> "/home/thinkPHP/public/static",
]
);
$this->ws->on('start',[$this,'onStart']);
$this->ws->on('workerStart',[$this,'onWorkerStart']);
$this->ws->on('open',[$this,'onOpen']);
$this->ws->on("message",[$this,"onMessage"]);
$this->ws->on("request",[$this,"onRequest"]);
$this->ws->on("task",[$this,"onTask"]);
$this->ws->on("finish",[$this,"onFinish"]);
$this->ws->on("close",[$this,"onClose"]);
$this->redis = new \Redis();
$result = $this->redis->connect('192.168.31.62',6379,10);
if ($result == false){
throw new \Exception("Redis connect error");
}
$this->redis->del('live');
$this->redis->del('chart');
$this->redis->close();
$this->redis = null;
$this->ws->start();
}
public function onStart($server){
// echo "Master_pid: {$server->master_pid}-Manager_pid:{$server->manager_pid}\n";
swoole_set_process_name("LIVE_MASTER");
}
/**
* [onWorkerStart 回调]
* @author Jhou Shuai
* @datetime 2019-06-24T13:58:54+0800
*/
public function onWorkerStart($server,$worker_id){
// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';
}
/**
* [onRequest 回调]
* @author Jhou Shuai
* @datetime 2019-06-24T14:00:23+0800
*/
public function onRequest($request, $response){
if ($request->server['request_uri']=='/favicon.ico'){
$response->status(404);
$response->end();
return ;
}
$_GET = $_POST = [];
if(isset($request->server)){
foreach ($request->server as $key => $value) {
$_SERVER[strtoupper($key)] = $value;
}
}
if(isset($request->header)){
foreach ($request->header as $key => $value) {
$_SERVER[strtoupper($key)] = $value;
}
}
if(isset($request->get)){
foreach ($request->get as $key => $value) {
$_GET[$key] = $value;
}
}
if(isset($request->post)){
foreach ($request->post as $key => $value) {
$_POST[$key] = $value;
}
}
$_FILE = [];
if(isset($request->files)){
foreach ($request->files as $key => $value) {
$_FILE[$key] = $value;
}
}
$_POST['http_server'] = $this->ws;
ob_start();
try{
// 执行应用并响应
think\Container::get('app')->run()->send();
}catch(\Exception $e){
//todo
}
$res = ob_get_contents();
ob_end_clean();
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end($res);
}
/**
* [onOpen 监听WebSocket连接打开事件]
* @author Jhou Shuai
* @datetime 2019-06-24T13:58:54+0800
* @param [type] $ws
* @param [type] $repuest
*/
public function onOpen($ws,$request){
if (!empty($request->server['path_info'])) {
$key = trim(ltrim($request->server['path_info'], '/'));
print_r($key."==>".$request->fd."\n");
app\common\lib\Redis::getInstance()->sAdd($key,$request->fd);
}else{
$ws->push($request->fd, "服务器连接错误~~~");
}
}
/**
* [onMessage 监听WebSocket消息事件]
* @author Jhou Shuai
* @datetime 2019-06-24T14:00:23+0800
* @param [type] $ws
* @param [type] $frame
*/
public function onMessage($ws,$frame){
// 接收到客户端的消息,下面的业务逻辑
echo "Message: {$frame->data}-Date:".date("Y-m-d")."\n";
// 接收到客户端的消息,下面的业务逻辑,非常耗时
// todo 10s
// swoole Task机制
// $data = [
// 'task'=>1,
// 'fd'=>$frame->fd,
// ];
// $ws->task($data);
//
// $ws->push($frame->fd, "Server: {$frame->data} Date:".date("Y-m-d H:i:s"));
}
/**
* [onClose 监听WebSocket连接关闭事件]
* @author Jhou Shuai
* @datetime 2019-06-24T14:01:29+0800
* @param [type] $ws
* @param [type] $fd
*/
public function onClose($ws, $fd){
echo "client-{$fd} is closed\n";
app\common\lib\Redis::getInstance()->sRem("live",$fd);
app\common\lib\Redis::getInstance()->sRem("chart",$fd);
}
/**
* [onTask ]
* @author Jhou Shuai
* @datetime 2019-06-24T14:49:27+0800
* @param [type] $ws
* @param [type] $taskId
* @param [type] $workerId
* @param [type] $data
*/
public function onTask($serv,$taskId,$workerId,$data){
print_r($data);
sleep(10);
return "On Task Finish";
}
/**
* [onFinish ]
* @author Jhou Shuai
* @datetime 2019-06-24T14:51:48+0800
* @param [type] $serv
* @param [type] $taskId
* @param [type] $data
*/
public function onFinish($serv,$taskId,$data){
echo "{$taskId}-{$data}--Succ Task\n";
}
}
new Ws();
Index.php
<?php
namespace app\index\controller;
use app\common\lib\Util;
class Index extends \think\Controller
{
public function index(){
return Util::return_client(config('code.success'),'成功!',$_GET);
}
public function hello($name = 'ThinkPHP5')
{
print_r(request()->get());
print_r(request()->post());
return 'hello,' . $name;
}
}
Live.php
<?php
namespace app\index\controller;
use app\common\lib\Util;
use app\common\lib\Redis;
class Live extends \think\Controller
{
public function push(){
$msg = request()->get('msg');
// 把数据push到直播页面。
// todo 业务逻辑~~~~
$info = !empty($msg)?trim($msg):"你有新短消息来啦~~~";
$server = $_POST['http_server'];
// $server->task();
$client = Redis::getInstance()->sMembers(config("redis.live"));
foreach ($client as $key => $fd) {
$server->push($fd,$info);
}
return Util::return_client(config('code.success'),'Push Live Msg Success~~',$client);
}
}
Chart.php
<?php
namespace app\index\controller;
use app\common\lib\Util;
use app\common\lib\Redis;
class Chart extends \think\Controller
{
public function send(){
$msg = request()->get('msg');
// 把数据push到聊天专用页面。
// todo 业务逻辑~~~~
$info = !empty($msg)?trim($msg):"你有新短消息来啦~~~";
$server = $_POST['http_server'];
// $server->task();
$client = Redis::getInstance()->sMembers(config("redis.chart"));
foreach ($client as $key => $fd) {
$server->push($fd,$info);
}
return Util::return_client(config('code.success'),'Send Chart Msg Success~~',$client);
}
}
Redis.php
<?php
namespace app\common\lib;
class Redis {
public $redis = "";
/**
* 定义单例模式的变量
* @var null
*/
private static $_instance=null;
private function __construct(){
$this->redis = new \Redis();
$result = $this->redis->connect('192.168.31.62',6379,10);
if ($result == false){
throw new \Exception("Redis connect error");
}
}
public static function getInstance() {
if(empty(self::$_instance)){
self::$_instance = new self();
}
return self::$_instance;
}
public function get($key){
# code...
}
public function sAdd($key,$value){
return $this->redis->sAdd($key,$value);
}
public function sRem($key,$value){
return $this->redis->sRem($key,$value);
}
public function sMembers($key)
{
return $this->redis->sMembers($key);
}
/**
* 清空当前数据库
* @return bool
*/
public function flushDB(){
return $this->redis->flushDB();
}
public function del($key){
return $this->redis->del($key);
}
/**
* [__destruct 释放连接]
* @author Jhou Shuai
*/
private function __destruct() {
$this->redis->close();
}
}
Util.php
<?php
namespace app\common\lib;
class Util {
/**
* [return_client 给客户端输出数据]
* @author Jhou Shuai
* @datetime 2019-06-25T16:53:59+0800
*/
public static function return_client($status,$msg='',$data=[]){
$result = [
'status' => $status,
'message' => $msg,
'data' => $data,
'curtime' => date("Y-m-d,H:i:s"),
];
return json_encode($result);
}
}
config/code.php
<?php
return [
"success" => '0000',
"error" => '1000',
];
config/redis.php
<?php
return [
'host' => '192.168.31.62',
'password' => '',
'port' => 6379,
'timeout' => 5,
'live' =>'live',
'chart' =>'chart',
];
index.html
<!DOCTYPE html>
<html>
<head>
<title>玫瑰之约<Swoole.WebSocket.JhouShuai></title>
<meta charset="utf-8">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
</head>
<body>
<h1>玫瑰之约~~来啦,老弟!@JhouShuai</h1>
<p style="text-align: center;">
<canvas id="c" height="500" width="500"></canvas>
<script>
var b = document.body;
var c = document.getElementsByTagName('canvas')[0];
var a = c.getContext('2d');
document.body.clientWidth;
</script>
<script>
with(m=Math)C=cos,S=sin,P=pow,R=random;c.width=c.height=f=500;h=-250;function p(a,b,c){if(c>60)return[S(a*7)*(13+5/(.2+P(b*4,4)))-S(b)*50,b*f+50,625+C(a*7)*(13+5/(.2+P(b*4,4)))+b*400,a*1-b/2,a];A=a*2-1;B=b*2-1;if(A*A+B*B<1){if(c>37){n=(j=c&1)?6:4;o=.5/(a+.01)+C(b*125)*3-a*300;w=b*h;return[o*C(n)+w*S(n)+j*610-390,o*S(n)-w*C(n)+550-j*350,1180+C(B+A)*99-j*300,.4-a*.1+P(1-B*B,-h*6)*.15-a*b*.4+C(a+b)/5+P(C((o*(a+1)+(B>0?w:-w))/25),30)*.1*(1-B*B),o/1e3+.7-o*w*3e-6]}if(c>32){c=c*1.16-.15;o=a*45-20;w=b*b*h;z=o*S(c)+w*C(c)+620;return[o*C(c)-w*S(c),28+C(B*.5)*99-b*b*b*60-z/2-h,z,(b*b*.3+P((1-(A*A)),7)*.15+.3)*b,b*.7]}o=A*(2-b)*(80-c*2);w=99-C(A)*120-C(b)*(-h-c*4.9)+C(P(1-b,7))*50+c*2;z=o*S(c)+w*C(c)+700;return[o*C(c)-w*S(c),B*99-C(P(b, 7))*50-c/3-z/1.35+450,z,(1-b/1.2)*.9+a*.1, P((1-b),20)/4+.05]}}setInterval('for(i=0;i<1e4;i++)if(s=p(R(),R(),i%46/.74)){z=s[2];x=~~(s[0]*f/z-h);y=~~(s[1]*f/z-h);if(!m[q=y*f+x]|m[q]>z)m[q]=z,a.fillStyle="rgb("+~(s[3]*h)+","+~(s[4]*h)+","+~(s[3]*s[3]*-80)+")",a.fillRect(x,y,1,1)}',0)
</script>
</p>
<script type="text/javascript">
(function(win) {
var Client = function(options) {
var MAX_CONNECT_TIMES = 10;
var DELAY = 15000;
this.options = options || {};
this.createConnect(MAX_CONNECT_TIMES, DELAY);
}
Client.prototype.createConnect = function(max, delay) {
var self = this;
if (max === 0) {
return;
}
connect();
function connect() {
var domain = document.domain;
var ws = new WebSocket('ws://'+domain+'/live');
ws.onopen = function() {
auth();
register();
}
ws.onmessage = function(evt) {
var data = evt.data;
var notify = self.options.notify;
if(notify) notify(data);
}
ws.onclose = function() {
console.log("连接断开咯~~~重连中~~~")
setTimeout(reConnect, delay);
}
function auth() {
// 可以做认证
ws.send("帅哥请求进入~~~");
}
function register() {
// 注册
ws.send("小可爱来啦~~~~");
}
}
function reConnect() {
self.createConnect(--max, delay * 2);
}
}
win['MyClient'] = Client;
})(window);
var client = new MyClient({
notify: function(data) {
console.log(data);
alert(JSON.stringify(data));
}
});
</script>
</body>
</html>
chart.html
<!DOCTYPE html>
<html>
<head>
<title>小嘴抹蜜,口吐芬芳~~<Swoole.WebSocket.JhouShuai></title>
<meta charset="utf-8">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
</head>
<body>
<h1>小嘴抹蜜,口吐芬芳~~@JhouShuai</h1>
<p style="text-align: center;">
<canvas id="c" height="500" width="500"></canvas>
<script>
var b = document.body;
var c = document.getElementsByTagName('canvas')[0];
var a = c.getContext('2d');
document.body.clientWidth;
</script>
<script>
with(m=Math)C=cos,S=sin,P=pow,R=random;c.width=c.height=f=500;h=-250;function p(a,b,c){if(c>60)return[S(a*7)*(13+5/(.2+P(b*4,4)))-S(b)*50,b*f+50,625+C(a*7)*(13+5/(.2+P(b*4,4)))+b*400,a*1-b/2,a];A=a*2-1;B=b*2-1;if(A*A+B*B<1){if(c>37){n=(j=c&1)?6:4;o=.5/(a+.01)+C(b*125)*3-a*300;w=b*h;return[o*C(n)+w*S(n)+j*610-390,o*S(n)-w*C(n)+550-j*350,1180+C(B+A)*99-j*300,.4-a*.1+P(1-B*B,-h*6)*.15-a*b*.4+C(a+b)/5+P(C((o*(a+1)+(B>0?w:-w))/25),30)*.1*(1-B*B),o/1e3+.7-o*w*3e-6]}if(c>32){c=c*1.16-.15;o=a*45-20;w=b*b*h;z=o*S(c)+w*C(c)+620;return[o*C(c)-w*S(c),28+C(B*.5)*99-b*b*b*60-z/2-h,z,(b*b*.3+P((1-(A*A)),7)*.15+.3)*b,b*.7]}o=A*(2-b)*(80-c*2);w=99-C(A)*120-C(b)*(-h-c*4.9)+C(P(1-b,7))*50+c*2;z=o*S(c)+w*C(c)+700;return[o*C(c)-w*S(c),B*99-C(P(b, 7))*50-c/3-z/1.35+450,z,(1-b/1.2)*.9+a*.1, P((1-b),20)/4+.05]}}setInterval('for(i=0;i<1e4;i++)if(s=p(R(),R(),i%46/.74)){z=s[2];x=~~(s[0]*f/z-h);y=~~(s[1]*f/z-h);if(!m[q=y*f+x]|m[q]>z)m[q]=z,a.fillStyle="rgb("+~(s[3]*h)+","+~(s[4]*h)+","+~(s[3]*s[3]*-80)+")",a.fillRect(x,y,1,1)}',0)
</script>
</p>
<script type="text/javascript">
(function(win) {
var Client = function(options) {
var MAX_CONNECT_TIMES = 10;
var DELAY = 15000;
this.options = options || {};
this.createConnect(MAX_CONNECT_TIMES, DELAY);
}
Client.prototype.createConnect = function(max, delay) {
var self = this;
if (max === 0) {
return;
}
connect();
function connect() {
var domain = document.domain;
var ws = new WebSocket('ws://'+domain+'/chart');
ws.onopen = function() {
auth();
register();
}
ws.onmessage = function(evt) {
var data = evt.data;
var notify = self.options.notify;
if(notify) notify(data);
}
ws.onclose = function() {
console.log("连接断开咯~~~重连中~~~")
setTimeout(reConnect, delay);
}
function auth() {
// 可以做认证
ws.send("帅哥进入聊天室~~~");
}
function register() {
// 注册
ws.send("嘤嘤嘤进入聊天室~~~~");
}
}
function reConnect() {
self.createConnect(--max, delay * 2);
}
}
win['MyClient'] = Client;
})(window);
var client = new MyClient({
notify: function(data) {
console.log(data);
alert(JSON.stringify(data));
}
});
</script>
</body>
</html>
最终效果:
网友评论