微服务实践之分布式定时任务

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 微服务实践之分布式定时任务

承接上篇:上篇文章讲到改造 go-zero 生成的 app module 中的 gateway & RPC 。本篇讲讲如何接入 异步任务 以及 log的使用

Delay Job

日常任务开放中,我们会有很多异步、批量、定时、延迟任务要处理,go-zero中有 go-queue,推荐使用 go-queue 去处理,go-queue 本身也是基于 go-zero 开发的,其本身是有两种模式:

  • dq: 依赖于beanstalkd ,分布式,可存储,延迟、定时设置,关机重启可以重新执行,消息不会丢,使用非常简单,go-queue中使用了redis setnx保证了每个消息只被消费一次,使用场景主要是用来做日常任务使用
  • kq:依赖于 kafka ,这个就不多介绍啦,大名鼎鼎的 kafka ,使用场景主要是做日志用

我们主要说一下dq,kq使用也一样的,只是依赖底层不同,如果没使用过beanstalkd,没接触过beanstalkd的可以先google一下,使用起来还是挺容易的。

我在jobs下使用goctl新建了一个message-job.api服务

info(
 title: //消息任务
 desc: // 消息任务
 author: "Mikael"
 email: "13247629622@163.com"
)
type BatchSendMessageReq {}
type BatchSendMessageResp {}
service message-job-api {
 @handler batchSendMessageHandler // 批量发送短信
 post batchSendMessage(BatchSendMessageReq) returns(BatchSendMessageResp)
}

因为不需要使用路由,所以handler下的routes.go被我删除了,在handler下新建了一个jobRun.go,内容如下:

package handler
import (
 "fishtwo/lib/xgo"
 "fishtwo/app/jobs/message/internal/svc"
)
/**
* @Description 启动job
* @Author Mikael
* @Date 2021/1/18 12:05
* @Version 1.0
**/
func JobRun(serverCtx *svc.ServiceContext)  {
 xgo.Go(func() {
  batchSendMessageHandler(serverCtx)
    //...many job
 })
}

其实xgo.Go就是 go batchSendMessageHandler(serverCtx) ,封装了一下go携程,防止野生goroutine panic

然后修改一下启动文件message-job.go

package main
import (
   "flag"
   "fmt"
   "fishtwo/app/jobs/message/internal/config"
   "fishtwo/app/jobs/message/internal/handler"
   "fishtwo/app/jobs/message/internal/svc"
   "github.com/tal-tech/go-zero/core/conf"
   "github.com/tal-tech/go-zero/rest"
)
var configFile = flag.String("f", "etc/message-job-api.yaml", "the config file")
func main() {
   flag.Parse()
   var c config.Config
   conf.MustLoad(*configFile, &c)
   ctx := svc.NewServiceContext(c)
   server := rest.MustNewServer(c.RestConf)
   defer server.Stop()
   handler.JobRun(ctx)
   fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
   server.Start()
}

主要是handler.RegisterHandlers(server, ctx) 修改为handler.JobRun(ctx)

接下来,我们就可以引入dq了,首先在etc/xxx.yaml下添加dqConf

.....
DqConf:
  Beanstalks:
    - Endpoint: 127.0.0.1:7771
      Tube: tube1
    - Endpoint: 127.0.0.1:7772
      Tube: tube2
  Redis:
    Host: 127.0.0.1:6379
    Type: node

我这里本地用不同端口,模拟开了2个节点,7771、7772

在internal/config/config.go添加配置解析对象

type Config struct {
 ....
 DqConf dq.DqConf
}

修改handler/batchsendmessagehandler.go

package handler
import (
 "context"
 "fishtwo/app/jobs/message/internal/logic"
 "fishtwo/app/jobs/message/internal/svc"
 "github.com/tal-tech/go-zero/core/logx"
)
func batchSendMessageHandler(ctx *svc.ServiceContext){
 rootCxt:= context.Background()
 l := logic.NewBatchSendMessageLogic(context.Background(), ctx)
 err := l.BatchSendMessage()
 if err != nil{
  logx.WithContext(rootCxt).Error("【JOB-ERR】 : %+v ",err)
 }
}

修改logic下batchsendmessagelogic.go,写我们的consumer消费逻辑

package logic
import (
   "context"
   "fishtwo/app/jobs/message/internal/svc"
   "fmt"
   "github.com/tal-tech/go-zero/core/logx"
)
type BatchSendMessageLogic struct {
   logx.Logger
   ctx    context.Context
   svcCtx *svc.ServiceContext
}
func NewBatchSendMessageLogic(ctx context.Context, svcCtx *svc.ServiceContext) BatchSendMessageLogic {
   return BatchSendMessageLogic{
    Logger: logx.WithContext(ctx),
    ctx:    ctx,
    svcCtx: svcCtx,
   }
}
func (l *BatchSendMessageLogic) BatchSendMessage() error {
   fmt.Println("job BatchSendMessage start")
   l.svcCtx.Consumer.Consume(func(body []byte) {
    fmt.Printf("job BatchSendMessage %s \n" + string(body))
   })
   fmt.Printf("job BatchSendMessage finish \n")
   return nil
}

这样就大功告成了,启动message-job.go就ok课

go run message-job.go

之后我们就可以在业务代码中向dq添加任务,它就可以自动消费了

producer.Delay 向dq中投递5个延迟任务:

producer := dq.NewProducer([]dq.Beanstalk{
  {
   Endpoint: "localhost:7771",
   Tube:     "tube1",
  },
  {
   Endpoint: "localhost:7772",
   Tube:     "tube2",
  },
 })
 for i := 1000; i < 1005; i++ {
  _, err := producer.Delay([]byte(strconv.Itoa(i)), time.Second * 1)
  if err != nil {
   fmt.Println(err)
  }
 }

producer.At 可以指定某个时间执行,非常好用,感兴趣的朋友自己可以研究下。

错误日志

在前面说到gateway改造时候,如果眼神好的童鞋,在上面的httpresult.go中已经看到了log的身影:

我们在来看下rpc中怎么处理的

是的,我在每个rpc启动的main中加入了grpc拦截器 https://www.yuque.com/tal-tech/go-zero/ttzlo1,那让我们看看grpc拦截器里面做了什么

然后我代码里面使用github/pkg/errors这个包去处理错误的,这个包还是很好用的

所以呢:

我们在 grpc 中打印日志 logx.WithContext(ctx).Errorf("[RPC-SRV-ERR] %+v",err)

api 中打印日志 logx.WithContext(r.Context()).Error("[GATEWAY-SRV-ERR] : %+v ",err)

go-zero 中打印日志,使用logx.WithContext会把trace-id带入,这样一个请求下来,比如

user-api --> user-srv --> message-srv

那如果 messsage-srv 出错,他们三个是同一个 trace-id ,是不是就可以在elk通过输入这个trace-id一次性搜索出来这条请求报错堆栈信息呢?当然你也可以接入 jaeger、zipkin、skywalking 等,这个我暂时还没接入。

框架地址

https://github.com/tal-tech/go-zero

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
13天前
|
API 持续交付 开发者
后端开发中的微服务架构实践与挑战
在数字化时代,后端服务的构建和管理变得日益复杂。本文将深入探讨微服务架构在后端开发中的应用,分析其在提高系统可扩展性、灵活性和可维护性方面的优势,同时讨论实施微服务时面临的挑战,如服务拆分、数据一致性和部署复杂性等。通过实际案例分析,本文旨在为开发者提供微服务架构的实用见解和解决策略。
|
14天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####
|
3天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
20 5
|
6天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
4天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####
|
6天前
|
Kubernetes Cloud Native Docker
云原生技术探索:容器化与微服务的实践之道
【10月更文挑战第36天】在云计算的浪潮中,云原生技术以其高效、灵活和可靠的特性成为企业数字化转型的重要推手。本文将深入探讨云原生的两大核心概念——容器化与微服务架构,并通过实际代码示例,揭示如何通过Docker和Kubernetes实现服务的快速部署和管理。我们将从基础概念入手,逐步引导读者理解并实践云原生技术,最终掌握如何构建和维护一个高效、可扩展的云原生应用。
|
7天前
|
监控 API 持续交付
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在后端开发中的应用,分析了其优势、面临的挑战以及最佳实践策略。不同于传统的单体应用,微服务通过细粒度的服务划分促进了系统的可维护性、可扩展性和敏捷性。文章首先概述了微服务的核心概念及其与传统架构的区别,随后详细阐述了构建微服务时需考虑的关键技术要素,如服务发现、API网关、容器化部署及持续集成/持续部署(CI/CD)流程。此外,还讨论了微服务实施过程中常见的问题,如服务间通信复杂度增加、数据一致性保障等,并提供了相应的解决方案和优化建议。总之,本文旨在为开发者提供一份关于如何在现代后端系统中有效采用和优化微服务架构的实用指南。 ####
|
9天前
|
消息中间件 设计模式 运维
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在现代后端开发中的应用,通过实际案例分析,揭示了其在提升系统灵活性、可扩展性及促进技术创新方面的显著优势。同时,文章也未回避微服务实施过程中面临的挑战,如服务间通信复杂性、数据一致性保障及部署运维难度增加等问题,并基于实践经验提出了一系列应对策略,为开发者在构建高效、稳定的微服务平台时提供有价值的参考。 ####
|
10天前
|
消息中间件 监控 数据管理
后端开发中的微服务架构实践与挑战####
【10月更文挑战第29天】 在当今快速发展的软件开发领域,微服务架构已成为构建高效、可扩展和易于维护应用程序的首选方案。本文探讨了微服务架构的核心概念、实施策略以及面临的主要挑战,旨在为开发者提供一份实用的指南,帮助他们在项目中成功应用微服务架构。通过具体案例分析,我们将深入了解如何克服服务划分、数据管理、通信机制等关键问题,以实现系统的高可用性和高性能。 --- ###
32 2
|
11天前
|
监控 安全 应用服务中间件
微服务架构下的API网关设计策略与实践####
本文深入探讨了在微服务架构下,API网关作为系统统一入口点的设计策略、实现细节及其在实际应用中的最佳实践。不同于传统的摘要概述,本部分将直接以一段精简的代码示例作为引子,展示一个基于NGINX的简单API网关配置片段,随后引出文章的核心内容,旨在通过具体实例激发读者兴趣,快速理解API网关在微服务架构中的关键作用及实现方式。 ```nginx server { listen 80; server_name api.example.com; location / { proxy_pass http://backend_service:5000;