通过企业微信构建消息推送服务

之前用server酱的微信消息推送服务,但是最近老抽风,准备换别的推送服务。 目前发现企业微信的应用消息很合适优点不少。个人也可注册使用。 图文消息 mpnews 内容最大支持666K字节 一次可最多发送八条消息,虽然基本都是发单条 可以显示摘要 可以消息免打扰 虽然是企业微信的应用消息,但是通过关注微信插件可以直接在微信app内查看消息。 简单使用 Golang 编写了一个腾讯 Serverless 云函数,实现了类似于server酱的消息推送,GET、POST方式调用均可。 腾讯云函数目前每月都有免费额度个人完全够用了,API网关新开会送一年资源包也是够用完全白嫖。可以绑定自有域名,选择香港区域,不用备案。 一年到期之后,可以转战阿里云。代码很简单,改改也可以直接docker部署。 详见GitHub wx-msg-push-tencent

March 15, 2021 · 1 min · 18 words · Nick

Golang 与 PHP 的 json 序列化问题

Intro 最近在做 Golang 与 PHP 的 RPC 实现。因 PHP 业务端已上线稳定,Golang 方面则需要完全兼容。其中使用了 json 序列化,发现区别还是很大的,见下面代码。 $ php -a php > echo json_encode("<test我爱中国>"); "<test\u6211\u7231\u4e2d\u56fd>" package main import ( "encoding/json" "fmt" ) func main() { st := "<test我爱中国>" res, err := json.Marshal(st) if err != nil { fmt.Println("json err:", err.Error()) } fmt.Printf("json is: %s", string(res)) // json is: "\u003ctest我爱中国\u003e" } PHP 默认的 json_encode() 函数会把多字节字符转成 \uXXXX 当然通过设置 JSON_UNESCAPED_UNICODE 可以解决这个问题。这里不动 PHP 代码。 Golang 这里用 json 包的 Marshal 方法实现序列化,对多字节字符是不进行处理的。但是这个方法出于安全考虑会将"<", “>”, “&“这三个字符转成 \uXXXX 形式。这还不是最魔幻的,这个方法没有可选参数进行设置。 ...

December 23, 2019 · 3 min · 467 words · Nick

Golang 生成 O'Reilly 在线学习平台的电子书

O’Reilly 是有名的动物书出版公司,当然他们也不仅仅出电子书还有很多别的业务。O’Reilly 在线学习平台上有他们所有出版过的图书都是原版的,但是仅提供在线阅读,虽然也有 App,但是想在 kindle 上看提高阅读体验。 于是抽时间练习练习 Golang 写了个程序,生成 mobi 格式的电子书。Github 地址 oreilly_kindle_book 。 使用说明 目前仅支持 macOS、linux, windows 改改应该也可以,但是我手头没有 windows 测不了,或者编个 Docker image 也 ok。 需要 O’Reilly 在线学习平台的账号,这是必须的。 安装 kindlegen,mobi 的生成就靠它。 oreilly_kindle_book 编译 基于 golang 1.13 开发,目前没有上传二进制包,自行编译吧。 推荐 clone 之后直接 make && make install。 $ git clone https://github.com/zyh94946/oreilly_kindle_book.git $ cd oreilly_kindle_book $ make && make install Building oreilly_kindle_book... Building success... Installing oreilly_kindle_book... Install success to /usr/local/bin/oreilly_kindle_book. Usage of oreilly_kindle_book: -email string you login email of https://www.oreilly.com/member/ -help help -n string the num of https://learning.oreilly.com/library/view/BOOK-NAME/*** -p string you login password of https://www.oreilly.com/member/ -version print version and exit 当然 go get 也可以 ...

October 31, 2019 · 1 min · 155 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