在Ubuntu AWS EC2实例上优化Golang net/http服务器性能
在Ubuntu AWS EC2实例上优化Golang net/http服务器性能 我已将Go服务器部署在AWS EC2上。目前使用的是t2.micro实例,配备Ubuntu Server 18.04 LTS (HVM)、SSD卷类型和8GB硬盘。
Go代码:
我使用的代码如下:
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
var (
chars_10 string
)
func main() {
chars_10 = strings.Repeat("a", 10)
http.HandleFunc("/experiment-1/10-characters", handler_10_chars)
log.Fatal(http.ListenAndServe(":8888", nil))
}
func handler_10_chars(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, chars_10)
}
基准测试代码:
我使用的基准测试工具是K6,基准测试代码如下:
import http from "k6/http";
import { check } from "k6";
export let options = {
vus: 2048,
duration: "60s"
};
let url = ""; // 这里我定义AWS EC2实例端点
export default function() {
let res = http.get(url);
check(res, {
"status was 200": (r) => r.status == 200,
});
};
这段代码将在我的笔记本上执行,访问AWS EC2端点。
实验过程:
首先,我通过终端命令go build server.go将Go代码编译为单个二进制文件。然后,在终端运行**./server**启动服务器。
当我首次对服务器进行压力测试时,k6基准测试中抛出了一个错误:
WARN[0005] Request Failed error="Get http://aws-url:8888/: read tcp [::1]:60816->[::1]:8888: read: connection reset by peer"
其中aws-url是AWS自动生成的URL,8888是我启用的端口(并映射到服务器监听传入请求的端口)。
在网上搜索后,我发现这是因为SOMAXCONN变量的值为128。所以我的做法是将这个数字改为65535。具体方法是编辑/etc/sysctl.conf,在文件末尾添加以下行:
net.core.somaxconn=65535
然后,我再次运行K6基准测试。我注意到Connection reset by peer错误消失了。但这次服务器端出现了新的错误:
Accept error: too many open files.
我再次搜索如何解决这个新错误,发现这是因为系统文件描述符数量限制。所以我的做法是再次编辑/etc/sysctl.conf,在文件末尾添加以下行:
fs.file-max = 1000000
此外,我在终端运行了以下命令:
ulimit -n 99999
我再次运行K6基准测试。这次没有错误了。但我注意到一些问题:在每次基准测试中,CPU使用率和内存使用率始终低于20%(我无法让服务器使用100%的CPU),而TTFB最高增加到平均2秒。经过数小时尝试不同的Ubuntu配置后,我发现了这篇文章:Slow Server? This is the Flow Chart You’re Looking For | Scout APM Blog。我再次运行基准测试,注意到我的CPU表现符合第3步:
第3步:IO等待较低且空闲时间较高
因此我得出结论,Go没有使用全部CPU容量来处理请求。我的问题是:如何让Go使用100%的CPU容量,以便我能处理每秒最大请求数?您认为还有哪些其他瓶颈可能限制服务器达到100%的CPU使用率?
更多关于在Ubuntu AWS EC2实例上优化Golang net/http服务器性能的实战教程也可以访问 https://www.itying.com/category-94-b0.html
如果你使用另一台EC2实例来运行负载测试,而不是用笔记本电脑,会发生什么情况?你确定没有超出实例的带宽或CPU限制吗?我不确定AWS限制你的资源时,是否会显示为应用程序使用的CPU。
如果选择更大的EC2节点(一个作为服务器,一个用于生成负载)会怎样?或许使用更大的实例来生成负载会更好,这样你就有更多机会了解主节点的性能极限?
更多关于在Ubuntu AWS EC2实例上优化Golang net/http服务器性能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
要优化Go net/http服务器在AWS EC2实例上的性能并充分利用CPU资源,需要从多个层面进行调优。以下是具体的优化方案:
1. 优化Go HTTP服务器配置
package main
import (
"fmt"
"log"
"net/http"
"runtime"
"strings"
"time"
)
var (
chars_10 string
)
func main() {
// 设置最大CPU核心数
runtime.GOMAXPROCS(runtime.NumCPU())
chars_10 = strings.Repeat("a", 10)
// 创建自定义HTTP服务器配置
server := &http.Server{
Addr: ":8888",
Handler: http.HandlerFunc(handler_10_chars),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
MaxHeaderBytes: 1 << 20, // 1MB
}
log.Fatal(server.ListenAndServe())
}
func handler_10_chars(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, chars_10)
}
2. 使用更高效的HTTP路由器
package main
import (
"fmt"
"log"
"net/http"
"runtime"
"strings"
"time"
"github.com/gorilla/mux"
)
var (
chars_10 string
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
chars_10 = strings.Repeat("a", 10)
router := mux.NewRouter()
router.HandleFunc("/experiment-1/10-characters", handler_10_chars)
server := &http.Server{
Addr: ":8888",
Handler: router,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
log.Fatal(server.ListenAndServe())
}
func handler_10_chars(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, chars_10)
}
3. 优化系统级别的配置
在Ubuntu上创建systemd服务文件 /etc/systemd/system/go-server.service:
[Unit]
Description=Go HTTP Server
After=network.target
[Service]
Type=simple
User=ubuntu
Group=ubuntu
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/server
Restart=always
LimitNOFILE=1000000
LimitNPROC=1000000
[Install]
WantedBy=multi-user.target
4. 使用连接池优化客户端请求
// 在K6测试中优化连接重用
import http from "k6/http";
import { check } from "k6";
export let options = {
vus: 2048,
duration: "60s",
};
// 创建HTTP客户端实例以重用连接
const client = new http.Client({
timeout: '30s',
});
let url = "http://your-aws-url:8888/experiment-1/10-characters";
export default function() {
let res = client.get(url);
check(res, {
"status was 200": (r) => r.status == 200,
});
};
5. 启用HTTP/2和优化TLS配置(如果使用HTTPS)
package main
import (
"fmt"
"log"
"net/http"
"runtime"
"strings"
"time"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
chars_10 := strings.Repeat("a", 10)
server := &http.Server{
Addr: ":8888",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, chars_10)
}),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
ReadHeaderTimeout: 2 * time.Second,
}
// 启用HTTP/2
log.Fatal(server.ListenAndServe())
}
6. 监控和性能分析
添加性能监控端点:
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"runtime"
"strings"
"time"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
chars_10 := strings.Repeat("a", 10)
// 启用pprof性能分析
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
http.HandleFunc("/experiment-1/10-characters", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, chars_10)
})
server := &http.Server{
Addr: ":8888",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
log.Fatal(server.ListenAndServe())
}
潜在瓶颈分析:
- 网络带宽限制:t2.micro实例的网络性能有限
- 实例类型限制:t2.micro是突发性能实例,持续高负载时CPU积分会耗尽
- 系统调用开销:大量的小HTTP请求会产生显著的系统调用开销
- 内存分配:频繁的字符串操作和HTTP响应构建可能产生GC压力
要验证这些优化效果,可以使用以下命令监控服务器性能:
# 监控CPU使用率
htop
# 监控网络连接
netstat -an | grep :8888 | wc -l
# 监控文件描述符使用
lsof -p $(pgrep server) | wc -l
这些优化应该能够帮助Go服务器更有效地利用CPU资源,但需要注意t2.micro实例本身的性能限制可能仍然是主要瓶颈。

