Swoole slowlog 乱码修复

先说结果:fork 了 swoole 的源码,修复之后打了个 v1.10.7 的版本。 发现问题 由于各种原因项目在使用 swoole 的 v1.x 远古版本跑 TCP 服务,基础环境如下。 CentOS 6.9 PHP 5.5.38 然后遇到了程序偶发性超时情况严重。怀疑是版本bug,首先将 swoole 升级到了当前环境可用的最高版本 v1.10.6,问题依然没有解决。但是这个版本已经有了慢日志功能,将慢日志功能开起来,模拟程序阻塞超时,试试看功能可用否。 tcp_server.php <?php class tcpTest { static public function aa() { self::bb(); } static public function bb() { sleep(3); } } class Server { static private $serv = null; private function __construct() { $serv = new \swoole_server("0.0.0.0", 9577); $serv->set([ 'reactor_num' => 2, 'worker_num' => 8, 'task_worker_num' => 0, 'dispatch_mode' => 2, 'daemonize' => false, 'tcp_fastopen' => true, 'request_slowlog_timeout' => 2, 'request_slowlog_file' => '/tmp/swoole_slow.log', 'trace_event_worker' => true, ]); $serv->on('Start', array($this, 'onStart')); $serv->on('Connect', array($this, 'onConnect')); $serv->on('Receive', array($this, 'onReceive')); $serv->on('Close', array($this, 'onClose')); $serv->start(); } public function onStart($serv) { echo __METHOD__ . PHP_EOL; } public function onConnect($serv, $fd, $from_id) { echo __METHOD__ . " worker_id:{$serv->worker_id} work_pid:{$serv->worker_pid} fd:{$fd} from_id:{$from_id}" . PHP_EOL; } public function onReceive($serv, $fd, $from_id, $data) { $fdinfo = $serv->connection_info($fd,$from_id,true); echo __METHOD__ . " ip:{$fdinfo['remote_ip']} worker_id:{$serv->worker_id} work_pid:{$serv->worker_pid} fd:{$fd} from_id:{$from_id} data:{$data}" . PHP_EOL; \tcpTest::aa(); $res_data = ['time' => date('Y-m-d H:i:s')]; $serv->send($fd, json_encode($res_data)); } public function onClose($serv, $fd, $from_id) { echo __METHOD__ . " worker_id:{$serv->worker_id} work_pid:{$serv->worker_pid} fd:{$fd} from_id:{$from_id}" . PHP_EOL . PHP_EOL; } static public function inst() { if (!(self::$serv instanceof self)) { self::$serv = new self; } return self::$serv; } } $res = \Server::inst(); tcp_client.php ...

May 11, 2020 · 3 min · 433 words · Nick

容器服务化方向的一些探索

本文参考使用Docker打造自己的云平台编写 本文基于 Docker Swarm Mode 实现容器化,虽然目前 k8s 更火一些,但实在是太重了,以后再折腾。 使用 traefik 来实现反向代理、负载均衡,traefik 还自带了服务发现、后端断路器、健康检查等,相当于是自带服务发现的 nginx。当然它还支持其他的容器编排工具如,服务发现工具如 Consul。 使用 Portainer 来管理 Docker 容器,可以兼容 Docker Swarm 模式。 Docker 的安装就不说了。装完初始化 Swarm 模式。 $ docker swarm init 先设置一下环境变量。 $ export DOCKER_DEV_PATH=/usr/local/src/docker-dev 日志文件都会统一存到 ${DOCKER_DEV_PATH}/logs。 traefik 新建一个编排文件 traefik.yml。 version: '3.3' services: reverse-proxy: image: traefik:1.7-alpine command: --web --docker --docker.domain=cloud-labs.io --docker.watch --docker.swarmmode=true --loglevel=INFO --accesslog --accesslog.filepath=/logs/access.log --traefiklog --traefiklog.filepath=/logs/traefik.log deploy: mode: replicated replicas: 1 labels: - traefik.enable=true - traefik.backend=traefik - traefik.frontend.rule=Host:monitor.cloud-labs.io - traefik.port=8080 - traefik.docker.network=traefik_proxy networks: - proxy ports: - 8081:80 volumes: - /var/run/docker.sock:/var/run/docker.sock - ${DOCKER_DEV_PATH}/logs/traefik:/logs - /dev/null:/traefik.toml networks: proxy: 简单解释下 labels 中配置的含义 ...

August 28, 2019 · 2 min · 361 words · Nick

编写Go的TCP服务来替代PHP的Swoole

背景说明 目前公司采用微服务架构,主要开发语言为PHP,通过Swoole开启TCP服务供业务端调用。通过公司内部编写的PHP扩展封装客户端调用逻辑。 需求 暂定使用Go语言开发新的业务,并提供TCP服务。其中老的PHP项目要通过原有的客户端扩展实现无修改调用。 解决方案 通过阅读客户端扩展源码了解调用逻辑。编写简单的测试如下。 <?php $_client = new \swoole_client(SWOOLE_SOCK_TCP | SWOOLE_KEEP); $_client->set([ 'open_length_check' => true, 'package_length_type' => 'N', 'package_length_offset' => 0, 'package_body_offset' => 4, 'package_max_length' => 24657920, ]); if (false == $_client->connect("127.0.0.1", 8880)) { printf("err_msg: %s err_code: %s" . PHP_EOL, var_export($_client->errMsg, true), var_export($_client->errCode, true)); } // 随便测试个请求参数 $data = [ 'api' => 'getUserInfo', 'params' => [ 'user_id' => 123 ] ]; $data = json_encode($data); $data = gzcompress($data, 9); $_client->send(pack("N", strlen($data)) . $data); $res = $_client->recv(); $end = getTime(); $data = json_decode($res, true); 其中前4个字节是head,表示body长度,采用二进制大端字节序编码。body先进行json编码再进行了zlib压缩。这都是编写Go的TCP服务时需要处理的。 写个简单的Go TCP服务试试,先不考虑过多的错误边界处理。 package main import ( "bytes" "compress/zlib" "encoding/binary" "fmt" "io" "net" ) func main() { ln, err := net.Listen("tcp", ":8880") if err != nil { fmt.Printf("%s", err) } for { conn, err := ln.Accept() if err != nil { fmt.Printf("accept err:%s", err) } go handleConnection(conn) } } func handleConnection(conn net.Conn) { fmt.Println("on conn") var err error headLen := 4 head := make([]byte, headLen) if _, err = conn.Read(head); err != nil { fmt.Println(err.Error()) return } // 解码大端字节序获取body长度 bodyLen := binary.BigEndian.Uint32(head) allBody := make([]byte, 0) readLen := 0 for bodyLen > 0 { body := make([]byte, bodyLen) readLen, err = conn.Read(body) if err != nil { fmt.Println(err.Error()) return } bodyLen = bodyLen - uint32(readLen) allBody = append(allBody, body[:readLen]...) } // 解压zlib压缩的数据 RFC 1950 b := bytes.NewReader(allBody) uncompressRead, err := zlib.NewReader(b) if err != nil { fmt.Printf("uncompress data err:%s", err) } var uncompressData bytes.Buffer io.Copy(&uncompressData, uncompressRead) // 解出的json字符串 fmt.Printf("Received:%s", uncompressData.Bytes()) // 路由调用实际业务逻辑处理 ... // conn.Write() conn.Close() return } 运行Go的TCP服务,跑一个PHP请求测试。 ...

April 30, 2019 · 2 min · 247 words · Nick