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代码中,主要问题出现在并发处理与资源管理上。以下是具体分析和修改建议:

问题分析:

  1. wg.Done() 在请求处理开始时立即调用,可能导致 WaitGroup 计数器过早归零
  2. 并发执行 1000 个 goroutine 可能超出系统资源限制
  3. exec.Command 在每次请求中都会创建新进程,高频并发时会产生大量子进程
  4. 缺少对 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
}

主要改进:

  1. 使用 defer wg.Done() 确保计数器正确递减
  2. 添加信号量 (sem) 控制最大并发数,防止资源耗尽
  3. 用 Go 原生 archive/zip 包替代外部 zip 命令,避免创建大量子进程
  4. 改进错误处理,使用 log.Printf 替代 log.Fatalf 避免程序意外退出
  5. 添加文件存在性检查,避免因文件不存在导致的错误
  6. 使用 io.Discard 替代已弃用的 ioutil.Discard

这些修改解决了原代码中的并发控制问题和外部命令执行效率问题,提高了程序的稳定性和性能。

回到顶部