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

news2025/1/16 21:39:49

今日待办

  1. 使用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/)
  2. 调研HyperScan golang implement(https://github.com/flier/gohs/)的使用和benchmark,扩充profile事件处理handler,添加一些正则处理任务(这个会给一些示例)

Watermill

what?

  • 高效处理消息流,事件驱动程序
  • 用于 event soucing, rpc over messages, sagas
  • pub/sub

why?

  • 微服务模式,异步通信
  • 减少复杂性

core?

  • Publisher
  • Subscriber
  • Message

官方示例

结合对 kafkapub/sub 的实现和对 routermiddleware 的使用尝试替换掉 baserunner

// Package watermillx
// @Author xzx 2023/8/11 18:53:00
package watermillx

import (
	"fmt"
	"github.com/Shopify/sarama"
	"github.com/ThreeDotsLabs/watermill"
	"github.com/ThreeDotsLabs/watermill-kafka/v2/pkg/kafka"
	"github.com/ThreeDotsLabs/watermill/message"
	"github.com/ThreeDotsLabs/watermill/message/router/middleware"
	"github.com/ThreeDotsLabs/watermill/message/router/plugin"
	"log"
	"profile/internal/config"
	"profile/internal/schema"
	"time"
)

// 测试字段
type WatermillContext struct {
	Status        int
	StageErr      error
	Event         schema.Event
	AppID         string // API 上报
	FetchScenario string // API 上报
}

var logger = watermill.NewStdLogger(false, false)

func (ctx *WatermillContext) Init() {

	saramaSubscriberConfig := kafka.DefaultSaramaSubscriberConfig()
	// equivalent of auto.offset.reset: earliest
	saramaSubscriberConfig.Consumer.Offsets.Initial = sarama.OffsetOldest

	subscriber, err := kafka.NewSubscriber(
		kafka.SubscriberConfig{
			Brokers:               []string{config.Profile.GetString("kafka.bootstrap")},
			Unmarshaler:           kafka.DefaultMarshaler{},
			OverwriteSaramaConfig: saramaSubscriberConfig,
			ConsumerGroup:         "test_consumer_group",
		},
		logger,
	)
	if err != nil {
		panic(err)
	}

	router, err := message.NewRouter(message.RouterConfig{}, logger)
	if err != nil {
		panic(err)
	}
	// SignalsHandler will gracefully shutdown Router when SIGTERM is received.
	// You can also close the router by just calling `r.Close()`.
	router.AddPlugin(plugin.SignalsHandler)
	// Router level middleware are executed for every message sent to the router
	router.AddMiddleware(
		// CorrelationID will copy the correlation id from the incoming message's metadata to the produced messages
		middleware.CorrelationID,
		// The handler function is retried if it returns an error.
		// After MaxRetries, the message is Nacked and it's up to the PubSub to resend it.
		middleware.Retry{
			MaxRetries:      3,
			InitialInterval: time.Millisecond * 100,
			Logger:          logger,
		}.Middleware,
		// Recoverer handles panics from handlers.
		// In this case, it passes them as errors to the Retry middleware.
		middleware.Recoverer,
	)
	// AddHandler returns a handler which can be used to add handler level middleware
	// or to stop handler.
	// just for debug, we are printing all messages received on `incoming_messages_topic`
	router.AddNoPublisherHandler(
		"print_incoming_messages",
		"to_analyzer__0.PERF_CRASH",
		subscriber,
		printMessages,
	)
    // Now that all handlers are registered, we're running the Router.
	// Run is blocking while the router is running.
	ctx := context.Background()
	if err := router.Run(ctx); err != nil {
		panic(err)
	}
}

func printMessages(msg *message.Message) error {
	fmt.Printf(
		"\n> Received message: %s\n> %s\n> metadata: %v\n\n",
		msg.UUID, string(msg.Payload), msg.Metadata,
	)
	return nil
}

image-20230811193329885

image-20230811193006417

思考:

ConsumerDispatchHandler 每次消费消息都会执行,注册完 4 个 stageHandler 后再执行Run去异步调用 BastContextHandler

考虑在此处进行订阅者的初始化、MiddleWareHandler 的注册

image-20230811195245920

// Package watermillx
// @Author xzx 2023/8/11 18:53:00
package watermillx

import (
	"context"
	"fmt"
	"github.com/Shopify/sarama"
	"github.com/ThreeDotsLabs/watermill"
	"github.com/ThreeDotsLabs/watermill-kafka/v2/pkg/kafka"
	"github.com/ThreeDotsLabs/watermill/message"
	"github.com/ThreeDotsLabs/watermill/message/router/middleware"
	"github.com/ThreeDotsLabs/watermill/message/router/plugin"
	"log"
	"profile/internal/config"
	"time"
)

var logger = watermill.NewStdLogger(true, false)

func Init() {
	saramaSubscriberConfig := kafka.DefaultSaramaSubscriberConfig()
	// equivalent of auto.offset.reset: earliest
	saramaSubscriberConfig.Consumer.Offsets.Initial = sarama.OffsetOldest

	subscriber, err := kafka.NewSubscriber(
		kafka.SubscriberConfig{
			Brokers:               []string{config.Profile.GetString("kafka.bootstrap")},
			Unmarshaler:           kafka.DefaultMarshaler{},
			OverwriteSaramaConfig: saramaSubscriberConfig,
			ConsumerGroup:         config.Profile.GetString("kafka.group"),
		},
		logger,
	)
	if err != nil {
		panic(err)
	}

	router, err := message.NewRouter(message.RouterConfig{}, logger)
	if err != nil {
		panic(err)
	}
	// SignalsHandler will gracefully shutdown Router when SIGTERM is received.
	// You can also close the router by just calling `r.Close()`.
	router.AddPlugin(plugin.SignalsHandler)
	// Router level middleware are executed for every message sent to the router
	router.AddMiddleware(
		// CorrelationID will copy the correlation id from the incoming message's metadata to the produced messages
		middleware.CorrelationID,
		// The handler function is retried if it returns an error.
		// After MaxRetries, the message is Nacked and it's up to the PubSub to resend it.
		middleware.Retry{
			MaxRetries:      3,
			InitialInterval: time.Millisecond * 100,
			Logger:          logger,
		}.Middleware,
		// Recoverer handles panics from handlers.
		// In this case, it passes them as errors to the Retry middleware.
		middleware.Recoverer,
	)
	// AddHandler returns a handler which can be used to add handler level middleware
	// or to stop handler.
	// just for debug, we are printing all messages received on `incoming_messages_topic`
	handler := router.AddNoPublisherHandler(
		"print_incoming_messages",
		"to_analyzer__0.PERF_CRASH",
		subscriber,
		printMessages,
	)

	// Handler level middleware is only executed for a specific handler
	// Such middleware can be added the same way the router level ones
	handler.AddMiddleware(func(h message.HandlerFunc) message.HandlerFunc {
		return func(message *message.Message) ([]*message.Message, error) {
			log.Println("executing handler specific middleware for ", message.UUID)

			return h(message)
		}
	})
	// Now that all handlers are registered, we're running the Router.
	// Run is blocking while the router is running.
	ctx := context.Background()
	if err := router.Run(ctx); err != nil {
		panic(err)
	}
}

func printMessages(msg *message.Message) error {
	fmt.Printf(
		"\n> Received message: %s\n> %s\n> metadata: %v\n\n",
		msg.UUID, string(msg.Payload), msg.Metadata,
	)
	return nil
}

可以正常消费到消息,接着就可以自定义 Handler 来对接到 Baserunner 的功能

image-20230811200725567

测试如下:

// Package watermillx
// @Author xzx 2023/8/11 18:53:00
package watermillx

import (
	"context"
	"encoding/json"
	"github.com/Shopify/sarama"
	"github.com/ThreeDotsLabs/watermill"
	"github.com/ThreeDotsLabs/watermill-kafka/v2/pkg/kafka"
	"github.com/ThreeDotsLabs/watermill/message"
	"github.com/ThreeDotsLabs/watermill/message/router/middleware"
	"github.com/ThreeDotsLabs/watermill/message/router/plugin"
	kafkago "github.com/segmentio/kafka-go"
	"go.uber.org/zap"
	"profile/internal/config"
	"profile/internal/connector"
	"profile/internal/log"
	"profile/internal/schema"
	"profile/internal/schema/performance"
	"profile/internal/state"
	"time"
)

// ProfileContext
// @Description:
// @Author xzx 2023-08-11 22:21:41
type ProfileContext struct {
	// Properties that can be called by inherited subclasses
	Status int
	Ctx    context.Context

	Event         schema.Event
	AppID         string // API 上报
	FetchScenario string // API 上报
}

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

// Init
// @Description 初始化
// @Author xzx 2023-08-11 22:22:01
func (profileCtx *ProfileContext) Init() {
	logger := watermill.NewStdLogger(true, false)
	saramaSubscriberConfig := kafka.DefaultSaramaSubscriberConfig()
	// equivalent of auto.offset.reset: earliest
	saramaSubscriberConfig.Consumer.Offsets.Initial = sarama.OffsetOldest
	subscriber, err := kafka.NewSubscriber(
		kafka.SubscriberConfig{
			Brokers:               []string{config.Profile.GetString("kafka.bootstrap")},
			Unmarshaler:           kafka.DefaultMarshaler{},
			OverwriteSaramaConfig: saramaSubscriberConfig,
			ConsumerGroup:         config.Profile.GetString("kafka.group"),
		},
		logger,
	)
	if err != nil {
		log.Logger.Error("creates a new Kafka Subscriber error", zap.Error(err))
		panic(err)
	}
	router, err := message.NewRouter(message.RouterConfig{}, logger)
	if err != nil {
		log.Logger.Error("creates a new Router with given configuration error", zap.Error(err))
		panic(err)
	}
	router.AddPlugin(plugin.SignalsHandler)
	router.AddMiddleware(
		middleware.CorrelationID,
		middleware.Retry{
			MaxRetries:      3,
			InitialInterval: time.Millisecond * 100,
			Logger:          logger,
		}.Middleware,
		middleware.Recoverer,
	)

	router.AddNoPublisherHandler(
		"print_incoming_messages",
		"to_analyzer__0.PERF_CRASH",
		subscriber,
		profileCtx.UnpackKafkaMessage,
	)
	/*router.AddNoPublisherHandler(
		"print_incoming_messages",
		"to_analyzer__0.PERF_CRASH",
		subscriber,
		profileCtx.InitPerformanceEvent,
	)
	router.AddNoPublisherHandler(
		"print_incoming_messages",
		"to_analyzer__0.PERF_CRASH",
		subscriber,
		profileCtx.AnalyzeEvent,
	)
	router.AddNoPublisherHandler(
		"print_incoming_messages",
		"to_analyzer__0.PERF_CRASH",
		subscriber,
		profileCtx.WriteKafka,
	)*/

	if err = router.Run(context.Background()); err != nil {
		log.Logger.Error("runs all plugins and handlers and starts subscribing to provided topics error", zap.Error(err))
		panic(err)
	}
}

// UnpackKafkaMessage
// @Description
// @Author xzx 2023-08-11 22:29:21
// @Param msg
// @Return contextErr
func (profileCtx *ProfileContext) UnpackKafkaMessage(msg *message.Message) (contextErr error) {
	// 反序列化,存入通用结构体
	if contextErr = json.Unmarshal(msg.Payload, &profileCtx.Event); contextErr != nil {
		profileCtx.Status = state.StatusUnmarshalError
		return
	}

	log.Logger.Info("[UnpackKafkaItem] unpack kafka item success", zap.Any("event", profileCtx.Event))
	return
}

// InitPerformanceEvent
// @Description
// @Author xzx 2023-08-11 22:30:36
// @Param msg
// @Return contextErr
func (profileCtx *ProfileContext) InitPerformanceEvent(msg *message.Message) (contextErr error) {
	event, contextErr := performance.EventFactory(profileCtx.Event.Category, profileCtx.Event.Dimensions, profileCtx.Event.Values)
	if contextErr != nil {
		profileCtx.Status = state.StatusEventFactoryError
		return
	}
	log.Logger.Info("[InitPerformanceEvent] init performance event success", zap.Any("event", event))
	profileCtx.Event.ProfileData = event
	return
}

// AnalyzeEvent
// @Description
// @Author xzx 2023-08-11 22:30:44
// @Param msg
// @Return contextErr
func (profileCtx *ProfileContext) AnalyzeEvent(msg *message.Message) (contextErr error) {
	contextErr = profileCtx.Event.ProfileData.Analyze()
	if contextErr != nil {
		profileCtx.Status = state.StatusAnalyzeError
		return
	}
	// clear dimensions and values
	profileCtx.Event.Dimensions = nil
	profileCtx.Event.Values = nil
	return
}

// WriteKafka
// @Description
// @Author xzx 2023-08-11 22:30:47
// @Param msg
// @Return contextErr
func (profileCtx *ProfileContext) WriteKafka(msg *message.Message) (contextErr error) {
	toWriteBytes, contextErr := json.Marshal(profileCtx.Event)
	if contextErr != nil {
		profileCtx.Status = state.StatusUnmarshalError
		return
	}
	topic := connector.GetTopic(profileCtx.Event.Category)
	contextErr = connector.GetProducer().WriteMessages(profileCtx.Ctx, kafkago.Message{
		Topic: topic,
		Key:   []byte(profileCtx.Event.ID),
		Value: toWriteBytes,
	})
	if contextErr != nil {
		profileCtx.Status = state.StatusWriteKafkaError
		return
	}
	log.Logger.Info("[WriteKafka] write kafka success", zap.String("topic", topic), zap.String("id", profileCtx.Event.ID), zap.String("msg", string(toWriteBytes)))
	return
}

可以正常执行 Handler 的逻辑

image-20230811224842411

明日待办

  • 为一个主题添加多个 Handler

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1016224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

马斯洛的动机与人格、需求层次理论

马斯洛是在研究动机(Motivation)时,才提出需求层次作为理论基础来支持动机理论的。所谓动机,就是人类的行为到底是由什么驱动,其实是对人类行为的当下原动力,区别于过去、未来或者是有可能起作用的动力。 …

Android开源 日志框架 LogDog V2.3.1

目录 一、简介 二、下载使用 添加jitpack 仓库 添加依赖: 三、更改 1、 LogDogV2.3.1初始化: 2、通过上面的初始化 ,已经知道IJsonEngine 优化了泛型参数,采用 Object/Any 3、优化空异常的判断,哪怕打印变量是NULL LogDog会打印“nul…

Spring Boot 自动注入失败的原因

问题 Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type com.sveinn.chatbotdomain.zsxq.service.ZsxqApi available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {ja…

elasticsearch6-RestClient操作文档

个人名片: 博主:酒徒ᝰ. 个人简介:沉醉在酒中,借着一股酒劲,去拼搏一个未来。 本篇励志:三人行,必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》,SpringCloud…

一,安卓aosp源码编译环境搭建

系列文章目录 第一章 安卓aosp源码编译环境搭建 第二章 手机硬件参数介绍和校验算法 第三章 修改安卓aosp代码更改硬件参数 第四章 编译定制rom并刷机实现硬改(一) 第五章 编译定制rom并刷机实现硬改(二) 第六章 不root不magisk不xposed lsposed frida原生修改定位 第七章 安卓…

【Unity基础】4.动画Animation

【Unity基础】4.动画Animation 大家好,我是Lampard~~ 欢迎来到Unity基础系列博客,所学知识来自B站阿发老师~感谢 (一)Unity动画编辑器 (1)Animation组件 这一张我们要学习如何在unity编辑器中&…

神经网络常用模型与应用

上手AI的一个捷径就是了解和使用各种网络模型,结合实际场景去打造自己的应用。神经网络模型是人类的共同财富。 神经网络 神经网络可以分为三种主要类型:前馈神经网络、反馈神经网络和图神经网络。 前馈神经⽹络(feedforward neural netwo…

mybatis mapper.xml转建表语句

从网上下载了代码&#xff0c;但是发现没有DDL建表语句&#xff0c;只能自己手动创建了&#xff0c;感觉太麻烦&#xff0c;就写了一个工具类 将所有的mapper.xml放入到一个文件夹中&#xff0c;程序会自动读取生成建表语句 依赖的jar <dependency><groupId>org.d…

非常详细的git-flow分支管理流程配置及使用

非常详细的git-flow分支管理流程配置及使用。 git-flow有两个涵义,一个是指软件开发领域的版本管理流程Gitflow。另一个是指git命令工具git flow。 目前业界主流的版本管理流程是Gitflow 和 trunk-based。 Gitflow流行的比较早。但是目前的流行度要低于 trunk-based模式工作…

Linux系统:OpenSSH7.4p升级到9.0p(服务器漏洞)

清华大学开源软件镜像站下载地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/pub/OpenBSD/OpenSSH/portable/openssh-9.0p1.tar.gz 一、升级 0、安装Telnet &#xff08;1&#xff09;为防止安装失败&#xff0c;无法用ssh做远程连接&#xff0c;因此先安装telnet yum…

elasticsearch3-索引库的操作

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

视屏点播项目

项目背景 大家应该在电脑上刷过视频吧,这个项目就是模拟一下我们刷视频的整个流程,我们要做的是一个类似B站的网页,这里面包含视频的上传修改和观看以及删除,注意我这个是一个简易版本的,在后面我会做一个升级,增加其他的功能. 基本原理 下面我们说一下我们项目的基本原理.我…

RabbitMQ快速实战以及集群架构详解

一、 MQ 介绍 1 、什么是 MQ &#xff1f;为什么要用 MQ &#xff1f; ChatGPT 中对于消息队列的介绍是这样的&#xff1a; MQ &#xff1a; MessageQueue &#xff0c;消息队列。这东西分两个部分来理解&#xff1a;队列&#xff0c;是一种 FIFO 先进先出的数据结构。 消…

社区团购商城小程序v18.1开源独立版+前端

新增后台清理缓存功能 修复定位权限 修复无法删除手机端管理员 11月新登录接口修复&#xff01; 修复商家付款到零钱&#xff0c; 修复会员登陆不显示头像&#xff0c; 修复无法修改会员开添加绑定

elasticsearch10-查询文档处理

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…

C++核心编程之类和对象---C++面向对象的三大特性--封装

目录 类和对象 类和对象的概念 C面向对象的三大特性 一、封装 封装案例1&#xff1a;设计一个学生类&#xff0c;可以给姓名和学号赋值&#xff0c;可以显示学生的姓名和学号。 二、访问权限 访问权限有三种 struct和class的区别 三、成员属性私有化 成员属性私有化的…

第十三章总结

一.泛型 1.定义泛型类 泛型机制语法&#xff1a; 类名<T> 其中&#xff0c;T是泛型的名称&#xff0c;代表某一种类型。 【例13.6】创建带泛型的图书类 代码&#xff1a; 结果&#xff1a; 2.泛型的常规用法 (1)定义泛型类时声明多个变量 class MyCla…

【MySQL集群一】CentOS 7上搭建MySQL集群:一主一从、多主多从

CentOS 7上搭建MySQL集群 介绍一主一从步骤1&#xff1a;准备工作步骤2&#xff1a;安装MySQL步骤3&#xff1a;配置主服务器步骤4&#xff1a;创建复制用户步骤5&#xff1a;备份主服务器数据&#xff0c;如果没有数据则省略这一步步骤6&#xff1a;配置从服务器步骤7&#xf…

Javase | 集合-上

目录&#xff1a; 一、集合&#xff1a;1.集合的概述2.集合的分类 二、“单个方式”存储元素&#xff1a;1.Collection1.1 Collection的概述1.2 Collection接口中常用的方法Iterator<T> iterator( ) 1.3 Collection下的子接口 2.Iterable&#xff1a;2.1 Iterable的概述2…

国内AI语言大模型【星火】各类模块及部分功能使用方法介绍

一、前言 现在AI语言大模型是百花齐放,挺好!有竞争,有发展,才能推出更好的产品。现在,科大讯飞就推出了大语言模型——星火!能够学习和理解人类的语言,进行多轮对话,回答问题,高效便捷地帮助人们获取信息、知识和灵感。星火在对话栏设置了三个插件:文档回答、PPT生成…