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资源。

主要问题在于你的代码中可能存在以下情况:

  1. 未限制并发goroutine数量
  2. 未使用连接池
  3. 未设置合理的超时时间
  4. 未使用缓冲通道控制并发

以下是修复方案:

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
}

关键改进点:

  1. 限制并发goroutine数量 - 使用信号量或worker池
  2. 设置请求大小限制 - 防止过大请求
  3. 配置服务器超时 - 防止长时间阻塞
  4. 使用缓冲队列 - 平滑处理流量峰值

这些修改应该能解决你的CPU占用100%问题。

回到顶部