swoole是用来补缺php异步处理缺陷的新技术,支持协程,用户也可以完全抛开原来的fpm模式调用,而直接使用php+swoole开发项目,能更加处理网站的高并发,大数据问题;它是php异步网络通讯引擎;异步多线程;
在线直播,聊天室,游戏行业
安装php,swoole就是php的一个扩展,可以用在http,websocket等请求。
知识点
$serv = new Swoole\Server("127.0.0.1", 9501);
$serv->set([
'worker_num' => 4, //worker process num
'max_request'=>10000
]);
//监听连接进入事件
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect.\n";
});
//监听数据接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
$serv->send($fd, "Server: ".$data);
});
//监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
echo "Client: Close.\n";
});
//启动服务器
$serv->start();
$serv = new Swoole\Server("127.0.0.1",9502,SWOOLE_PROCESS,SWOOLE_SOCK_UDP);
$serv->set([
'worker_num' => 4, //worker process num
'max_request'=>10000
]);
//UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包。对应的事件为onPacket
$serv->on('Packet',function($serv,$data,$clientInfo){
$serv->sendto($clientInfo['address'], $clientInfo['port'], "Server ".$data);
var_dump($clientInfo);
var_dump($data);
});
//启动服务器
$serv->start();
$http = new swoole_http_server("0.0.0.0", 9501);
// 配置 这里设置了该项,浏览器路由加了,index.html ,代码就不会执行西下边的输出
$http->set([
'enable_static_handler'=>true,
'document_root'=>'/home/swoole-mooc/demo/data',
]);
$http->on('request',function($request,$response){
print_r($request->get);
$response->cookie('login','cook-name',time()+3600);//给当前服务设置一个cookie
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end("Hello Swoole. #"
.rand(1000, 9999)."");
});
$http->start();
上边如果请求找不到对应的html文件,就会走到request中的end方法,找到就不会
websocket协议是基于TCP的一种新的网络协议。它实现了浏览器和服务器全双工通信----允许服务器主动发送信息给客户端。
http的通信只能由客户端发起,需要不断发送消息需要轮询发送请求
$server = new Swoole\WebSocket\Server("0.0.0.0", 9502);
//监听websocket连接打开事件
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
});
//监听ws消息事件
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
//关闭ws消息事件
$server->on('close', function ($ser, $fd) {
echo "client {$fd} closed\n";
});
//启动websocket服务器
$server->start();
websocket前端代码:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h1>Websocket的测试111</h1>
<h1>2222</h1>
<script type="text/javascript">
var wsurl = "ws://xx.xx.xx.xx:9502";
var websocket = new WebSocket(wsurl);
websocket.onopen=function(evt){
websocket.send("hello world");
console.log("链接成功");
}
websocket.onmessage=function(evt){
console.log("后端数据:"+evt.data);
}
websocket.onclose=function(evt){
console.log("close");
}
</script>
</body>
</html>
使用场景:执行耗时操作,比如发送邮件,广播
实现要求:
进程就是正在运行的程序的一个实例。
进程概念:主进程-》子进程-》管理进程-》work进程
进程使用场景:比如需要耗时读取多个文件里的内容,执行速度慢,这是可以开启多个进程,互不影响读取速度加快。
进程间的通讯,通过管道通讯。
echo date("Ymd H:i:s").PHP_EOL;
$workers=[];
$urls=array("aaa,bbb,ccc,ddd,eee,fff");
for ($i=0; $i <6 ; $i++) {
// 创建子进程
$process= new swoole_process(function(swoole_process $work) use($i,$urls){
//匿名函数调用函数外部变量值的方法,使用use
$content = curlData($urls[$i]);
$work->write($content.PHP_EOL);//数据写入管道中
},true);
$pid = $process->start();
$workers[$pid] = $process;
}
foreach ($workers as $process) {
echo $process->read();//从管道获取数据
}
function curlData($url){
sleep(1);
return $url.PHP_EOL;
}
echo date("Ymd H:i:s").PHP_EOL;
Lock、Buffer、Table、Mmap、Atomic。。。。。。
swoole_table(内存表)是一个基于共享内存的锁实现的超高性能,并发数据结构。
//内存。类似数据库能力
use Swoole\Table;
$table = new Swoole\Table(1024);
//内存增加一列
$table->column('id',Table::TYPE_INT,4);
$table->column('name',Table::TYPE_STRING,16);
$table->column('age',Table::TYPE_INT,3);
$table->create();
//表中增加一条数据 ,hello为key
$table->set("hello",[
"id"=>1,
"name"=>"syf",
"age"=>30
]);
//原子自增信息,只针对 TYPE_INT生效
$table->incr("hello","age",12);
//内存中获取数据
print_r($table->get("hello"));
协程可以理解为纯用户态的线程,通过协作而不是抢占来进行切换。应用层可以使用同步编程方式,底层自动实现异步IO。
优势:
$http = new swoole_http_server("0.0.0.0", 9501);
$http->on("request",function($request,$response){
$redis = new Swoole\Coroutine\Redis();
$redis->connect("127.0.0.1",6379);
$val = $redis->get("hello");//获得redis中key=hello值,输出在页面中
// $response->header("Content-type","text/html");
$response->header("Content-type","text/plain");
$response->end($val);
});
$http->start();
return [
'host'=>'127.0.0.1',
'port'=>'6379',
'out_time'=>120,
'timeout'=>5,
'test_code'=>6789,
'live_game_key'=>'clientKey',
];
class Http
{
CONST HOST = "0.0.0.0";
CONST PORT = 9501;
public $http = null;
public function __construct()
{
$this->http = new swoole_http_server(self::HOST,self::PORT);
$this->http->set([
'enable_static_handler'=>true,
'document_root'=>'/home/swoole-mooc/thinkphp51/public/static',
'work_num'=>5,
'task_worker_num'=>2,
]);
$this->http->on('workerstart',[$this,'onWorkerStart']);
$this->http->on('request',[$this,'onRequest']);
$this->http->on('task',[$this,'onTask']);
$this->http->on('finish',[$this,'onFinish']);
$this->http->on('close',[$this,'onClose']);
$this->http->start();
}
public function onWorkerStart(){
define('APP_PATH', __DIR__ . '/../application/');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
}
public function onRequest($request,$response){
$_GET = [];
$_POST = [];
// $_SERVER = [];
if(isset($request->get)){
foreach ($request->get as $k=> $v) {
$_GET[$k]=$v;
}
}
if(isset($request->post)){
foreach ($request->post as $k => $v) {
$_POST[$k]=$v;
}
}
if(isset($request->header)){
foreach ($request->header as $k => $v) {
$_SERVER[strtoupper($k)]=$v;
}
}
if(isset($request->server)){
foreach ($request->server as $k => $v) {
$_SERVER[strtoupper($k)]=$v;
}
}
$_POST['http_server'] = $this->http;
ob_start();
try{
// 执行应用并响应
think\Facade::make('app', [APP_PATH])
->run()
->send();
} catch (\Exception $e){
echo "有错误".$e->getMessage().PHP_EOL;
}
$res = ob_get_contents();
ob_end_clean();
// var_dump($_SERVER);
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end($res);
}
public function onTask($http,$task_id,$workId,$data){
//分发任务,会进入 Task.php 来找到相应方法,来进行任务。
$obj = new app\common\lib\task\Task();
$method = $data['method'];
$flag = $obj->$method($data['data']);
return $flag;
}
public function onFinish($http,$task_id,$data){
echo "task is finish".PHP_EOL;
}
public function onClose($http,$fd){
echo "client is shutdown".$fd.PHP_EOL;
}
}
new Http();
/**
* 设置同步redis,这里的重写,是为了不让redis每次都重新连接,造成内存资源的浪费
*/
class Predis
{
public $redis = '';
private static $_instance = null;
//采用单例模式设计,
public static function getInstance()
{
if(empty(self::$_instance)){
self::$_instance = new self();
}
return self::$_instance;
}
/**/
public function __construct()
{
$this->redis = new \Redis();
$result = $this->redis->connect(config('redis.host'),
config('redis.port'),config('redis.timeout'));
if($result===false){
throw new \Exception("redis connect error");
}
}
/*
redis ->set()
*/
public function set($key,$value,$time=0)
{
if (!$key) {
return '';
}
if(is_array($value)){
$value = json_encode($value);
}
if(!$time){
$this->redis->set($key,$value);
}
return $this->redis->setex($key,$time,$value);
}
/*
redis->get
*/
public function get($key)
{
if(!$key){
return '';
}
return $this->redis->get($key);
}
/*
redis->add
*/
// public function sAdd($key,$value)
// {
// return $this->redis->sadd($key,$value);
// }
/*
redis->rem
*/
// public function sRem($key,$value)
// {
// return $this->redis->srem($key,$value);
// }
/*
redis->sMember
*/
public function sMembers($key)
{
return $this->redis->sMembers($key);
}
public function __call($name,$arguments)
{
if(count($arguments)==2){
return $this->redis->$name($arguments[0],$arguments[1]);
} else {
return "不等于2";
}
}
class Task
{
/**
* 异步发送验证码
*
* @param type
* @return void
*/
public function sms($value,$serv)
{
try {
$respond = SMS::sendSms($value['phone'],$value['code']);
} catch (\Exception $e) {
echo $e->getMessage();
}
if($respond){
//这里不能用异步redis来存储,
// $redis = new \Swoole\Coroutine\Redis();
// $redis->connect(config('redis.host'),config('redis.port'));
// $val = $redis->set(Redis::setRedisKey($phone),config('redis.test_code'),config('redis.out_time'));
//方法一:同步redis
// $val = \Cache::set('key1',123456);
// $getval = \Cache::get('key1');
// 方法二:同步redis
$val = Predis::getInstance()->set($value['phone'],6789);
// $getval = Predis::getInstance()->get($phone);
}else{
return false;
}
return true;
}
public function pushLive($data,$serv)
{
$clients = Predis::getInstance()->sMembers(config('redis.live_game_key'));
foreach ($clients as $fd) {
$serv->push($fd,json_encode($data));
}
}
}