├── README.md ├── Smtp.class.php ├── pt-kill.php ├── pt_kill演示录像.zip ├── simplejson-3.8.2.tar.gz ├── smtp_config.php └── wechat.py /README.md: -------------------------------------------------------------------------------- 1 | /** 2 | * Percona PT-kill重构版(PHP) 3 | * https://github.com/hcymysql/pt-kill 4 | * 5 | * UPDATE: 6 | * Modified by: hcymysql 2018/11/27 7 | * 1、增加慢SQL邮件报警功能 8 | * 2、增加慢SQL微信报警功能 9 | * 10 | * 环境准备: 11 | * shell> yum install -y php-process php php-mysql 12 | * 13 | */ 14 | 15 | 概述 16 | 原生Percona版 PT-kill(Perl)工具只是单纯的KILL掉正在运行中的慢SQL,而不能作为一个监控工具使用,例如缺少邮件报警或者微信报警功能,固需要将其重构。 17 | 18 | 重构版 PT-kill(PHP)从information_schema.PROCESSLIST表中捕获正在运行中的SELECT|ALTER等DML/DDL消耗资源过多的查询,过滤它们,然后杀死它们(可选择不杀)且发邮件/微信报警给DBA和相关开发知悉,避免因慢SQL执行时间过长对数据库造成一定程度的伤害。 19 | 20 | (注:慢SQL执行完才记录到slow.log里,执行过程中不记录。) 21 | 22 | Usage: 23 | 24 | Options: 25 | 26 | -u username 27 | 28 | -p password 29 | 30 | -h host ip 31 | 32 | -P port 33 | 34 | -B busytime time seconds 设置慢SQL执行时间触发报警 35 | 36 | -I interval time seconds 设置守护进程下间隔监测时间 37 | 38 | --kill 如果想杀掉慢查询,加上该选项。 39 | 40 | --match-info 匹配杀掉SELECT|INSERT|UPDATE语句 41 | 42 | --match-user 匹配杀掉的用户 43 | 44 | --daemon 1开启后台守护进程,0关闭后台守护进程 45 | 46 | --mail 开启发送邮件报警 47 | 48 | --weixin 开启发送微信报警 49 | 50 | --help Help 51 | 52 | 53 | Example : 54 | 55 | 前台运行 56 | 57 | shell> php pt-kill.php -u admin -p 123456 -h 10.10.159.31 -P 3306 -B 10 --match-info='select|alter' --match-user='dev' --kill --mail --weixin 58 | 59 | 后台运行 60 | 61 | shell> nohup php pt-kill.php -u admin -p 123456 -h 10.10.159.31 -P 3306 -B 10 -I 15 --match-info='select|alter' --match-user='dev' --kill --mail --weixin --daemon 1 & 62 | 63 | 关闭后台运行 64 | 65 | shell> php pt-kill.php --daemon 0 66 | 67 | 以上是工具的使用方法和参数选项。 68 | 这里说下比较重要的参数: 69 | 70 | 1、--kill 如果想杀掉慢查询,那么在后面添加该选项; 71 | 72 | 2、--match-info 可以单独使用,也可以和--match-user结合一起使用; 73 | 74 | 3、--daemon 1 是开启后台守护进程,如果不添加该选择,可以用系统的crontab代替。 75 | 该选项要和-I 10(秒)配合一起使用,即每休眠10秒监控一次。0为关闭后台守护进程。 76 | 77 | 4、--mail 为开启发送邮件报警,需先设置smtp_config.php,改成你自己的邮箱账号信息 78 | 79 | smtp_config.php 80 | 81 | ******************** 配置信息 ******************************** 82 | $smtpserver = "smtp.126.com";//SMTP服务器 83 | $smtpserverport = 25;//SMTP服务器端口 84 | $smtpusermail = "chunyang_he@126.com";//SMTP服务器的用户邮箱 85 | $smtpemailto = 'chunyang_he@126.com';//发送给谁 86 | $smtpuser = "chunyang_he@126.com";//SMTP服务器的用户帐号,注:部分邮箱只需@前面的用户名 87 | $smtppass = "123456";//SMTP服务器的授权码 88 | $mailtitle = "警告!出现卡顿慢SQL,请及时优化处理!";//邮件主题 89 | $mailcontent = "

".$content."

";//邮件内容 90 | $mailtype = "HTML";//邮件格式(HTML/TXT),TXT为文本邮件 91 | ************************ 配置信息 **************************** 92 | 93 | 5、--weixin 为开启发送微信报警,需要先安装下simplejson-3.8.2.tar.gz 94 | 95 | shell> tar zxvf simplejson-3.8.2.tar.gz 96 | 97 | shell> cd simplejson-3.8.2 98 | 99 | shell> python setup.py build 100 | 101 | shell> python setup.py install 102 | 103 | 104 | 然后编辑pt-kill.php脚本 105 | 106 | 找到 107 | 108 | $status1 = system("/usr/bin/python wechat.py 'hcymysql' {$row['DB']}库出现卡顿慢SQL! '{$content1}'"); 109 | 110 | 将其'hcymysql'我的微信号换成你自己的即可。 111 | 112 | 微信企业号设置 113 | 114 | 移步https://www.cnblogs.com/linuxprobe/p/5717776.html 看此教程配置。 115 | 116 | 6、会在工具目录下生成kill.txt文件保存慢SQL。 117 | 118 | shell> cat kill.txt 119 | 120 | 2018-11-27 16:41:22 121 | 122 | 用户名:root 123 | 124 | 来源IP:localhost 125 | 126 | 数据库名:hcy 127 | 128 | 执行时间:18 129 | 130 | SQL语句:select sleep(60) 131 | 132 | 133 | 7、默认只杀连接中的慢SQL,保留会话连接,如果想把连接也杀掉,去掉QUERY 134 | 135 | 修改pt-kill.php 136 | 137 | //$kill_sql = "KILL QUERY {$row['ID']}"; 138 | 139 | $kill_sql = "KILL {$row['ID']}"; 140 | 141 | 具体演示请看“pt_kill演示录像.avi” 142 | 143 | 144 | -------------------------------------------------------------------------------- /Smtp.class.php: -------------------------------------------------------------------------------- 1 | debug = FALSE; 45 | 46 | $this->smtp_port = $smtp_port; 47 | 48 | $this->relay_host = $relay_host; 49 | 50 | $this->time_out = 30; //is used in fsockopen() 51 | 52 | 53 | $this->auth = $auth;//auth 54 | 55 | $this->user = $user; 56 | 57 | $this->pass = $pass; 58 | 59 | 60 | $this->host_name = "localhost"; //is used in HELO command 61 | $this->log_file = ""; 62 | 63 | $this->sock = FALSE; 64 | 65 | } 66 | 67 | /* Main Function */ 68 | 69 | function sendmail($to, $from, $subject = "", $body = "", $mailtype, $cc = "", $bcc = "", $additional_headers = "") 70 | { 71 | 72 | $mail_from = $this->get_address($this->strip_comment($from)); 73 | 74 | $body = preg_replace("/(^|(\r\n))(\.)/", "\1.\3", $body); 75 | 76 | $header = "MIME-Version:1.0\r\n"; 77 | 78 | if($mailtype=="HTML"){ 79 | 80 | $header .= "Content-Type:text/html\r\n"; 81 | 82 | } 83 | 84 | $header .= "To: ".$to."\r\n"; 85 | 86 | if ($cc != "") { 87 | 88 | $header .= "Cc: ".$cc."\r\n"; 89 | 90 | } 91 | 92 | $header .= "From: $from<".$from.">\r\n"; 93 | 94 | $header .= "Subject: ".$subject."\r\n"; 95 | 96 | $header .= $additional_headers; 97 | 98 | $header .= "Date: ".date("r")."\r\n"; 99 | 100 | $header .= "X-Mailer:By Redhat (PHP/".phpversion().")\r\n"; 101 | 102 | list($msec, $sec) = explode(" ", microtime()); 103 | 104 | $header .= "Message-ID: <".date("YmdHis", $sec).".".($msec*1000000).".".$mail_from.">\r\n"; 105 | 106 | $TO = explode(",", $this->strip_comment($to)); 107 | 108 | if ($cc != "") { 109 | 110 | $TO = array_merge($TO, explode(",", $this->strip_comment($cc))); 111 | 112 | } 113 | 114 | if ($bcc != "") { 115 | 116 | $TO = array_merge($TO, explode(",", $this->strip_comment($bcc))); 117 | 118 | } 119 | 120 | $sent = TRUE; 121 | 122 | foreach ($TO as $rcpt_to) { 123 | 124 | $rcpt_to = $this->get_address($rcpt_to); 125 | 126 | if (!$this->smtp_sockopen($rcpt_to)) { 127 | 128 | $this->log_write("Error: Cannot send email to ".$rcpt_to."\n"); 129 | 130 | $sent = FALSE; 131 | 132 | continue; 133 | 134 | } 135 | 136 | if ($this->smtp_send($this->host_name, $mail_from, $rcpt_to, $header, $body)) { 137 | 138 | $this->log_write("E-mail has been sent to <".$rcpt_to.">\n"); 139 | 140 | } else { 141 | 142 | $this->log_write("Error: Cannot send email to <".$rcpt_to.">\n"); 143 | 144 | $sent = FALSE; 145 | 146 | } 147 | 148 | fclose($this->sock); 149 | 150 | $this->log_write("Disconnected from remote host\n"); 151 | 152 | } 153 | 154 | return $sent; 155 | 156 | } 157 | 158 | /* Private Functions */ 159 | 160 | function smtp_send($helo, $from, $to, $header, $body = "") 161 | { 162 | 163 | if (!$this->smtp_putcmd("HELO", $helo)) { 164 | 165 | return $this->smtp_error("sending HELO command"); 166 | 167 | } 168 | 169 | //auth 170 | if($this->auth){ 171 | 172 | if (!$this->smtp_putcmd("AUTH LOGIN", base64_encode($this->user))) { 173 | 174 | return $this->smtp_error("sending HELO command"); 175 | 176 | } 177 | 178 | if (!$this->smtp_putcmd("", base64_encode($this->pass))) { 179 | 180 | return $this->smtp_error("sending HELO command"); 181 | 182 | } 183 | 184 | } 185 | 186 | 187 | if (!$this->smtp_putcmd("MAIL", "FROM:<".$from.">")) { 188 | 189 | return $this->smtp_error("sending MAIL FROM command"); 190 | 191 | } 192 | 193 | if (!$this->smtp_putcmd("RCPT", "TO:<".$to.">")) { 194 | 195 | return $this->smtp_error("sending RCPT TO command"); 196 | 197 | } 198 | 199 | if (!$this->smtp_putcmd("DATA")) { 200 | 201 | return $this->smtp_error("sending DATA command"); 202 | 203 | } 204 | 205 | if (!$this->smtp_message($header, $body)) { 206 | 207 | return $this->smtp_error("sending message"); 208 | 209 | } 210 | 211 | if (!$this->smtp_eom()) { 212 | 213 | return $this->smtp_error("sending . [EOM]"); 214 | 215 | } 216 | 217 | if (!$this->smtp_putcmd("QUIT")) { 218 | 219 | return $this->smtp_error("sending QUIT command"); 220 | 221 | } 222 | 223 | return TRUE; 224 | 225 | } 226 | 227 | function smtp_sockopen($address) 228 | { 229 | 230 | if ($this->relay_host == "") { 231 | 232 | return $this->smtp_sockopen_mx($address); 233 | 234 | } else { 235 | 236 | return $this->smtp_sockopen_relay(); 237 | 238 | } 239 | 240 | } 241 | 242 | function smtp_sockopen_relay() 243 | { 244 | 245 | $this->log_write("Trying to ".$this->relay_host.":".$this->smtp_port."\n"); 246 | 247 | $this->sock = @fsockopen($this->relay_host, $this->smtp_port, $errno, $errstr, $this->time_out); 248 | 249 | if (!($this->sock && $this->smtp_ok())) { 250 | 251 | $this->log_write("Error: Cannot connenct to relay host ".$this->relay_host."\n"); 252 | 253 | $this->log_write("Error: ".$errstr." (".$errno.")\n"); 254 | 255 | return FALSE; 256 | 257 | } 258 | 259 | $this->log_write("Connected to relay host ".$this->relay_host."\n"); 260 | 261 | return TRUE; 262 | 263 | } 264 | 265 | function smtp_sockopen_mx($address) 266 | { 267 | 268 | $domain = preg_replace("/^.+@([^@]+)$/", "\1", $address); 269 | 270 | if (!@getmxrr($domain, $MXHOSTS)) { 271 | 272 | $this->log_write("Error: Cannot resolve MX \"".$domain."\"\n"); 273 | 274 | return FALSE; 275 | 276 | } 277 | 278 | //专注与php学习 http://www.daixiaorui.com 欢迎您的访问 279 | 280 | foreach ($MXHOSTS as $host) { 281 | 282 | $this->log_write("Trying to ".$host.":".$this->smtp_port."\n"); 283 | 284 | $this->sock = @fsockopen($host, $this->smtp_port, $errno, $errstr, $this->time_out); 285 | 286 | if (!($this->sock && $this->smtp_ok())) { 287 | 288 | $this->log_write("Warning: Cannot connect to mx host ".$host."\n"); 289 | 290 | $this->log_write("Error: ".$errstr." (".$errno.")\n"); 291 | 292 | continue; 293 | 294 | } 295 | 296 | $this->log_write("Connected to mx host ".$host."\n"); 297 | 298 | return TRUE; 299 | 300 | } 301 | 302 | $this->log_write("Error: Cannot connect to any mx hosts (".implode(", ", $MXHOSTS).")\n"); 303 | 304 | return FALSE; 305 | 306 | } 307 | 308 | function smtp_message($header, $body) 309 | { 310 | 311 | fputs($this->sock, $header."\r\n".$body); 312 | 313 | $this->smtp_debug("> ".str_replace("\r\n", "\n"."> ", $header."\n> ".$body."\n> ")); 314 | 315 | return TRUE; 316 | 317 | } 318 | 319 | function smtp_eom() 320 | { 321 | 322 | fputs($this->sock, "\r\n.\r\n"); 323 | 324 | $this->smtp_debug(". [EOM]\n"); 325 | 326 | return $this->smtp_ok(); 327 | 328 | } 329 | 330 | function smtp_ok() 331 | { 332 | 333 | $response = str_replace("\r\n", "", fgets($this->sock, 512)); 334 | 335 | $this->smtp_debug($response."\n"); 336 | 337 | if (!preg_match("/^[23]/", $response)) { 338 | 339 | fputs($this->sock, "QUIT\r\n"); 340 | 341 | fgets($this->sock, 512); 342 | 343 | $this->log_write("Error: Remote host returned \"".$response."\"\n"); 344 | 345 | return FALSE; 346 | 347 | } 348 | 349 | return TRUE; 350 | 351 | } 352 | 353 | function smtp_putcmd($cmd, $arg = "") 354 | { 355 | 356 | if ($arg != "") { 357 | 358 | if($cmd=="") $cmd = $arg; 359 | 360 | else $cmd = $cmd." ".$arg; 361 | 362 | } 363 | 364 | fputs($this->sock, $cmd."\r\n"); 365 | 366 | $this->smtp_debug("> ".$cmd."\n"); 367 | 368 | return $this->smtp_ok(); 369 | 370 | } 371 | 372 | function smtp_error($string) 373 | { 374 | 375 | $this->log_write("Error: Error occurred while ".$string.".\n"); 376 | 377 | return FALSE; 378 | 379 | } 380 | 381 | function log_write($message) 382 | { 383 | 384 | $this->smtp_debug($message); 385 | 386 | if ($this->log_file == "") { 387 | 388 | return TRUE; 389 | 390 | } 391 | 392 | $message = date("M d H:i:s ").get_current_user()."[".getmypid()."]: ".$message; 393 | 394 | if (!@file_exists($this->log_file) || !($fp = @fopen($this->log_file, "a"))) { 395 | 396 | $this->smtp_debug("Warning: Cannot open log file \"".$this->log_file."\"\n"); 397 | 398 | return FALSE; 399 | 400 | } 401 | 402 | flock($fp, LOCK_EX); 403 | 404 | fputs($fp, $message); 405 | 406 | fclose($fp); 407 | 408 | 409 | return TRUE; 410 | 411 | } 412 | 413 | 414 | function strip_comment($address) 415 | { 416 | 417 | $comment = "/\([^()]*\)/"; 418 | 419 | while (preg_match($comment, $address)) { 420 | 421 | $address = preg_replace($comment, "", $address); 422 | 423 | } 424 | 425 | return $address; 426 | 427 | } 428 | 429 | 430 | function get_address($address) 431 | { 432 | 433 | $address = preg_replace("/([ \t\r\n])+/", "", $address); 434 | 435 | $address = preg_replace("/^.*<(.+)>.*$/", "\1", $address); 436 | 437 | return $address; 438 | 439 | } 440 | 441 | function smtp_debug($message) 442 | { 443 | 444 | if ($this->debug) { 445 | 446 | echo $message; 447 | 448 | } 449 | 450 | } 451 | 452 | } 453 | 454 | -------------------------------------------------------------------------------- /pt-kill.php: -------------------------------------------------------------------------------- 1 | yum install -y php-process php php-mysql 14 | * 15 | */ 16 | 17 | ini_set('date.timezone','Asia/Shanghai'); 18 | error_reporting(7); 19 | 20 | function Usage(){ 21 | echo "\e[38;5;11m 22 | Usage: 23 | Options: 24 | -u username 25 | -p password 26 | -h host ip 27 | -P port 28 | -B busytime time seconds 设置慢SQL执行时间触发报警 29 | -I interval time seconds 设置守护进程下间隔监测时间 30 | --kill 如果想杀掉慢查询,加上该选项。 31 | --match-info 匹配杀掉SELECT|INSERT|UPDATE语句 32 | --match-user 匹配杀掉的用户 33 | --daemon 1开启后台守护进程,0关闭后台守护进程 34 | --mail 开启发送邮件报警 35 | --weixin 开启发送微信报警 36 | --help Help 37 | 38 | Example : 39 | 前台运行 40 | shell> php pt-kill.php -u admin -p 123456 -h 10.10.159.31 -P 3306 -B 10 --match-info='select|alter' --match-user='dev' --kill --mail --weixin 41 | 42 | 后台运行 43 | shell> nohup php pt-kill.php -u admin -p 123456 -h 10.10.159.31 -P 3306 -B 10 -I 15 --match-info='select|alter' --match-user='dev' --kill --mail --weixin --daemon 1 & 44 | 45 | 关闭后台运行 46 | shell> php pt-kill.php --daemon 0 47 | \e[0m" .PHP_EOL; 48 | } 49 | 50 | $shortopts = "u:"; 51 | $shortopts .= "p:"; 52 | $shortopts .= "h:"; 53 | $shortopts .= "P:"; 54 | $shortopts .= "B:"; 55 | $shortopts .= "I:"; 56 | 57 | $longopts = array( 58 | "kill", 59 | "match-info::", 60 | "match-user::", 61 | "daemon:", 62 | "mail", 63 | "weixin", 64 | "help", 65 | ); 66 | 67 | $options = getopt($shortopts, $longopts); 68 | 69 | if(empty($options) || isset($options['help'])){ 70 | Usage(); 71 | exit; 72 | } 73 | 74 | if(!isset($options['daemon'])){ 75 | Slowsql(); 76 | exit; 77 | } 78 | else{ 79 | if($options['daemon'] != 0){ 80 | if(!isset($options['I'])){ 81 | die("请设置间隔时间 -I 5(单位秒)"."\n"); 82 | } 83 | } 84 | $deamon = new Daemon($options['I']); 85 | $deamon->run($options['daemon']); 86 | } 87 | 88 | function Slowsql(){ 89 | 90 | global $options; 91 | 92 | $con = mysqli_connect("{$options['h']}","{$options['u']}","{$options['p']}","information_schema","{$options['P']}") 93 | or die("数据库链接错误"."\n".mysqli_error($con)); 94 | mysqli_query($con,"set names utf8"); 95 | 96 | if(isset($options['match-info'])){ 97 | $get_match_info = "SELECT ID,USER,HOST,DB,TIME,COMMAND,STATE,INFO FROM information_schema.PROCESSLIST 98 | WHERE TIME >= '{$options['B']}' AND INFO REGEXP '{$options['match-info']}'"; 99 | $result = mysqli_query($con,$get_match_info); 100 | } 101 | else if(isset($options['match-user'])){ 102 | echo "\e[38;5;196m没有匹配到SELECT|INSERT|UPDATE|DELETE等DML/DDL语句,退出主程序。\e[0m"."\n"; exit; 103 | } 104 | else if(isset($options['match-info']) && isset($options['match-user'])){ 105 | $get_match_user_info = "SELECT ID,USER,HOST,DB,TIME,COMMAND,STATE,INFO FROM information_schema.PROCESSLIST 106 | WHERE TIME >= '{$options['B']}' AND (INFO REGEXP '{$options['match-info']}' 107 | AND USER REGEXP '{$options['u']}')"; 108 | $result = mysqli_query($con,$get_match_user_info); 109 | } 110 | else{ echo "\e[38;5;196m没有匹配到SELECT|INSERT|UPDATE|DELETE等DML/DDL语句,退出主程序。\e[0m"."\n\n"; exit;} 111 | 112 | $rowcount=mysqli_num_rows($result); 113 | if ($rowcount == 0){ 114 | if(file_exists(dirname(__FILE__)."/kill.txt")){ 115 | rename(dirname(__FILE__)."/kill.txt","kill_".date('Y-m-d_H:i:s')."_history.txt"); 116 | //file_put_contents(dirname(__FILE__).'/kill.txt',""); 117 | } 118 | echo "\e[38;5;10m".date('Y-m-d H:i:s')." 未检测出当前执行中的卡顿慢SQL。\e[0m" .PHP_EOL; 119 | } 120 | else{ 121 | echo "\n"; 122 | echo "\e[38;5;196m".date('Y-m-d H:i:s')." 警告!出现卡顿慢SQL,请及时排查问题。\e[0m" .PHP_EOL; 123 | while($row = mysqli_fetch_array($result)){ 124 | file_put_contents(dirname(__FILE__).'/kill.txt', date('Y-m-d H:i:s')."\n". 125 | "用户名:".$row['USER']."\n". 126 | "来源IP:".$row['HOST']."\n". 127 | "数据库名:".$row['DB']."\n". 128 | "执行时间:".$row['TIME']."\n". 129 | "SQL语句:".$row['INFO']."\n". 130 | "\n", FILE_APPEND); 131 | 132 | if(isset($options['kill'])){ 133 | echo "\e[38;5;11m自动杀死执行时间超过{$options['B']}秒的慢SQL\e[0m" .PHP_EOL; 134 | $kill_sql = "KILL QUERY {$row['ID']}"; //默认只杀连接中的慢SQL,保留会话连接,如果想把连接也杀掉,去掉QUERY 135 | //$kill_sql = "KILL {$row['ID']}"; 136 | mysqli_query($con,$kill_sql); 137 | } 138 | 139 | //调用发邮件对象 140 | if(isset($options['mail'])){ 141 | $content = nl2br(file_get_contents(dirname(__FILE__).'/kill.txt')); 142 | require "smtp_config.php"; 143 | $smtp = new Smtp($smtpserver,$smtpserverport,true,$smtpuser,$smtppass);//这里面的一个true是表示使用身份验证,否则不使用身份验证. 144 | $smtp->debug = false;//是否显示发送的调试信息 145 | $status = $smtp->sendmail($smtpemailto, $smtpusermail, $mailtitle, $mailcontent, $mailtype); 146 | 147 | if($status==""){ 148 | echo "对不起,邮件发送失败!请检查邮箱填写是否有误。"; 149 | }else{ 150 | echo "恭喜!邮件发送成功!!" .PHP_EOL;} 151 | } 152 | 153 | //调用发微信wechat.py脚本 154 | if(isset($options['weixin'])){ 155 | $content1 = file_get_contents(dirname(__FILE__).'/kill.txt'); 156 | $status1 = system("/usr/bin/python wechat.py 'hcymysql' {$row['DB']}库出现卡顿慢SQL! '{$content1}'"); 157 | 158 | if($status1==""){ 159 | echo "对不起,微信发送失败!请检查wechat.py脚本设置是否有误。"; 160 | }else{ 161 | echo "恭喜!微信发送成功!!" .PHP_EOL;} 162 | } 163 | } 164 | } 165 | } 166 | 167 | class Daemon { 168 | private $pidfile; 169 | private $sleep_time; 170 | 171 | function __construct($st) { 172 | $this->pidfile = dirname(__FILE__).'/pt-kill.pid'; 173 | $this->sleep_time = $st; 174 | } 175 | 176 | private function startDeamon() { 177 | if (file_exists($this->pidfile)) { 178 | echo "The file $this->pidfile exists." . PHP_EOL; 179 | exit(); 180 | } 181 | 182 | $pid = pcntl_fork(); 183 | if ($pid == -1) { 184 | die('could not fork\n'); 185 | } else if ($pid) { 186 | echo 'start ok' . PHP_EOL; 187 | exit($pid); 188 | } else { 189 | file_put_contents($this->pidfile, getmypid()); 190 | return getmypid(); 191 | } 192 | } 193 | 194 | private function start(){ 195 | $pid = $this->startDeamon(); 196 | while (true) { 197 | Slowsql(); 198 | sleep($this->sleep_time); 199 | } 200 | } 201 | 202 | private function stop(){ 203 | if (file_exists($this->pidfile)) { 204 | $pid = file_get_contents($this->pidfile); 205 | posix_kill($pid, 9); 206 | unlink($this->pidfile); 207 | } 208 | } 209 | 210 | public function run($param) { 211 | if($param == 1) { 212 | $this->start(); 213 | }else if($param == 0) { 214 | $this->stop(); 215 | echo "pt-kill.php后台守护进程已停止。". PHP_EOL; 216 | } 217 | else{ 218 | echo 'daemon传参错误,请输入0关闭后台进程,1开启后台线程。'. PHP_EOL; 219 | } 220 | } 221 | 222 | } 223 | 224 | ?> 225 | 226 | -------------------------------------------------------------------------------- /pt_kill演示录像.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcymysql/pt-kill/6ed17c21640c9c93e826307140c5759d5dd3fabd/pt_kill演示录像.zip -------------------------------------------------------------------------------- /simplejson-3.8.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hcymysql/pt-kill/6ed17c21640c9c93e826307140c5759d5dd3fabd/simplejson-3.8.2.tar.gz -------------------------------------------------------------------------------- /smtp_config.php: -------------------------------------------------------------------------------- 1 | ".$content."";//邮件内容 15 | $mailtype = "HTML";//邮件格式(HTML/TXT),TXT为文本邮件 16 | //************************ 配置信息 **************************** 17 | 18 | ?> 19 | -------------------------------------------------------------------------------- /wechat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #_*_coding:utf-8 _*_ 3 | 4 | 5 | import urllib,urllib2 6 | import json 7 | import sys 8 | import simplejson 9 | 10 | reload(sys) 11 | sys.setdefaultencoding('utf-8') 12 | 13 | 14 | def gettoken(corpid,corpsecret): 15 | gettoken_url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=' + corpid + '&corpsecret=' + corpsecret 16 | print gettoken_url 17 | try: 18 | token_file = urllib2.urlopen(gettoken_url) 19 | except urllib2.HTTPError as e: 20 | print e.code 21 | print e.read().decode("utf8") 22 | sys.exit() 23 | token_data = token_file.read().decode('utf-8') 24 | token_json = json.loads(token_data) 25 | token_json.keys() 26 | token = token_json['access_token'] 27 | return token 28 | 29 | 30 | 31 | def senddata(access_token,user,subject,content): 32 | 33 | send_url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + access_token 34 | send_values = { 35 | "touser":user, #企业号中的用户帐号,在zabbix用户Media中配置,如果配置不正常,将按部门发送。 36 | "toparty":"1", #企业号中的部门id。 37 | "msgtype":"text", #消息类型。 38 | "agentid":"1", #企业号中的应用id。 39 | "text":{ 40 | "content":subject + '\n' + content 41 | }, 42 | "safe":"0" 43 | } 44 | # send_data = json.dumps(send_values, ensure_ascii=False) 45 | send_data = simplejson.dumps(send_values, ensure_ascii=False).encode('utf-8') 46 | send_request = urllib2.Request(send_url, send_data) 47 | response = json.loads(urllib2.urlopen(send_request).read()) 48 | print str(response) 49 | 50 | 51 | if __name__ == '__main__': 52 | user = str(sys.argv[1]) #zabbix传过来的第一个参数 53 | subject = str(sys.argv[2]) #zabbix传过来的第二个参数 54 | content = str(sys.argv[3]) #zabbix传过来的第三个参数 55 | 56 | corpid = 'wxaxxxxxxxxxxxxxx' #CorpID是企业号的标识 57 | corpsecret = 'vK4Foxxxxxxxxxxxxxxxxxxxx' #corpsecretSecret是管理组凭证密钥 58 | accesstoken = gettoken(corpid,corpsecret) 59 | senddata(accesstoken,user,subject,content) 60 | --------------------------------------------------------------------------------