背景说明
目前公司采用微服务架构,主要开发语言为PHP,通过Swoole开启TCP服务供业务端调用。通过公司内部编写的PHP扩展封装客户端调用逻辑。
需求
暂定使用Go语言开发新的业务,并提供TCP服务。其中老的PHP项目要通过原有的客户端扩展实现无修改调用。
解决方案
通过阅读客户端扩展源码了解调用逻辑。编写简单的测试如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?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服务试试,先不考虑过多的错误边界处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| 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 }
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]...) }
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)
fmt.Printf("Received:%s", uncompressData.Bytes())
conn.Close() return }
|
运行Go的TCP服务,跑一个PHP请求测试。
1 2
| on conn Received:{"api":"getUserInfo","params":{"user_id":123}}
|
经过多次修改测试终于成功。