写入响应写入器的内容显示出不稳定的行为。

huangapple go评论86阅读模式
英文:

Content written to response writer shows erratic behaviour

问题

文件:main.go

import (
  "github.com/gorilla/mux"
  )
  
func setupRouting(pBus *bus.Bus) *mux.Router {

	log.Debug("设置路由")

	r := mux.NewRouter()

	pInvoiceHandler := handlers.NewInvoiceHandler(pBus)

	postRouter.HandleFunc("/invoice", pInvoiceHandler.HandleInvoiceGenerationRequest)


	return r
}

main() {
	pRouter := setupRouting(pBus)
	
	s := &http.Server{
		Addr:         address,
		Handler:      pRouter,
		IdleTimeout:  time.Duration(idlTimeout) * time.Second,
		ReadTimeout:  time.Duration(rdTimeout) * time.Second,
		WriteTimeout: time.Duration(wrtTimeout) * time.Second,
	}
}

文件:InvoiceGenerationHandler.go

func (ih *InvoiceHandler) HandleInvoiceGenerationRequest(rw http.ResponseWriter, pReq *http.Request) {

	log.Info("处理发票生成请求")

	query := pReq.URL.Query()
	resourceGrp := query.Get("rGrp")

	if resourceGrp == "" {
		http.Error(rw, "无效的URL,URL中应包含rGrp参数", http.StatusBadRequest)
		return
	}

	pSliceCostDetailsCtx, err := ih.pApiLoadTesting.CalculateCost(resourceGrp)

	if err != nil {
		log.Error("无法计算成本:", err.Error())
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}

	pResponseJson, err := json.Marshal(pSliceCostDetailsCtx)

	if err != nil {
		log.Error("无法将价格上下文切片转换为json:", err.Error())
		http.Error(rw, "无法生成成本响应", http.StatusInternalServerError)
		log.Error(pResponseJson)
		return
	}

	rw.Write(pResponseJson)
}

文件:apiloadtestpricing.go

type ApiLoadTestPricing struct {
	mpHttpClient *http.Client
}

func NewApiLoadTesting() *ApiLoadTestPricing {
	var pHttpClient = &http.Client{
		Timeout: time.Second * 120,
	}
	return &ApiLoadTestPricing{pHttpClient}
}

func getBearerToken(pHttpClient *http.Client, microsoftTokenEndpoint string) (string, error) {

	v := url.Values{}
	v.Set("grant_type", "client_credentials")
	v.Set("client_id", "******************************")
	v.Set("client_secret", "**************************")

	v.Set("resource", "https://management.core.windows.net")

	params := strings.NewReader(v.Encode())

	req, err := http.NewRequest(http.MethodPost, microsoftTokenEndpoint, params)

	if err != nil {
		log.Error("无法创建新请求")
		log.Error("遇到错误:", err.Error())
		return "", err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Host", "login.microsoftonline.com")

	resp, err := pHttpClient.Do(req)

	if err != nil {
		log.Error("无法从Microsoft令牌端点获取令牌", microsoftTokenEndpoint)
		log.Error("遇到错误:", err.Error())
		return "", err
	}
	
	/* 更多未显示的代码 */
}

func getCostFromAzureCostManagement(pHttpClient *http.Client, bearerToken string, resourcegroup string) (*[]models.CostDetailsContext, error) {

	log.Info("从Azure获取成本")

	url := "https://management.azure.com/subscriptions/6a877c8f-4ca3-4135-9a0c-8e92db2384b4/resourceGroups/" + resourcegroup + "/providers/Microsoft.CostManagement/query?api-version=2021-10-01"
	postBody := `{
			    "type": "Usage",
			    "timeframe": "MonthToDate",
			    "dataset": {
			      "granularity": "Daily",
			      "aggregation": {
			       "totalCost": {
			          "name": "PreTaxCost",
			          "function": "Sum"
			        }
			      },
			     "grouping": [
			        {
			         "type": "Dimension",
			          "name": "resourceGroup"
			        }
			      ]
			    }
			  }`

	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postBody))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", bearerToken)

	resp, err := pHttpClient.Do(req)
	
	/* 更多未显示的代码 */
}

func (pPrice *ApiLoadTestPricing) CalculateCost(rGrp string) (*[]models.CostDetailsContext, error) {

	log.Info("计算Azure中的成本,资源组为:", rGrp)

	token, err := getBearerToken(pPrice.mpHttpClient, "https://login.microsoftonline.com/7a9376d4-7c43-480f-82ba-a090647f651d/oauth2/token")

	if err != nil {
		log.Error("由于没有正确的令牌,成本计算失败")
		return nil, err
	}

	pSliceCostDetailsContext, err := getCostFromAzureCostManagement(pPrice.mpHttpClient, "Bearer "+token, rGrp)

	return pSliceCostDetailsContext, err
}

问题陈述:

我用golang编写了一个小型API,它有两个功能:
1. 从Azure Active Directory获取Bearer令牌
2. 调用Microsoft成本管理API获取资源组成本

问题:
情况1:
当调用getBearerToken和getCostFromAzureCostManagement时,有时将响应写入响应写入器(response writer),有时不会到达客户端。

情况2:
当只调用getBearerToken并返回令牌字符串时,每次写入都会到达客户端。

注意:

  1. getBearerToken和getCostFromAzureCostManagement都使用相同的http客户端进行请求
  2. 使用gorilla mux进行路由

我做了什么?

  1. 尝试注释掉CalculateCost代码-响应写入器正常工作。
  2. 尝试注释掉getCostFromAzureCostManagement-响应写入器正常工作。
  3. 尝试同时取消注释它们-响应写入器不起作用。
  4. 阅读了关于共享http客户端可能存在的问题,并尝试了以下代码
    https://github.com/hashicorp/go-cleanhttp-响应写入器不起作用。

我被困在这个问题上,已经没有了想法,有人可以帮我调试吗?

英文:

File: main.go

import (
  "github.com/gorilla/mux"
  )
  
func setupRouting(pBus *bus.Bus) *mux.Router {

	log.Debug("Setting up routing")

	r := mux.NewRouter()

	pInvoiceHandler := handlers.NewInvoiceHandler(pBus)

	postRouter.HandleFunc("/invoice", pInvoiceHandler.HandleInvoiceGenerationRequest)


	return r
}

main() {
	pRouter := setupRouting(pBus)
	
	s := &http.Server{
		Addr:         address,
		Handler:      pRouter,
		IdleTimeout:  time.Duration(idlTimeout) * time.Second,
		ReadTimeout:  time.Duration(rdTimeout) * time.Second,
		WriteTimeout: time.Duration(wrtTimeout) * time.Second,
	}
}

File: InvoiceGenerationHandler.go

func (ih *InvoiceHandler) HandleInvoiceGenerationRequest(rw http.ResponseWriter, pReq *http.Request) {

	log.Info("Handling invoice generation request")

	query := pReq.URL.Query()
	resourceGrp := query.Get("rGrp")

	if resourceGrp == "" {
		http.Error(rw, "Invalid URL, rGrp parameter should be present in the URL ", http.StatusBadRequest)
		return
	}

	pSliceCostDetailsCtx, err := ih.pApiLoadTesting.CalculateCost(resourceGrp)

	if err != nil {
		log.Error("Could not calculate cost :", err.Error())
		http.Error(rw, err.Error(), http.StatusInternalServerError)
		return
	}

	pResponseJson, err := json.Marshal(pSliceCostDetailsCtx)

	if err != nil {
		log.Error("Could not convert price context slice to json: ", err.Error())
		http.Error(rw, "Could not generate cost response", http.StatusInternalServerError)
		log.Error(pResponseJson)
		return
	}

	rw.Write(pResponseJson)
}

File: apiloadtestpricing.go

type ApiLoadTestPricing struct {
	mpHttpClient *http.Client
}

func NewApiLoadTesting() *ApiLoadTestPricing {
	var pHttpClient = &http.Client{
		Timeout: time.Second * 120,
	}
	return &ApiLoadTestPricing{pHttpClient}
}

func getBearerToken(pHttpClient *http.Client, microsoftTokenEndpoint string) (string, error) {

	v := url.Values{}
	v.Set("grant_type", "client_credentials")
	v.Set("client_id", "******************************")
	v.Set("client_secret", "**************************")

	v.Set("resource", "https://management.core.windows.net")

	params := strings.NewReader(v.Encode())

	req, err := http.NewRequest(http.MethodPost, microsoftTokenEndpoint, params)

	if err != nil {
		log.Error("Could not create a new request")
		log.Error("Encountered error: ", err.Error())
		return "", err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Host", "login.microsoftonline.com")

	resp, err := pHttpClient.Do(req)

	if err != nil {
		log.Error("Could not get token from microsoft token endpoint ", microsoftTokenEndpoint)
		log.Error("Encountered error: ", err.Error())
		return "", err
	}
	
	/* More Code here not shown */
}

func getCostFromAzureCostManagement(pHttpClient *http.Client, bearerToken string, resourcegroup string) (*[]models.CostDetailsContext, error) {

	log.Info("Getting Cost from Azure")

	url := "https://management.azure.com/subscriptions/6a877c8f-4ca3-4135-9a0c-8e92db2384b4/resourceGroups/" + resourcegroup + "/providers/Microsoft.CostManagement/query?api-version=2021-10-01"
	postBody := `{
			    "type": "Usage",
			    "timeframe": "MonthToDate",
			    "dataset": {
			      "granularity": "Daily",
			      "aggregation": {
			       "totalCost": {
			          "name": "PreTaxCost",
			          "function": "Sum"
			        }
			      },
			     "grouping": [
			        {
			         "type": "Dimension",
			          "name": "resourceGroup"
			        }
			      ]
			    }
			  }`

	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postBody))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", bearerToken)

	resp, err := pHttpClient.Do(req)
	
	/* More Code here not shown */
}

func (pPrice *ApiLoadTestPricing) CalculateCost(rGrp string) (*[]models.CostDetailsContext, error) {

	log.Info("Calculating cost from azure for resource group: ", rGrp)

	token, err := getBearerToken(pPrice.mpHttpClient, "https://login.microsoftonline.com/7a9376d4-7c43-480f-82ba-a090647f651d/oauth2/token")

	if err != nil {
		log.Error("Cost calculation has failed because proper bearer token was not present")
		return nil, err
	}

	pSliceCostDetailsContext, err := getCostFromAzureCostManagement(pPrice.mpHttpClient, "Bearer "+token, rGrp)

	return pSliceCostDetailsContext, err
}

Problem Statement:

I have written a small API in golang which does two things:
1. Get Bearer token from azure active directory
2. Call the Microsoft cost management API to get the resource group cost

Issue:
Case 1:
When getBearerToken and getCostFromAzureCostManagement get called. The response written to the response writer in InvoiceGenerationHandler.go sometimes reach and sometimes doesn't to the client.

Case 2:
When only getBearerToken is called and the token string is returned it reaches the client on every write.

Note:

  1. Both getBearerToken and getCostFromAzureCostManagement use the same http client for the
    request
  2. gorilla mux is used for routing

What have I done ?

  1. Tried commenting out the CalculateCost code - response writer works.
  2. Tried commenting out getCostFromAzureCostManagement - responsewriter works
  3. Tried with both of them uncommented - reponsewriter doesnt work
  4. Read about the possible issue of shared httpclient and tried the follwing code
    https://github.com/hashicorp/go-cleanhttp - Responsewriter doesnt work.

I am stuck with this and have run out of ideas can someone please help me debug this ?

答案1

得分: 0

尝试增加超时时间。

IdleTimeout: 60 * time.Second,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
英文:

Try increasing the timeouts.

IdleTimeout:  60 * time.Second,
ReadTimeout:  60* time.Second,
WriteTimeout: 60* time.Second,

huangapple
  • 本文由 发表于 2022年7月27日 21:23:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/73138764.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定