腾讯mini项目-【指标监控服务重构】2023-08-13

今日已办

使用watermill框架替代当前的base_runner框架

a. 参考官方提供的sarama kafka Pub/Sub(https://github.com/ThreeDotsLabs/watermill-kafka/)实现kafka-go(https://github.com/segmentio/kafka-go)的Pub/Sub(sarama需要cgo,会导致一些额外的镜像依赖)

b. 参考https://watermill.io/docs/messages-router/实现不同topic(不同事件)走不同的Handler处理逻辑,相同处理逻辑则可以使用MiddleWare(https://watermill.io/docs/middlewares/)

watermill-kafka

  1. 构造 publisher, subscriber qiulin/watermill-kafkago: Kafka Pub/Sub for the Watermill project, based on segmentio/kafka-go (github.com)
  2. 定义 router
  3. 获取符合正则表达式的主题
  4. 为每一个主题添加 middleware、handler
go 复制代码
// Package consumer
// @Author xzx 2023/8/11 18:53:00
package consumer

import (
	"context"
	kc "github.com/Kevinello/kafka-client"
	"github.com/ThreeDotsLabs/watermill"
	"github.com/ThreeDotsLabs/watermill/message"
	"github.com/ThreeDotsLabs/watermill/message/router/middleware"
	"github.com/ThreeDotsLabs/watermill/message/router/plugin"
	"github.com/qiulin/watermill-kafkago/pkg/kafkago"
	"go.uber.org/zap"
	"profile/internal/config"
	"profile/internal/connector"
	"profile/internal/log"
	"profile/internal/schema"
	"profile/internal/schema/performance"
	"strings"
	"time"
)

// ProfileContext
// @Description:
// @Author xzx 2023-08-11 22:21:41
type ProfileContext struct {
	Status int
	Ctx    context.Context

	Router *message.Router
	Event  schema.Event

	AppID         string // API 上报
	FetchScenario string // API 上报
}

// NewProfileContext
// @Description
// @Author xzx 2023-08-11 22:49:00
// @Return *ProfileContext
func NewProfileContext() *ProfileContext {
	profileCtx := &ProfileContext{
		Ctx: context.Background(),
	}
	profileCtx.init()
	return profileCtx
}

// init
// @Description 初始化
// @Author xzx 2023-08-11 22:22:01
func (profileCtx *ProfileContext) init() {
	logger := watermill.NewStdLogger(false, false)
	publisher, subscriber := newPubSub()
	router, err := message.NewRouter(message.RouterConfig{}, logger)
	if err != nil {
		log.Logger.Fatal("creates a new Router with given configuration error", zap.Error(err))
	}

	router.AddPlugin(plugin.SignalsHandler)
	router.AddMiddleware(
		middleware.Retry{
			MaxRetries:      3,
			InitialInterval: time.Millisecond * 100,
			Logger:          logger,
		}.Middleware,
		middleware.Recoverer,
	)

	getTopics := kc.GetTopicReMatch(strings.Split(config.Profile.GetString("kafka.topicRE"), ","))
	topics, err := getTopics(config.Profile.GetString("kafka.bootstrap"))
	if err != nil {
		log.Logger.Fatal("get topics failed", zap.Error(err))
		return
	}

	for _, topic := range topics {
		var category string
		if strings.Contains(topic, performance.CategoryCrash) {
			category = performance.CategoryCrash
		} else if strings.Contains(topic, performance.CategoryLag) {
			category = performance.CategoryLag
		} else {
			continue
		}
		router.AddHandler("profile-consume", topic, subscriber, connector.GetTopic(category), publisher, profileCtx.WriteKafka).
			AddMiddleware(
				profileCtx.UnpackKafkaMessage,
				profileCtx.InitPerformanceEvent,
				profileCtx.AnalyzeEvent,
			)
	}
	profileCtx.Router = router
}

// Run
// @Description
// @Author xzx 2023-08-12 13:52:53
func (profileCtx *ProfileContext) Run() {
	// router.Run contains defer cancel()
	if err := profileCtx.Router.Run(profileCtx.Ctx); err != nil {
		log.Logger.Error("runs all plugins and handlers and starts subscribing to provided topics error", zap.Error(err))
	}
}

func (profileCtx *ProfileContext) Done() <-chan struct{} {
	return profileCtx.Ctx.Done()
}

func (profileCtx *ProfileContext) Err() error {
	return profileCtx.Ctx.Err()
}

func (profileCtx *ProfileContext) Deadline() (deadline time.Time, ok bool) {
	return profileCtx.Ctx.Deadline()
}

func (profileCtx *ProfileContext) Value(key any) any {
	return profileCtx.Ctx.Value(key)
}

// newPubSub
// @Description
// @Author xzx 2023-08-13 16:00:26
// @Return message.Publisher
// @Return message.Subscriber
func newPubSub() (message.Publisher, message.Subscriber) {
	logger := watermill.NewStdLogger(false, false)
	marshaler := kafkago.DefaultMarshaler{}
	publisher := kafkago.NewPublisher(kafkago.PublisherConfig{
		Brokers:     []string{config.Profile.GetString("kafka.bootstrap")},
		Async:       false,
		Marshaler:   marshaler,
		OTELEnabled: false,
		Ipv4Only:    true,
		Timeout:     100 * time.Second,
	}, logger)

	subscriber, err := kafkago.NewSubscriber(kafkago.SubscriberConfig{
		Brokers:       []string{config.Profile.GetString("kafka.bootstrap")},
		Unmarshaler:   marshaler,
		ConsumerGroup: config.Profile.GetString("kafka.group"),
		OTELEnabled:   false,
	}, logger)
	if err != nil {
		log.Logger.Fatal("Unable to create subscriber", zap.Error(err))
	}
	return publisher, subscriber
}

组内会议

已完成:

  1. 找到了基于 kafka-go 的 watermill 的 pub/sub 模型:https://github.com/qiulin/watermill-kafkago
  2. 将 baserunner 替换为 watermill 的 pub/sub 中可以大致还原原本的功能
  3. grafana 展示 trace 的SQL比较复杂,因为表结构的定义有出入,需要进行重命名,时间范围也需要重新修改SQL,而且组员反馈 grafana 的可视化并没有signoz 来得直观友好

待办:

  1. 调研 HyperScan golang implement 和 benchmark
  2. 优化 watermill-pub/sub 的逻辑

问题:

  1. watermill-pub/sub : 目前的做法有将4个阶段整合为一个 Handler 来处理,还有将其抽离为Middleware 的做法,原有 baserunner 的逻辑是所有 topic 的都走 4 个阶段的 Handler
  2. 是否可以停止 grafana 展示 trace 的工作

明日待办

相关推荐
Code季风1 小时前
gRPC与Protobuf集成详解—从服务定义到跨语言通信(含Go和Java示例)
go·grpc·protobuf
Code季风1 小时前
Protobuf 高级特性详解
go·protobuf
_代号0071 小时前
Go 编译报错排查:vendor/golang.org/x/crypto/cryptobyte/asn1 no Go source files
后端·go
岁忧9 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
Nejosi_念旧9 小时前
解读 Go 中的 constraints包
后端·golang·go
zskj_zhyl10 小时前
毫米波雷达守护银发安全:七彩喜跌倒检测仪重构居家养老防线
人工智能·安全·重构
漫步向前16 小时前
gin问题知识点汇总
go
mao毛16 小时前
go Mutex 深入理解
go·源码阅读
zzywxc78718 小时前
AI技术通过提示词工程(Prompt Engineering)正在深度重塑职场生态和行业格局,这种变革不仅体现在效率提升,更在重构人机协作模式。
java·大数据·开发语言·人工智能·spring·重构·prompt
钩子波比19 小时前
🚀 Asynq 学习文档
redis·消息队列·go