首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >响应后异步工作

响应后异步工作
EN

Stack Overflow用户
提问于 2018-05-07 09:22:45
回答 1查看 1.5K关注 0票数 2

我正在尝试实现http服务器:

  • 使用某种逻辑进一步计算重定向
  • 重定向用户
  • 日志用户数据

目标是实现最大吞吐量(至少15k rps)。为了做到这一点,我想异步保存日志。我使用kafka作为日志记录系统,并将代码块分成单独的goroutine。目前实施的总体实例:

代码语言:javascript
复制
package main

import (
    "github.com/confluentinc/confluent-kafka-go/kafka"
    "net/http"
    "time"
    "encoding/json"
)

type log struct {
    RuntimeParam  string `json:"runtime_param"`
    AsyncParam    string `json:"async_param"`
    RemoteAddress string `json:"remote_address"`
}

var (
    producer, _ = kafka.NewProducer(&kafka.ConfigMap{
        "bootstrap.servers": "localhost:9092,localhost:9093",
        "queue.buffering.max.ms": 1 * 1000,
        "go.delivery.reports": false,
        "client.id": 1,
    })
    topicName = "log"
)

func main() {
    siteMux := http.NewServeMux()
    siteMux.HandleFunc("/", httpHandler)
    srv := &http.Server{
        Addr: ":8080",
        Handler: siteMux,
        ReadTimeout:  2 * time.Second,
        WriteTimeout: 5 * time.Second,
        IdleTimeout:  10 * time.Second,
    }
    if err := srv.ListenAndServe(); err != nil {
        panic(err)
    }
}

func httpHandler(w http.ResponseWriter, r *http.Request) {
    handlerLog := new(log)
    handlerLog.RuntimeParam = "runtimeDataString"
    http.Redirect(w, r, "http://google.com", 301)
    go func(goroutineLog *log, request *http.Request) {
        goroutineLog.AsyncParam = "asyncDataString"
        goroutineLog.RemoteAddress = r.RemoteAddr
        jsonLog, err := json.Marshal(goroutineLog)
        if err == nil {
            producer.ProduceChannel() <- &kafka.Message{
                TopicPartition: kafka.TopicPartition{Topic: &topicName, Partition: kafka.PartitionAny},
                Value:          jsonLog,
            }
        }
    }(handlerLog, r)
}

问题如下:

  1. 使用单独的goroutine实现异步日志记录是正确/有效的,还是应该使用不同的方法?(例如工人和渠道)
  2. 也许有一种方法可以进一步提高服务器的性能,我错过了吗?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-07 14:03:23

  1. 是的,这是正确而有效地使用了goroutine (正如Flimzy在评论中所指出的)。我完全同意,这是一个很好的方法。

问题是,处理程序可能会在goroutine开始处理所有内容之前完成执行,并且请求(即指针)可能会消失,或者您可能会在中间件堆栈中进行一些竞争。我读到了你的评论,这不是你的情况,但总的来说,你不应该把请求传递给戈鲁蒂。正如我从您的代码中看到的那样,您实际上只使用了来自请求的RemoteAddr,为什么不直接重定向并将日志记录放在defer语句中呢?所以,我会重写一下你的处理程序:

代码语言:javascript
复制
func httpHandler(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "http://google.com", 301)
    defer func(runtimeDataString, RemoteAddr string) {
            handlerLog := new(log)
            handlerLog.RuntimeParam = runtimeDataString
            handlerLog.AsyncParam = "asyncDataString"
            handlerLog.RemoteAddress = RemoteAddr
            jsonLog, err := json.Marshal(handlerLog)
            if err == nil {
                producer.ProduceChannel() <- &kafka.Message{
                    TopicPartition: kafka.TopicPartition{Topic: &topicName, Partition: kafka.PartitionAny},
                    Value:          jsonLog,
                }
            }
        }("runtimeDataString", r.RemoteAddr)
}  
  1. goroutines不太可能提高服务器的性能,因为您只是更早地发送响应,而这些kafka连接可能会在后台堆积起来,从而减缓整个服务器的运行速度。如果您发现这是瓶颈,您可以考虑在本地保存日志,并在服务器外的另一个进程(或工人池)中将它们发送到kafka。这可能会随着时间的推移而分散工作负载(比如当您有更多请求时发送更少的日志,反之亦然)。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50210857

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档