swoole是一个php的网络通信框架,可以简单支持http服务。按照官方介绍,如果遇到mysql或者广播消息等耗时或阻塞的业务,需要开启task去提供服务,在Finish回调中处理结果(定时器中如果有阻塞任务,会导致work进程无法提供服务)。
(需要安装配置扩展,如果源码编译运行提示glibc版本不兼容,可以用pecl install swoole)
针对一些数据不常变化的部分,我们可以用定时器读取到内存,然后提供服务,这样对mysql压力小了很多。大致示例代码如下:
#!/usr/bin/php <?php date_default_timezone_set("Asia/Shanghai"); class RotateMap { public function __construct () { $this->d = array(array(), array()); $this->idx = 0; } public function &get () { return $this->d[$this->idx]; } public function set ($t) { $this->d[!$this->idx] = $t; $this->swap(); } private function swap () { $this->idx = !$this->idx; } private $d; private $idx; }; class Server { private $serv; private $upgrade; private $allowurl; private $checkurl; const RELOAD_SECOND_INTERVAL = 1000; const RELOAD_MINUTE_INTERVAL = 60000; const RELOAD_HOUR_INTERVAL = 360000; const RELOAD_DAY_INTERVAL = 86400000; const ERROR_LOG = "/tmp/swoole.error.log"; const LOCAL_TXT = "/tmp/localfile"; public function __construct () { $this->upgrade = new RotateMap(); $this->allowurl = new RotateMap(); $this->checkurl = new RotateMap(); $this->serv = new swoole_http_server("127.0.0.1", 1987); $this->serv->set(array('worker_num' =>1, 'daemonize' => true, 'max_request' =>10000, 'dispatch_mode' =>3, 'debug_mode' =>1, 'task_worker_num' =>1, 'log_file' =>'/tmp/swoole.log')); $this->serv->on('Start', array($this, 'onStart')); $this->serv->on('Request', array($this, 'onRequest')); $this->serv->on('Task', array($this, 'onTask')); $this->serv->on('Finish', array($this, 'onFinish')); $this->serv->on('WorkerStart', array($this, 'onWorkerStart')); $this->serv->on('Timer', array($this, 'reloadDB')); $this->serv->start(); } public function onStart($serv) { } private function slog($msg){ echo "[".date('Y-m-d H:i:s')." #".$this->serv->worker_pid.".0] INFO ".$msg.PHP_EOL; } public function onWorkerStart($serv, $worker_id) { if($serv->taskworker == false){ //Init Data $this->slog("InitData Begin"); $this->reloadDBUpgrade(true); $this->reloadDB1(true); $this->reloadDB2(true); $this->slog("InitData Done"); $serv->addtimer(Server::RELOAD_HOUR_INTERVAL); } } private function reloadDBUpgrade($save = false){ try{ $dsn='mysql:host=myhost;dbname=mydb;'; $dbh=new PDO($dsn,"myusername","mypassword"); $dbh->query('set names utf8;'); $stmt=$dbh->query('SELECT id,name FROM tb_version ORDER BY id DESC LIMIT 1'); while($row = $stmt->fetch()){ $verinfo = array( "id" => $row["id"], "name" => $row["name"], ); break; } } catch(PDOException $e){ error_log($e->getMessage(),3,Server::ERROR_LOG); } $dbh = null; if($save){ $this->upgrade->set($verinfo); }else{ return $verinfo; } } private function reloadDB1($save = false){ $handle = fopen(Server::LOCAL_TXT, "r"); if(!$handle){ return; } $data = array(); while (!feof ($handle)) { $buffer = fgets($handle, 1024); $domain = trim($buffer); if(strlen($domain)>=5){ array_push($data, $domain); } } fclose ($handle); if($save){ $this->allowurl->set($data); }else{ return $data; } } private function reloadDB2($save = false){ $handle = fopen(Server::CHECK_TXT, "r"); if(!$handle){ return; } $data = array(); while (!feof ($handle)) { $buffer = fgets($handle, 1024); $info = explode("|",trim($buffer)); if(count($info)==2){ $domain = $info[0]; $sortid = $info[1]; if(strlen($domain)>=5 && is_numeric($sortid)){ $data[$domain] = $sortid; } } } fclose ($handle); if($save){ $this->checkurl->set($data); }else{ return $data; } } public function reloadDB($serv, $interval) { switch($interval) { case Server::RELOAD_HOUR_INTERVAL: $param = array("job", "reload"); $serv->task($param); break; } } public function onRequest (swoole_http_request $request, swoole_http_response $response) { $code = 200; $request_uri = $request->server["request_uri"]; $request_method = $request->server["request_method"]; $remote_addr = $request->server["remote_addr"]; $getdata = $postdata = '{}'; if(isset($request->get)){ $getdata = json_encode($request->get); } if(isset($request->post)){ $postdata = json_encode($request->post); } $this->slog("$remote_addr $request_method $request_uri $getdata $postdata"); switch($request_uri){ case '/url1': $id =isset($request->get["id"])?$request->get["id"]:null; if($id == null){ $code = 400; $res = "400 bad request"; }else{ $data = &$this->upgrade->get(); if($id == $data["id"]){ $res = array("hasNewVersion"=>false); }else{ $res = array("hasNewVersion"=>true,"versionInfo"=>$this->upgrade->get()); } } $res = json_encode($res, JSON_UNESCAPED_SLASHES); break; case '/url2': $host = isset($request->post["host"])?$request->post["host"]:null; $referer = isset($request->post["referer"])?$request->post["referer"]:null; $v1 = 1; $v2 = 1; $data = &$this->allowurl->get(); if($host){ $info = explode(".", $host); if(count($info)>=2){ $host = $info[count($info)-2].".".$info[count($info)-1]; } if(in_array($host,$data)){ $v1 = 0; } } if($referer){ $info = explode(".", $referer); if(count($info)>=2){ $referer = $info[count($info)-2].".".$info[count($info)-1]; } if(in_array($referer,$data)){ $v2 = 0; } } $res = "$v1|$v2"; break; case '/url3': $host = isset($request->post["host"])?$request->post["host"]:null; $url = isset($request->post["url"])?$request->post["url"]:null; $data = &$this->checkurl->get(); $v1 = 0; if($host){ $info = explode(".", $host); if(count($info)>=2){ $host = $info[count($info)-2].".".$info[count($info)-1]; } if(array_key_exists($host,$data)){ $v1 = $data[$host]; } } $res = $v1; break; default: $code = 404; $res = "404 not found"; } $response->status($code); $response->header("Server", "nginx"); $response->header("Content-Type", "text/html"); $response->end($res); } public function onTask ($serv, $task_id, $from_id, $data) { $this->slog("ReloadData Begin"); $ret = array(); $ret["upgrade"] = $this->reloadDBUpgrade(); $ret["allowurl"] = $this->reloadDB1(); $ret["checkurl"] = $this->reloadDB2(); $this->slog("ReloadData Done"); return $ret; } public function onFinish ($serv, $task_id, $data) { if(count($data["allowurl"])>0){ $this->allowurl->set($data["allowurl"]); }else{ error_log("load allowurl error",3,Server::ERROR_LOG); } if(count($data["checkurl"])>0){ $this->checkurl->set($data["checkurl"]); }else{ error_log("load checkurl error",3,Server::ERROR_LOG); } $this->upgrade->set($data["upgrade"]); } } $server = new Server (); ?>