Goroutine Pool 实现高并发

本文是读完 Handling 1 Million Requests per Minute with Go 之后,根据自己的理解,对文中提到的并发模型和实现再梳理一遍。 前言 假设有一个 http server 接收 client 发来的 request,如果用下面的这样的代码,会有什么问题呢? func payloadHandler(w http.ResponseWriter, r *http.Request) { // Go through each payload and queue items individually to be posted to S3 for _, payload := range content.Payloads { go payload.UploadToS3() // <----- DON'T DO THIS } } 显而易见,有 2 个问题: 接收一个 request 就开启一个 goroutine 处理,当 request 数量在短时间内暴增的话,光是 goroutine 的数量都足以让 server 崩溃。 每个 goroutine 都会与后端建立 TCP 连接,既耗费三次握手的时间,也会造成后端有大量 TCP 连接 所以,我们的目标是 没有蛀牙 可以控制 goroutine 的总数,方法是事先创建好一定数量的 goroutine,加入到一个 Pool 中 goroutine 启动时与后端建立 TCP 长连接,之后的通信都基于这个连接 根据原文作者给出的方法,整体的架构如下: ...

2020-10-13 · Me

gRPC-go 建立 TCP 连接的过程

首先看一个最简单的建立 client server 之间 gRPC 连接的代码,以这个代码为例,分析一下 TCP 是在何时建立的。 Server 端的代码相对来说很容易,一个最简单的 server 代码如下: func main() { lis, _ := net.Listen("tcp", fmt.Sprintf(":%d", 8080)) grpcServer := grpc.NewServer() protobuf.RegisterTestServer(grpcServer, &server{}) grpcServer.Serve(lis) } 在 grpc/server.go 中的 Serve() 函数调用了 lis.Accept() 并阻塞,当 client 端发来 TCP 请求时,Accept() 返回 Conn 结构,并开启 goroutine handleRawConn() 进行后续的处理。 就 TCP 来说,server 端的代码简单易懂,相比之下 client 端则不一样,一个基本的 Client 代码如下: func main() { conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure()) defer conn.Close() cli := protobuf.NewTestClient(conn) } 而要弄清楚 Client 端如何建立 TCP 却不容易,这是因为 grpc client 有 resolve DNS 以及做 load balancer 的功能,因此代码复杂很多。 从上面的代码不难看出肯定是在 Dial() 函数中建立的,它的具体实现是在 DialContext(), 返回 ClientConn 结构体指针,但是却看不到在哪建立了 TCP 链接,这是因为 TCP 链接是在一个 Goroutine 中异步建立的。如果想要 DialContext() 等连接建立完再返回,可以指定grpc.WithBlock()传入Options来实现。 ...

2020-10-11 · Me

Golang 操作共享内存

前言 进程间通信的方式有很多种,如果两个进程分别在不同的机器上,那么使用 socket 通信;如果在同一台机器上,共享内存机制是一种快速高效的方式。 本文实现一个 go 语言二进制程序和 C 语言二进制程序通过共享内存交换数据。 提到共享内存主要有两种: System V 标准的 shmget/shmdt 等接口 POSIX 标准的 shm_open 等接口 另外 Linux 下 mmap() 匿名映射也是最常用的进程间共享内存方法。 创建了共享内存以后,一般会显示在系统的 /dev/shm 目录下。Linux 默认 /dev/shm 为实际物理内存的1/2, 比如我的机器上物理内存为 16G,运行 df 命令后可以看到 /dev/shm 的大小为 7.8G 。 $ df -h Filesystem Size Used Avail Use% Mounted on tmpfs 1.6G 3.2M 1.6G 1% /run tmpfs 7.8G 4.0K 7.8G 1% /dev/shm tmpfs, ramfs 和 ramdisk tmpfs是一个虚拟内存文件系统,在Linux内核中,虚拟内存资源由物理内存(RAM)和交换分区组成,Tmpfs可以使用物理内存,也可以使用交换分区。 ramdisk 是一个块设备,只不过它是存在于内存上的。 ramfs 也是文件系统,不过已经被 tmpfs 替代了。 ...

2019-08-02 · Me