Golang中net/http的POST方法导致CPU占用100%怎么办
Golang中net/http的POST方法导致CPU占用100%怎么办
我使用net/http和julienschmidt/httprouter构建了一个Web服务器。不知道为什么,当我提交"大量"数据时,比如包含数百行换行的<textarea>内容,我的Go服务器就会崩溃。
我已经尝试使用goroutine将for _, value := range target从主线程中分离出来,并尝试增加ulimit,但似乎没有效果。代码如下:
2 回复
尝试将 helper.DataDatabase() 移出 for range 循环。如果它确实创建了新的数据库连接(我不确定,只是猜测),这将是一个非常昂贵的操作。
更多关于Golang中net/http的POST方法导致CPU占用100%怎么办的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个典型的I/O密集型操作导致的CPU占用问题。当处理大量数据时,如果没有正确的并发控制和资源管理,goroutine会大量创建并竞争CPU资源。
主要问题在于你的代码中可能存在以下情况:
- 未限制并发goroutine数量
- 未使用连接池
- 未设置合理的超时时间
- 未使用缓冲通道控制并发
以下是修复方案:
package main
import (
"fmt"
"log"
"net/http"
"sync"
"time"
"github.com/julienschmidt/httprouter"
)
// 限制并发处理的goroutine数量
const maxConcurrent = 100
// 使用带缓冲的通道作为信号量
var semaphore = make(chan struct{}, maxConcurrent)
type RequestProcessor struct {
wg sync.WaitGroup
}
func (rp *RequestProcessor) processData(data string) {
defer rp.wg.Done()
defer func() { <-semaphore }() // 释放信号量
// 模拟数据处理
time.Sleep(10 * time.Millisecond)
_ = len(data) // 实际的数据处理逻辑
}
func handlePost(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 限制请求体大小
r.Body = http.MaxBytesReader(w, r.Body, 10<<20) // 10MB限制
if err := r.ParseForm(); err != nil {
http.Error(w, "Request too large", http.StatusRequestEntityTooLarge)
return
}
data := r.FormValue("data")
if data == "" {
http.Error(w, "No data provided", http.StatusBadRequest)
return
}
processor := &RequestProcessor{}
// 分割数据并并发处理(受信号量控制)
lines := splitLines(data)
for _, line := range lines {
select {
case semaphore <- struct{}{}: // 获取信号量
processor.wg.Add(1)
go processor.processData(line)
default:
// 如果达到并发限制,直接在当前goroutine中处理
processor.processData(line)
processor.wg.Add(1)
}
}
processor.wg.Wait()
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Processing completed")
}
func splitLines(data string) []string {
// 简单的换行分割实现
var lines []string
var line string
for _, char := range data {
if char == '\n' {
if line != "" {
lines = append(lines, line)
line = ""
}
} else {
line += string(char)
}
}
if line != "" {
lines = append(lines, line)
}
return lines
}
func main() {
router := httprouter.New()
router.POST("/submit", handlePost)
server := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Println("Server starting on :8080")
log.Fatal(server.ListenAndServe())
}
更优化的版本使用worker池模式:
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/julienschmidt/httprouter"
)
const (
maxWorkers = 50
jobQueueSize = 1000
)
type Job struct {
Data string
}
type WorkerPool struct {
jobs chan Job
workers int
}
func NewWorkerPool(workers int) *WorkerPool {
pool := &WorkerPool{
jobs: make(chan Job, jobQueueSize),
workers: workers,
}
for i := 0; i < workers; i++ {
go pool.worker()
}
return pool
}
func (wp *WorkerPool) worker() {
for job := range wp.jobs {
// 处理数据
processLine(job.Data)
}
}
func (wp *WorkerPool) Submit(data string) bool {
select {
case wp.jobs <- Job{Data: data}:
return true
default:
return false // 队列已满
}
}
func (wp *WorkerPool) Close() {
close(wp.jobs)
}
func processLine(line string) {
// 实际的数据处理逻辑
time.Sleep(5 * time.Millisecond)
_ = len(line)
}
func main() {
workerPool := NewWorkerPool(maxWorkers)
defer workerPool.Close()
router := httprouter.New()
router.POST("/submit", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
r.Body = http.MaxBytesReader(w, r.Body, 5<<20) // 5MB限制
if err := r.ParseForm(); err != nil {
http.Error(w, "Request too large", http.StatusRequestEntityTooLarge)
return
}
data := r.FormValue("data")
lines := splitLines(data)
processed := 0
for _, line := range lines {
if workerPool.Submit(line) {
processed++
}
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Submitted %d lines for processing", processed)
})
server := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Println("Server starting on :8080")
log.Fatal(server.ListenAndServe())
}
func splitLines(data string) []string {
var lines []string
var line string
for _, char := range data {
if char == '\n' {
if line != "" {
lines = append(lines, line)
line = ""
}
} else {
line += string(char)
}
}
if line != "" {
lines = append(lines, line)
}
return lines
}
关键改进点:
- 限制并发goroutine数量 - 使用信号量或worker池
- 设置请求大小限制 - 防止过大请求
- 配置服务器超时 - 防止长时间阻塞
- 使用缓冲队列 - 平滑处理流量峰值
这些修改应该能解决你的CPU占用100%问题。

