# http断开后,如何继续执行服务端代码
# 问题
# 业务需求
- 服务端需要调用一个event stream格式响应的接口,实时响应数据给客户端。
- 响应数据是被分为多个数据块,以流失数据的形式给到。
- 服务端能够实现对event stream接口的完整调用
# 遇到问题
由于公司主流的技术架构还是lamp那一套,apache服务器通过调用php module来执行php的脚本。当http请求断开后,php脚本就会终止执行。当客户端没有等待http响应完成就主动断开了连接,就会导致服务端没有从封装的event stream接口中获取到完整的数据。是一次无效的接口调用。
# 思考
为了解决上面的问题,基于公司现有的技术架构,我首先想到了如下方案:
# 方案1.0
- 开启多个常驻的php进程来处理event stream接口的调用,生产数据。
- 使用redis list来进行数据之间的同步。
- 服务端通告轮询redis list消费数据,直到接受到"data [done]"结束。
# 优缺点
- 优点是http断开后常驻脚本依旧能够完成接口的调用。
- 缺点是无法解决多用户的并发问题,同一个常驻脚本同一时间只能处理一个请求。
在考虑到并发之后,我想到了下面的第二个方案:
# 方案2.0
- 使用go来实现业务逻辑,并以api或者让rpc的形式暴露服务,供php调用。这样既能解决调用event stream接口调用完整性的问题,同时还能实现高并发的能力。而且go有着比php更高的性能。
# 解答
下面的解答示例只演示了http主动断开后go继续执行直到结束的相关demo
# DEMO
package main
import (
"log"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Any("/long_async_operation", func(c *gin.Context) {
// 启动goroutine
go func() {
for i := 0; i < 10; i++ {
// 进行耗时操作
time.Sleep(1 * time.Second)
// 结束时打印日志
log.Println("long_async_operation done")
}
}()
})
r.Run(":8081")
}
# 启动服务
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] POST /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] PUT /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] PATCH /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] HEAD /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] OPTIONS /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] DELETE /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] CONNECT /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] TRACE /long_async_operation --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :8081
[GIN] 2023/05/21 - 12:17:34 | 200 | 750ns | 127.0.0.1 | HEAD "/long_async_operation"
2023/05/21 12:17:35 long_async_operation done
2023/05/21 12:17:36 long_async_operation done
2023/05/21 12:17:37 long_async_operation done
2023/05/21 12:17:38 long_async_operation done
2023/05/21 12:17:39 long_async_operation done
2023/05/21 12:17:40 long_async_operation done
2023/05/21 12:17:41 long_async_operation done
2023/05/21 12:17:42 long_async_operation done
2023/05/21 12:17:43 long_async_operation done
2023/05/21 12:17:44 long_async_operation done
# 客户端调用
$ curl -I http://localhost:8081/long_async_operation
HTTP/1.1 200 OK
Date: Sun, 21 May 2023 04:17:34 GMT