Go写一个云函数,支持长连接
参考链接
https://github.com/tencentyun/scf-go-lib
开发说明
推荐安装Tencent CloudBase Toolkit
打包说明
GOOS=linux GOARCH=amd64 go build -o main main.go
zip main.zip main
需要注意,go版本的云函数只能本地编译成可执行文件后在上传zip,而且看起来必须要指定GOARCH以及GOOS。否则云函数是跑步起来的。
部署说明
选择上传
点击保存,就可以测试了;
注意,千万别直接在线上IDE编辑上改代码测试!
就算你试一千遍都不会生效的,很奇怪,既然不支持go在线编辑,那为何不把这个入口干掉呢。
代码示例
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/tencentyun/scf-go-lib/cloudfunction"
"github.com/tencentyun/scf-go-lib/functioncontext"
)
func hello(ctx context.Context, event interface{}) (interface{}, error) {
lc, _ := functioncontext.FromContext(ctx)
fmt.Printf("function ClientContext.Env: %s\n", lc.ClientContext.Env)
fmt.Printf("function ClientContext.Custom: %s\n", lc.ClientContext.Custom)
// fmt.Printf("function Environment: %s\n", lc.Environment)
requestUrl := lc.Environment["APIGateWay_Url"] //这里可以拿到自己设置的环境变量
secretId := lc.Environment["APIGateWay_SecretId"]
secretKey := lc.Environment["APIGateWay_SecretKey"]
fmt.Printf("requestUrl %s\n,secretId %s\n,secretKey %s\n", requestUrl, secretId, secretKey)
resp, err := http.Get("http://httpbin.org/get")
if err != nil {
return err, nil
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var res interface{}
json.Unmarshal(body, &res)
return res, nil
}
func main() {
// Make the handler available for Remote Procedure Call by Cloud Function
cloudfunction.Start(hello)
}
这里在微信小程序云函数环境中,有点坑,是拿不到请求客户端的clientip的。ClientContext里面打印出来,发现是没有该项的,已经在开发者社区中提问了。
长连接优化
package main
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"time"
)
var httpTransportCloseOnComplete = &http.Transport{
DisableKeepAlives: true, //禁用长连接
}
var httpTransportKeepAlive = &http.Transport{
DialContext: (&net.Dialer{
Timeout: 3 * time.Second, // 连接超时时间
KeepAlive: 7 * time.Second, // 保持长连接的时间
}).DialContext, // 设置连接的参数
MaxIdleConns: 500, // 最大空闲连接
IdleConnTimeout: 60 * time.Second, // 空闲连接的超时时间
ExpectContinueTimeout: 30 * time.Second, // 等待服务第一个响应的超时时间
MaxIdleConnsPerHost: 100, // 每个host保持的空闲连接数
DisableKeepAlives: false, //长连接生效
}
func main() {
times := 20
uri := "https://github.com/request/request/blob/master/request.js"
// 短连接的情况
start := time.Now()
client := http.Client{Transport: httpTransportCloseOnComplete} // 初始化http的client
for i := 0; i < times; i++ {
req, err := http.NewRequest(http.MethodGet, uri, nil)
if err != nil {
panic("Http Req Failed " + err.Error())
}
resp, err := client.Do(req) // 发起请求
if err != nil {
panic("Http Request Failed " + err.Error())
}
defer resp.Body.Close()
ioutil.ReadAll(resp.Body)
}
fmt.Println("Orig GoNet Short Link", time.Since(start))
// 长连接的情况
start2 := time.Now()
client2 := http.Client{Transport: httpTransportKeepAlive} // 初始化一个带有transport的http的client
for i := 0; i < times; i++ {
req, err := http.NewRequest(http.MethodGet, uri, nil)
if err != nil {
panic("Http Req Failed " + err.Error())
}
resp, err := client2.Do(req)
if err != nil {
panic("Http Request Failed " + err.Error())
}
defer resp.Body.Close()
ioutil.ReadAll(resp.Body) // 如果不及时从请求中获取结果,此连接会占用,其他请求服务复用连接
}
fmt.Println("Orig GoNet Long Link", time.Since(start2))
}
这里使用https://github.com/request/request/blob/master/request.js 测试,使用长连接明显要好过不适用长连接。
这里有一点需要注意,这种特性还需要服务端支持,比如,你遇到这种服务端,那基本上你客户端在怎么努力也行不通
package main
import (
"log"
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request) {
log.Println("receive a request from:", r.RemoteAddr, r.Header)
w.Write([]byte("ok"))
}
func main() {
var s = http.Server{
Addr: ":8080",
Handler: http.HandlerFunc(Index),
}
s.SetKeepAlivesEnabled(false)//服务度直接给你关闭
s.ListenAndServe()
}
各种测试长连接的参考链接在这里