基于go-micro微服务的实战-注册成功推送Rabbitmq队列,邮件服务异步发送邮件(七)

基于go-micro微服务的实战-注册成功推送Rabbitmq队列,邮件服务异步发送邮件(七)

文章最后附带完整代码

这一节是在用户注册基础上,注册成果则推送用户信息到Rabbitmq,异步处理,邮件服务订阅并发送注册邮件。

go-micro v3也可以通过定制Broker,使用Rabbitmq发布和订阅,但使用过程遇到点问题,在github上也提了issues。 当然也可以自己用原生rabbitmq库或者其它封装的库,这里使用的是开源的rabbitmq连接池库gitee.com/tym_hmm/rabbitmq-pool-go",整体调用简单,也支持死信队列,重试等。

至于Rabbitmq的部署,这里就不详细讲解。可以网上搜索。


设计流程是这样

  1. 用户服务启用Rabbitmq的发布池
  2. 新增邮件服务,同时启用Rabbitmq的消费池,消费者订阅等待消费
  3. 用户注册成功后,用户服务发布消息到MQ队列
  4. 邮件服务的消费者订阅MQ队列,消费并发送注册邮件

第一步:用户服务启用Rabbitmq的发布池

在配置文件grpc_server/user/conf/service.conf中新增配置项

##rabbitmq
rabmq_addr = "127.0.0.1"
rabmq_port = 5672
rabmq_user = "guest"
rabmq_pwd = "guest"

新增rabbitmq处理模块lib/mq/rabbitmq.go

import RabbimqPool "gitee.com/tym_hmm/rabbitmq-pool-go"

var PubMqPool *RabbimqPool.RabbitPool

//初始化发布池
func InitRabbitmq() {
	PubMqPool = RabbimqPool.NewProductPool()
    //设置最大连接是4个
	PubMqPool.SetMaxConnection(4)
	rabAddr := common.Config.String("rabmq_addr")
	rabPort, _ := common.Config.Int("rabmq_port")
	rabUser := common.Config.String("rabmq_user")
	rabPwd := common.Config.String("rabmq_pwd")
	err:= PubMqPool.ConnectVirtualHost(rabAddr, rabPort, rabUser, rabPwd, "/")
	if err != nil {
		log.Println("InitRabbitmq Err:", err)
	}
}

//发布消息
func Publish(args ...string) *RabbimqPool.RabbitMqError{
	if len(args) < 4 {
		return &RabbimqPool.RabbitMqError{Message: "args error"}
	}

	var exChangeType string
	exChangeName := args[0]
	queueName := args[1]
	routeKey := args[2]
	body := args[3]

	if len(args) == 4 {
		exChangeType = RabbimqPool.EXCHANGE_TYPE_DIRECT
	}else {
		exChangeType = args[4]
	}

	data := RabbimqPool.GetRabbitMqDataFormat(
		exChangeName,   //交换机名
		exChangeType,   //交换机类型
		queueName,      //队列名
		routeKey,       //key
		body,           //内容
	)
	return PubMqPool.Push(data)
}

在服务启动处server.go初始化Rabbitmq

import "emicro_test/emicro_7/grpc_server/user/lib/mq"
...
//初始rabbitmq连接池
mq.InitRabbitmq()
...
第二步:新增邮件服务,同时启用Rabbitmq的消费池

新增服务的目录结构和编码跟用户服务类似,具体可看项目源码
mail目录

新增Rabbitmq的消费者配置信息mail/lib/conf/mq.toml,这里由于需要多层嵌套,用toml配置格式github.com/BurntSushi/toml

[Rabbitmq]
    [Rabbitmq.user_reg]
        ExchangeName = "user_exchange"
        ExchangeType = "direct"
        Route = ""
        QueueName = "user_reg"
        IsTry = true
        IsAutoAck = false
        MaxReTry = 3

对应的toml解析结构体

type Consumer struct{
	ExchangeName string
	ExchangeType string
	Route string
	QueueName string
	IsTry bool
	IsAutoAck bool
	MaxReTry int
}

type config struct {
	Consumers map[string]Consumer `toml:"Rabbitmq"`
}

Rabbitmq的消费池初始化和消费者订阅处理

func InitRabbitmq() {
	SubMqPool = RabbimqPool.NewConsumePool()
    //设置最大连接数为10
	SubMqPool.SetMaxConnection(10)
    //设置每个消费者队列的最大消费者数为10
	SubMqPool.SetMaxConsumeChannel(10)
	rabAddr := common.Config.String("rabmq_addr")
	rabPort, _ := common.Config.Int("rabmq_port")
	rabUser := common.Config.String("rabmq_user")
	rabPwd := common.Config.String("rabmq_pwd")
	err:= SubMqPool.ConnectVirtualHost(rabAddr, rabPort, rabUser, rabPwd, "/")
	if err != nil {
		log.Println("InitRabbitmq Err:", err)
	}
	InitConsumer()
}

func InitConsumer() {
    //获取配置和解析
	var conf config
	_, err := toml.DecodeFile("./lib/conf/mq.toml", &conf)
	if err !=nil {
		log.Println("parse mq.toml Err:", err)
		return
	}
    //初始各个消费者队列
	for _, q := range conf.Consumers{
		consumeReceive := &RabbimqPool.ConsumeReceive{
			ExchangeName: q.ExchangeName,   //交换机名
			ExchangeType: q.ExchangeType,   //交换机类型
			Route:        q.Route,          //key
			QueueName:    q.QueueName,      //队列名
			IsTry:        q.IsTry,  				//是否重试
			IsAutoAck:    q.IsAutoAck, 				//自动消息确认
			MaxReTry:     int32(q.MaxReTry),     	//最大重试次数
			EventFail: func(code int, e error, data []byte) {   //消费失败的调用
				log.Printf("error:%s", e)
			},
			EventSuccess: successHandler(q),        //消费成功的调用
		}
		SubMqPool.RegisterConsumeReceive(consumeReceive)
	}

	go func() {
		err := SubMqPool.RunConsume()
		if err != nil {
			log.Println("InitConsumer Err:", err)
		}
	}()
}

//根据不同队列的不同分发处理
func successHandler(consumer Consumer) func(data []byte, header map[string]interface{}, retryClient RabbimqPool.RetryClientInterface) bool{
	switch consumer.QueueName {
	case "user_reg":
		return user_reg_handler()
	}
	return nil
}

//用户注册消费处理
func user_reg_handler() func(data []byte, header map[string]interface{}, retryClient RabbimqPool.RetryClientInterface) bool {
	return func(data []byte, header map[string]interface{}, retryClient RabbimqPool.RetryClientInterface) bool { //如果返回true 则无需重试
		log.Printf("user_reg_handler data:%s\n", string(data))
		retryClient.Ack()
		return true
	}
}
第三步:用户注册成功后,发布消息到MQ队列

user/handler/user_handler.go中的UserReg函数新增发布消息

import "emicro_test/emicro_7/grpc_server/user/lib/mq"
...
    //发送到rabbitmq
	jsonUser, _ := json.Marshal(user)
	res := mq.Publish("user_exchange", "user_reg", "", string(jsonUser))
	log.Println("pub res:",res)

	resp.Status = common.RESP_SUCCESS
	resp.Msg = "success"
第四步:邮件服务发送注册邮件

这里发送邮件用的是smtp,需要先开通和获取授权码,具体可参考其他人博客邮箱设置

mail/conf/service.conf新增邮件配置项

##邮件
smtp_addr = "smtp.qq.com"
smtp_port = 25
smtp_user = ""      //发件人邮箱
smtp_password = ""  //发件人授权码

新增发送邮件的Api,mail/utils/mail.go, 使用第三方库gomail

import "gopkg.in/gomail.v2"

func SendMail(toMail string, body string) error{
	smtp_addr := common.Config.String("smtp_addr")
	smtp_port, _ := common.Config.Int("smtp_port")
	smtp_user := common.Config.String("smtp_user")
	smtp_pwd := common.Config.String("smtp_password")
	m := gomail.NewMessage()
	m.SetHeader("From", smtp_user)
	m.SetHeader("To", toMail)
	m.SetBody("text/plain", body)
	d := gomail.NewPlainDialer(smtp_addr, smtp_port, smtp_user, smtp_pwd)
	if err := d.DialAndSend(m); err != nil {
		return err
	}
	return nil
}

消费发送注册邮件,调整上述第二步新增的用户注册消费处理func user_reg_handler

func user_reg_handler() func(data []byte, header map[string]interface{}, retryClient RabbimqPool.RetryClientInterface) bool {
	return func(data []byte, header map[string]interface{}, retryClient RabbimqPool.RetryClientInterface) bool { //如果返回true 则无需重试
		log.Printf("user_reg_handler data:%s\n", string(data))

		//解析json数据
		var user struct{
			Id int32
			Name string
			Phone string
			Email string
		}
		err := json.Unmarshal(data, &user)
		if err != nil{
			log.Println("user_reg_handler Unmarshal Err:", err)
			return false
		}

		//推送邮件
		Body := fmt.Sprintf("Hello. The name is %v and the phone is %v register success!!", user.Name, user.Phone)
		err = utils.SendMail(user.Email, Body)
		if err != nil{
			log.Println("user_reg_handler Err:", err)
			return false
		}

		retryClient.Ack()
		return true
	}
}
第五步:测试验证

测试验证这步跟随第4节 的注册测试,同样测试方式即可。最终有发送邮件到注册的邮箱即可。如下图效果:

效果

gitee完整代码链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值