goframe开发一个企业网站 rabbitmq队例15

news2024/11/14 3:51:35

RabbitMQ消息队列封装

在目录internal/pkg/rabbitmq/rabbitmq.go

# 消息队列配置
mq:
  # 消息队列类型: rocketmq 或 rabbitmq
  type: "rabbitmq"
  # 是否启用消息队列
  enabled: true


rocketmq:
  nameServer: "127.0.0.1:9876"
  producerGroup: "myProducerGroup"
  consumerGroup: "myConsumerGroup"
  brokerAddress: "127.0.0.1:10911"  # 添加 broker 地址

rabbitmq:
  url: "amqp://wanghaibin:wanghaibin@127.0.0.1:5672/"
  exchange: "gf_exchange"
  dlx_exchange: "gf_dlx_exchange"    # 新增:死信交换机
  queue: "gf_queue"
  delay_queue: "gf_delay_queue"      # 新增:延迟队列
  routingKey: "gf_key"
  vhost: "/"

package rabbitmq

import (
	"context"
	"fmt"
	"time"

	"github.com/gogf/gf/v2/frame/g"
	amqp "github.com/rabbitmq/amqp091-go"
)

var (
	// conn RabbitMQ连接实例
	conn *amqp.Connection
	// channel RabbitMQ通道实例
	channel *amqp.Channel
)

// Initialize 初始化 RabbitMQ 连接和通道
// 包括:建立连接、创建通道、声明交换机和队列、建立绑定关系
func Initialize() {
	var err error
	ctx := context.Background()

	// 从配置文件获取RabbitMQ连接URL
	url := g.Cfg().MustGet(ctx, "rabbitmq.url").String()

	// 建立RabbitMQ连接
	conn, err = amqp.Dial(url)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to connect to RabbitMQ: %v", err)
	}

	// 创建通道
	channel, err = conn.Channel()
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to open channel: %v", err)
	}

	// 1. 声明主交换机
	// 类型:direct,持久化:true,自动删除:false,内部的:false,非阻塞:false
	err = channel.ExchangeDeclare(
		g.Cfg().MustGet(ctx, "rabbitmq.exchange").String(),
		"direct", // 交换机类型
		true,     // 持久化
		false,    // 自动删除
		false,    // 内部的
		false,    // 非阻塞
		nil,      // 参数
	)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to declare main exchange: %v", err)
	}

	// 2. 声明死信交换机(DLX)
	// 用于处理无法被正常消费的消息
	err = channel.ExchangeDeclare(
		g.Cfg().MustGet(ctx, "rabbitmq.dlx_exchange").String(),
		"direct",
		true,
		false,
		false,
		false,
		nil,
	)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to declare DLX exchange: %v", err)
	}

	// 3. 声明主队列
	// 持久化:true,非自动删除,非排他,非阻塞
	_, err = channel.QueueDeclare(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),
		true,  // 持久化
		false, // 自动删除
		false, // 排他的
		false, // 非阻塞
		nil,   // 参数
	)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to declare main queue: %v", err)
	}

	// 4. 声明延迟队列
	// 配置死信交换机参数
	args := amqp.Table{
		"x-dead-letter-exchange":    g.Cfg().MustGet(ctx, "rabbitmq.dlx_exchange").String(),
		"x-dead-letter-routing-key": g.Cfg().MustGet(ctx, "rabbitmq.routingKey").String(),
	}
	_, err = channel.QueueDeclare(
		g.Cfg().MustGet(ctx, "rabbitmq.delay_queue").String(),
		true,
		false,
		false,
		false,
		args,
	)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to declare delay queue: %v", err)
	}

	// 5. 绑定主队列到主交换机
	err = channel.QueueBind(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),      // 队列名
		g.Cfg().MustGet(ctx, "rabbitmq.routingKey").String(), // 路由键
		g.Cfg().MustGet(ctx, "rabbitmq.exchange").String(),   // 交换机名
		false,
		nil,
	)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to bind main queue: %v", err)
	}

	// 6. 绑定主队列到死信交换机
	err = channel.QueueBind(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),
		g.Cfg().MustGet(ctx, "rabbitmq.routingKey").String(),
		g.Cfg().MustGet(ctx, "rabbitmq.dlx_exchange").String(),
		false,
		nil,
	)
	if err != nil {
		g.Log().Fatalf(ctx, "Failed to bind queue to DLX: %v", err)
	}

	g.Log().Info(ctx, "RabbitMQ initialized successfully")
}

// PublishMessage 发布消息到RabbitMQ
// 参数:
//   - ctx: 上下文
//   - message: 要发送的消息内容
// 返回:
//   - error: 发送错误,如果成功则为nil
func PublishMessage(ctx context.Context, message string) error {
	// 创建带超时的上下文
	ctxTimeout, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	// 发布消息到指定的交换机和路由
	err := channel.PublishWithContext(ctxTimeout,
		g.Cfg().MustGet(ctx, "rabbitmq.exchange").String(),
		g.Cfg().MustGet(ctx, "rabbitmq.routingKey").String(),
		false, // mandatory
		false, // immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
		},
	)
	if err != nil {
		return fmt.Errorf("failed to publish message: %v", err)
	}

	return nil
}

// ConsumeMessages 消费队列中的消息
// 参数:
//   - ctx: 上下文
//   - handler: 消息处理函数
// 返回:
//   - error: 消费错误,如果成功则为nil
func ConsumeMessages(ctx context.Context, handler func(string) error) error {
	messages, err := channel.Consume(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),
		"",    // consumer
		false, // auto-ack
		false, // exclusive
		false, // no-local
		false, // no-wait
		nil,   // args
	)
	if err != nil {
		return fmt.Errorf("failed to register a consumer: %v", err)
	}

	// 启动goroutine处理消息
	go func() {
		for msg := range messages {
			err := handler(string(msg.Body))
			if err != nil {
				g.Log().Errorf(ctx, "Error handling message: %v", err)
				msg.Nack(false, true) // 处理失败,消息重新入队
			} else {
				msg.Ack(false) // 处理成功,确认消息
			}
		}
	}()

	return nil
}

// Cleanup 清理RabbitMQ连接和通道
func Cleanup() {
	if channel != nil {
		channel.Close()
	}
	if conn != nil {
		conn.Close()
	}
}

// GetChannel 获取RabbitMQ通道实例
func GetChannel() *amqp.Channel {
	return channel
}

// PurgeQueue 清空指定队列中的所有消息
// 参数:
//   - ctx: 上下文
// 返回:
//   - error: 清空错误,如果成功则为nil
func PurgeQueue(ctx context.Context) error {
	_, err := channel.QueuePurge(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),
		false, // no-wait
	)
	return err
}

// PublishDelayMessage 发送延迟消息
// 参数:
//   - ctx: 上下文
//   - message: 消息内容
//   - delaySeconds: 延迟秒数
// 返回:
//   - error: 发送错误,如果成功则为nil
func PublishDelayMessage(ctx context.Context, message string, delaySeconds int) error {
	return channel.PublishWithContext(ctx,
		"",                                                    // 默认交换机
		g.Cfg().MustGet(ctx, "rabbitmq.delay_queue").String(), // 延迟队列
		false,
		false,
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
			Expiration:  fmt.Sprintf("%d", delaySeconds*1000), // 转换为毫秒
		},
	)
}

// GetQueueLength 获取队列中的消息数量
// 参数:
//   - ctx: 上下文
// 返回:
//   - int: 消息数量
//   - error: 获取错误,如果成功则为nil
func GetQueueLength(ctx context.Context) (int, error) {
	queue, err := channel.QueueInspect(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),
	)
	if err != nil {
		return 0, fmt.Errorf("failed to inspect queue: %v", err)
	}
	return queue.Messages, nil
}

logic逻辑的实现
package rabbitmqmsg

import (
	"context"
	"fmt"
	"gf_new_web/internal/pkg/rabbitmq"
	"gf_new_web/internal/service"

	"github.com/gogf/gf/v2/frame/g"
)

// sRabbitmqMsg RabbitMQ消息服务结构体
type sRabbitmqMsg struct{}

// New 创建新的RabbitMQ消息服务实例
func New() *sRabbitmqMsg {
	return &sRabbitmqMsg{}
}

// init 初始化函数,在包加载时自动注册RabbitMQ消息服务
func init() {
	service.RegisterRabbitmqMsg(New())
}

// SendMessage 发送普通消息到RabbitMQ
// 参数:
//   - ctx: 上下文信息
//   - message: 要发送的消息内容
// 返回:
//   - error: 发送错误,成功则为nil
func (s *sRabbitmqMsg) SendMessage(ctx context.Context, message string) error {
	return rabbitmq.PublishMessage(ctx, message)
}

// SendDelayMessage 发送延迟消息到RabbitMQ
// 参数:
//   - ctx: 上下文信息
//   - message: 要发送的消息内容
//   - delaySeconds: 延迟时间(秒)
// 返回:
//   - error: 发送错误,成功则为nil
func (s *sRabbitmqMsg) SendDelayMessage(ctx context.Context, message string, delaySeconds int) error {
	return rabbitmq.PublishDelayMessage(ctx, message, delaySeconds)
}

// SendBatchMessages 批量发送消息到RabbitMQ
// 参数:
//   - ctx: 上下文信息
//   - messages: 消息内容数组
// 返回:
//   - error: 发送错误,成功则为nil
// 注意:任一消息发送失败都会导致整个批次失败
func (s *sRabbitmqMsg) SendBatchMessages(ctx context.Context, messages []string) error {
	for _, msg := range messages {
		if err := rabbitmq.PublishMessage(ctx, msg); err != nil {
			return err
		}
	}
	return nil
}

// GetQueueLength 获取队列当前的消息数量
// 参数:
//   - ctx: 上下文信息
// 返回:
//   - int: 队列中的消息数量
//   - error: 获取错误,成功则为nil
func (s *sRabbitmqMsg) GetQueueLength(ctx context.Context) (int, error) {
	queue, err := rabbitmq.GetChannel().QueueInspect(
		g.Cfg().MustGet(ctx, "rabbitmq.queue").String(),
	)
	if err != nil {
		return 0, fmt.Errorf("failed to inspect queue: %v", err)
	}
	return queue.Messages, nil
}

// PurgeQueue 清空队列中的所有消息
// 参数:
//   - ctx: 上下文信息
// 返回:
//   - error: 清空错误,成功则为nil
func (s *sRabbitmqMsg) PurgeQueue(ctx context.Context) error {
	return rabbitmq.PurgeQueue(ctx)
}

// handleMessage 处理接收到的单条消息
// 参数:
//   - message: 消息内容
// 返回:
//   - error: 处理错误,成功则为nil
// 注意:这是内部方法,实现具体的消息处理逻辑
func (s *sRabbitmqMsg) handleMessage(message string) error {
	// 记录接收到的消息
	g.Log().Info(context.Background(), "收到消息:", message)
	// TODO: 在这里添加实际的消息处理逻辑
	return nil
}

// Initialize 初始化消息消费处理
// 参数:
//   - ctx: 上下文信息
// 返回:
//   - error: 初始化错误,成功则为nil
// 功能:启动消息消费者,并设置消息处理函数
func (s *sRabbitmqMsg) Initialize(ctx context.Context) error {
	return rabbitmq.ConsumeMessages(ctx, func(msg string) error {
		return s.handleMessage(msg)
	})
}

生成service ,不再写上

controller代码

package front

import (
	"fmt"
	"gf_new_web/internal/service"
	"time"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
)

var (
	RabbitMsg = cRabbitMsg{}
)

type cRabbitMsg struct{}

// SendMessage 处理发送普通消息的HTTP请求
// 请求参数:
//   - message: 消息内容
// 响应格式:
//   成功:{"code": 0, "msg": "消息发送成功"}
//   失败:{"code": -1, "msg": "错误信息"}
func (c *cRabbitMsg) SendMessage(r *ghttp.Request) {
	message := r.Get("message").String()

	err := service.RabbitmqMsg().SendMessage(r.GetCtx(), message)
	if err != nil {
		g.Log().Error(r.GetCtx(), err)
		r.Response.WriteJson(g.Map{
			"code": -1,
			"msg":  err.Error(),
		})
		return
	}

	r.Response.WriteJson(g.Map{
		"code": 0,
		"msg":  "消息发送成功",
	})
}

// SendDelayMessage 处理发送延迟消息的HTTP请求
// 请求参数:
//   - message: 消息内容
//   - delay: 延迟时间(秒)
// 响应格式:
//   成功:{"code": 0, "msg": "延迟消息发送成功"}
//   失败:{"code": -1, "msg": "错误信息"}
func (c *cRabbitMsg) SendDelayMessage(r *ghttp.Request) {
	message := r.Get("message").String()
	delaySeconds := r.Get("delay").Int()

	err := service.RabbitmqMsg().SendDelayMessage(r.GetCtx(), message, delaySeconds)
	if err != nil {
		g.Log().Error(r.GetCtx(), err)
		r.Response.WriteJson(g.Map{
			"code": -1,
			"msg":  err.Error(),
		})
		return
	}

	r.Response.WriteJson(g.Map{
		"code": 0,
		"msg":  "延迟消息发送成功",
	})
}

// SendBatchMessages 处理批量发送消息的HTTP请求
// 请求参数:
//   - messages: 消息内容数组
// 响应格式:
//   成功:{"code": 0, "msg": "批量消息发送成功"}
//   失败:{"code": -1, "msg": "错误信息"}
func (c *cRabbitMsg) SendBatchMessages(r *ghttp.Request) {
	messages := r.Get("messages").Strings()

	err := service.RabbitmqMsg().SendBatchMessages(r.GetCtx(), messages)
	if err != nil {
		g.Log().Error(r.GetCtx(), err)
		r.Response.WriteJson(g.Map{
			"code": -1,
			"msg":  err.Error(),
		})
		return
	}

	r.Response.WriteJson(g.Map{
		"code": 0,
		"msg":  "批量消息发送成功",
	})
}

// GetQueueLength 处理获取队列长度的HTTP请求
// 响应格式:
//   成功:{"code": 0, "msg": "获取队列长度成功", "data": 队列长度}
//   失败:{"code": -1, "msg": "错误信息"}
func (c *cRabbitMsg) GetQueueLength(r *ghttp.Request) {
	length, err := service.RabbitmqMsg().GetQueueLength(r.GetCtx())
	if err != nil {
		g.Log().Error(r.GetCtx(), err)
		r.Response.WriteJson(g.Map{
			"code": -1,
			"msg":  err.Error(),
		})
		return
	}

	r.Response.WriteJson(g.Map{
		"code": 0,
		"msg":  "获取队列长度成功",
		"data": length,
	})
}

// PurgeQueue 处理清空队列的HTTP请求
// 响应格式:
//   成功:{"code": 0, "msg": "清空队列成功"}
//   失败:{"code": -1, "msg": "错误信息"}
func (c *cRabbitMsg) PurgeQueue(r *ghttp.Request) {
	err := service.RabbitmqMsg().PurgeQueue(r.GetCtx())
	if err != nil {
		g.Log().Error(r.GetCtx(), err)
		r.Response.WriteJson(g.Map{
			"code": -1,
			"msg":  err.Error(),
		})
		return
	}

	r.Response.WriteJson(g.Map{
		"code": 0,
		"msg":  "清空队列成功",
	})
}

// ConsumeMessages 处理消费消息的HTTP请求
// 特点:异步处理,非阻塞
// 响应格式:
//   成功:{"code": 0, "msg": "消息消费已开始,请查看服务器日志获取消费详情"}
//   失败:{"code": -1, "msg": "错误信息"}
func (c *cRabbitMsg) ConsumeMessages(r *ghttp.Request) {
	g.Log().Info(r.GetCtx(), "开始消费消息...")

	done := make(chan bool)

	go func() {
		err := service.RabbitmqMsg().Initialize(r.GetCtx())
		if err != nil {
			g.Log().Error(r.GetCtx(), "消费消息出错:", err)
			r.Response.WriteJson(g.Map{
				"code": -1,
				"msg":  fmt.Sprintf("消费消息失败: %v", err),
			})
			done <- true
			return
		}
	}()

	select {
	case <-done:
		return
	case <-time.After(5 * time.Second):
		g.Log().Info(r.GetCtx(), "消息消费进行中...")
		r.Response.WriteJson(g.Map{
			"code": 0,
			"msg":  "消息消费已开始,请查看服务器日志获取消费详情",
		})
	}
}

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

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

相关文章

el-table 纵向垂直表头处理

项目中表格展示会遇到需要纵向垂直表头情况&#xff0c;下面&#xff0c;我们基于el-table组件来实现这种表格。 以下是这次需要用到的数据表格&#xff0c;已知左侧违章名称是固定的&#xff0c;而月份是不固定的&#xff0c;在后端返回数据格式已确定的情况下&#xff0c;需…

Android OpenGL ES详解——纹理:纹理过滤GL_NEAREST和GL_LINEAR的区别

目录 一、概念 1、纹理过滤 2、邻近过滤 3、线性过滤 二、邻近过滤和线性过滤的区别 三、源码下载 一、概念 1、纹理过滤 当纹理被应用到三维物体上时&#xff0c;随着物体表面的形状和相机视角的变化&#xff0c;会导致纹理在渲染过程中出现一些问题&#xff0c;如锯齿…

超市11-12月生鲜重点商品配置

11月份&#xff1a;应季商品很多,特别是与季节相对应的蔬菜大量上市。宜推荐对预防感冒等相应的特殊食谱,推荐对于常外出的人方便又省事、省时的食谱&#xff0c;推荐多种花样的火锅&#xff0c;推荐便于保存的应季食品原料。 生活特性&#xff1a;大众食谱宜以炖菜、红焖、火锅…

c++设计模式demo

模式设计原则 依赖倒置原则 ⾼层模块不应该依赖低层模块&#xff0c;⼆者都应该依赖抽象 &#xff1b; 抽象不应该依赖具体实现&#xff0c;具体实现应该依赖于抽象&#xff1b; ⾃动驾驶系统公司是⾼层&#xff0c;汽⻋⽣产⼚商为低层&#xff0c;它们不应该互相依赖&#x…

【网络面试篇】其他面试题——Cookie、Session、DNS、CDN、SSL/TLS、加密概念

目录 一、HTTP 相关问题 1. Cookie 和 Session 是什么&#xff1f; &#xff08;1&#xff09;Cookie &#xff08;2&#xff09;Session 2. Cookie 的工作原理&#xff1f; 3. Session 的工作原理&#xff1f; 4. Cookie 和 Session 有什么区别&#xff1f; 二、其他问…

软件测试第二篇软件测试技术

第五章单元测试和集成测试的技术 单元静态测试主要由开发人员完成。 标准&#xff1a;规定什么能做&#xff0c;什么不能做。 规范&#xff1a;建议你要怎么做。 5.1.2 代码评审 代码评审是一种发现代码缺陷的另一种测试方法。 代码审查的最佳实践&#xff1a; 创建代码审…

QT中 update()函数无法实时调用 paintEvent

QT中 update()函数无法实时调用 paintEvent&#xff01; 在QT中&#xff0c;update()函数用于标记一个窗口区域为“需要重绘”。当调用update()后&#xff0c;QT会在合适的时候调用paintEvent()来重绘这个区域。然而&#xff0c;update()不会立即调用paintEvent()&#xff0c;…

SDL事件相关

文章目录 事件相关的函数和数据结构用户自定义事件代码相关&#xff1a; 事件相关的函数和数据结构 SDL_WaitEvent :等待一个事件SDL_PushEvent 发送一个事件SDL_PumpEvents(): 将硬件设备产生的时间放入事件队列 &#xff0c;用于读取事件&#xff0c;在调用该函数之前&#…

优化时钟网络之时钟抖动

Note&#xff1a;文章内容以Xilinx 7系列FPGA进行讲解 1、什么是时钟抖动 时钟抖动就是时钟周期之间出现的偏差。比如一个时钟周期为10ns的时钟&#xff0c;理想情况下&#xff0c;其上升沿会出现在0ns&#xff0c;10ns&#xff0c;20ns时刻&#xff0c;假设某个上升沿出现的时…

达梦8-达梦数据实时同步软件(DMHS)配置-Oracle-DM8

1、安装环境 源端目的端IP地址192.168.6.111192.168.6.110系统版本Red Hat 6.4Kylin v10数据库版本Oracle11g达梦 v8系统用户Oracledmdba字符集MERICAN_AMERICA.AL32UTF8UTF-8端口15215236实例名PRODDMSERVER数据库软件目录/u01/app/oracle/opt/dmdbmsDMHS安装目录/u01/dmhs/o…

AI基础知识

目录 1.激活函数:one: 激活函数的作用:two: sigmoid函数:three: tanh函数:four: ReLu:five: Leaky ReLU 2.Softmax函数3.优化器:one: 优化器的作用:two: BGD&#xff08;批梯度下降&#xff09;:three: SGD&#xff08;随机梯度下降&#xff09;:four: MBGD&#xff08;Mini Ba…

【论文阅读】Learning dynamic alignment via meta-filter for few-shot learning

通过元滤波器学习动态对齐以实现小样本学习 引用&#xff1a;Xu C, Fu Y, Liu C, et al. Learning dynamic alignment via meta-filter for few-shot learning[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2021: 5182-5191. 论文…

ArcGIS Pro SDK Addin-DAML

ArcGIS Pro SDK Addin-DAML 文章目录 ArcGIS Pro SDK Addin-DAML1 Panes: 重置窗格2 Button: 从功能区中移除核心按钮3 Button: 将新按钮插入功能区上的现有组4 Menu: 在图层上下文菜单中插入一个新按钮5 Menu: 在 Map Container 上下文菜单中插入新菜单6 Menu: 在2D Map上下文…

FPGA学习笔记#7 Vitis HLS 数组优化和函数优化

本笔记使用的Vitis HLS版本为2022.2&#xff0c;在windows11下运行&#xff0c;仿真part为xcku15p_CIV-ffva1156-2LV-e&#xff0c;主要根据教程&#xff1a;跟Xilinx SAE 学HLS系列视频讲座-高亚军进行学习 学习笔记&#xff1a;《FPGA学习笔记》索引 FPGA学习笔记#1 HLS简介及…

熊猫追剧 1.0.2 | 免费影视播放,独家蓝光线路

熊猫追剧是一款免费的视频播放软件&#xff0c;集合了网络上的电影、电视剧、综艺、动漫以及短剧等多种类型的视频资源。经过测试&#xff0c;该软件内没有广告干扰&#xff0c;采用独家蓝光线路&#xff0c;提供高清流畅的观影体验。用户还可以享受视频投屏、下载和倍速播放等…

[CKS] K8S AppArmor Set Up

最近准备花一周的时间准备CKS考试&#xff0c;在准备考试中发现有一个题目关于AppArmor Pod操作权限的问题。 ​ 专栏其他文章: [CKS] Create/Read/Mount a Secret in K8S-CSDN博客[CKS] Audit Log Policy-CSDN博客 -[CKS] 利用falco进行容器日志捕捉和安全监控-CSDN博客[CKS] …

Tomcat漏洞利用工具-TomcatScanPro(二)

项目地址 https://github.com/lizhianyuguangming/TomcatScanPro 简介 本项目是一个针对 Tomcat 服务的弱口令检测、漏洞检测以及本地文件包含&#xff08;LFI&#xff09;攻击工具。除了支持 CVE-2017-12615 漏洞的多种利用方式外&#xff0c;新版本还集成了 CNVD-2020-104…

【XML协议】轻松掌握使用C++ XML解析库——pugixml

文章介绍了xml协议的组成以及C xml解析库pugixml的常用操作。源于开发中每次遇到xml操作时&#xff0c;都要回过头查看pugixml库常用操作时什么样的&#xff0c;能不能有个更深刻和清晰的认识呢&#xff1f;其实搞清楚xml结构和pugixml组织结构的对照关系&#xff0c;以及pugix…

《无线重构世界》射频模组演进

射频前端四大金刚 射频前端由PA、LNA、滤波器、开关“四大金刚” 不同的模块有自己的工艺和性能特点 分层设计 射频前端虽然只由PA、LNA、开关、混频器4个模块构成&#xff0c;但不同模块之间相互连接且相互影响。如果将射频系统当成一个整体来理解&#xff0c;其中的细节和…

《Python编程实训快速上手》第五天--模式匹配与正则表达式

一、不用正则表达式查找文本模式 文本模式是一种人为规定的结构&#xff0c;现在有一个模式&#xff1a;3个数字-3个数字-4个数字 使用isPhoneNumber()函数来判断字符串是否匹配该模式 def isPhoneNumber(number):if len(number) ! 12:return Falsefor i in range(0,3):if n…