Golang中遇到C语言问题需要帮助
Golang中遇到C语言问题需要帮助
package main
import (
"context"
"fmt"
"golang.org/x/net/http2"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"sync"
"sync/atomic"
)
func main() {
n := 1000
wg := new(sync.WaitGroup)
wg.Add(n)
var rn int64
cst := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wg.Done()
atomic.AddInt64(&rn, 1)
fmt.Println(" 请求编号 ", atomic.LoadInt64(&rn))
files := []string{
"main.go",
"/home/v/GolangProjects/browsefile/backend/.gometalinter.json",
"/home/v/GolangProjects/browsefile/backend/LICENSE",
"/home/v/GolangProjects/browsefile/backend/.dockerignore",
}
if err := serveDownload(r.Context(), w, files); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}))
if err := http2.ConfigureServer(cst.Config, new(http2.Server)); err != nil {
log.Fatal(err)
}
cst.TLS = cst.Config.TLSConfig
cst.StartTLS()
defer cst.Close()
tr := &http.Transport{
TLSClientConfig: cst.Config.TLSConfig,
}
tr.TLSClientConfig.InsecureSkipVerify = true
http2.ConfigureTransport(tr)
for i := 0; i < n; i++ {
go func() {
req, _ := http.NewRequest("GET", cst.URL, nil)
res, err := tr.RoundTrip(req)
if err != nil {
log.Fatalf("请求失败: %v", err)
}
defer res.Body.Close()
if false { // 取消注释以检查完整响应并确保使用HTTP/2.0
blob, _ := httputil.DumpResponse(res, true)
println(string(blob))
} else {
io.Copy(ioutil.Discard, res.Body)
}
}()
}
fmt.Println("在此处挂起")
wg.Wait()
}
func serveDownload(ctx context.Context, w http.ResponseWriter, files []string) (err error) {
// 定义文件名
name := "archive.zip"
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
pr, pw := io.Pipe()
// j - 剪切路径,仅存储
cmd := exec.Command("zip", append([]string{"-0rq", "-"}, files...)...)
cmd.Stdout = pw
cmd.Stderr = os.Stderr
go func() {
defer pr.Close()
// 将通过cmd写入PipeReader的数据复制到stdout
if _, err := io.Copy(w, pr); err != nil {
panic(err)
}
}()
return cmd.Run()
}
更多关于Golang中遇到C语言问题需要帮助的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
这个论坛管理过度,我已经解决了自己的问题,甚至在此之前都没有机会编辑或查看我的帖子。请删除这个帖子以及我的账户,不再有兴趣使用这个论坛了。
更多关于Golang中遇到C语言问题需要帮助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在您提供的Go代码中,主要问题出现在并发处理与资源管理上。以下是具体分析和修改建议:
问题分析:
wg.Done()在请求处理开始时立即调用,可能导致 WaitGroup 计数器过早归零- 并发执行 1000 个 goroutine 可能超出系统资源限制
exec.Command在每次请求中都会创建新进程,高频并发时会产生大量子进程- 缺少对
cmd.Start()错误处理和进程终止控制
修改后的代码:
package main
import (
"archive/zip"
"context"
"fmt"
"golang.org/x/net/http2"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"sync"
"sync/atomic"
)
func main() {
n := 100 // 降低并发数避免资源耗尽
wg := new(sync.WaitGroup)
wg.Add(n)
var rn int64
cst := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer wg.Done() // 确保在函数退出时调用
current := atomic.AddInt64(&rn, 1)
fmt.Println("请求编号", current)
files := []string{
"main.go",
"/home/v/GolangProjects/browsefile/backend/.gometalinter.json",
"/home/v/GolangProjects/browsefile/backend/LICENSE",
"/home/v/GolangProjects/browsefile/backend/.dockerignore",
}
if err := serveDownload(r.Context(), w, files); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}))
if err := http2.ConfigureServer(cst.Config, new(http2.Server)); err != nil {
log.Fatal(err)
}
cst.TLS = cst.Config.TLSConfig
cst.StartTLS()
defer cst.Close()
tr := &http.Transport{
TLSClientConfig: cst.Config.TLSConfig,
}
tr.TLSClientConfig.InsecureSkipVerify = true
http2.ConfigureTransport(tr)
// 使用工作池控制并发
sem := make(chan struct{}, 50) // 限制最大并发数为50
for i := 0; i < n; i++ {
sem <- struct{}{}
go func(i int) {
defer func() { <-sem }()
req, err := http.NewRequest("GET", cst.URL, nil)
if err != nil {
log.Printf("创建请求失败: %v", err)
return
}
res, err := tr.RoundTrip(req)
if err != nil {
log.Printf("请求失败: %v", err)
return
}
defer res.Body.Close()
// 读取并丢弃响应体
_, err = io.Copy(io.Discard, res.Body)
if err != nil {
log.Printf("读取响应失败: %v", err)
}
}(i)
}
fmt.Println("等待所有请求完成...")
wg.Wait()
fmt.Println("所有请求处理完成")
}
// 使用Go原生zip库替代外部命令
func serveDownload(ctx context.Context, w http.ResponseWriter, files []string) error {
name := "archive.zip"
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
w.Header().Set("Content-Type", "application/zip")
zipWriter := zip.NewWriter(w)
defer zipWriter.Close()
for _, file := range files {
// 检查文件是否存在
if _, err := os.Stat(file); os.IsNotExist(err) {
continue
}
// 读取文件内容
data, err := os.ReadFile(file)
if err != nil {
return fmt.Errorf("读取文件 %s 失败: %v", file, err)
}
// 创建zip文件条目
f, err := zipWriter.Create(file)
if err != nil {
return fmt.Errorf("创建zip条目失败: %v", err)
}
// 写入文件内容
_, err = f.Write(data)
if err != nil {
return fmt.Errorf("写入zip内容失败: %v", err)
}
}
return nil
}
主要改进:
- 使用
defer wg.Done()确保计数器正确递减 - 添加信号量 (
sem) 控制最大并发数,防止资源耗尽 - 用 Go 原生
archive/zip包替代外部zip命令,避免创建大量子进程 - 改进错误处理,使用
log.Printf替代log.Fatalf避免程序意外退出 - 添加文件存在性检查,避免因文件不存在导致的错误
- 使用
io.Discard替代已弃用的ioutil.Discard
这些修改解决了原代码中的并发控制问题和外部命令执行效率问题,提高了程序的稳定性和性能。

