珠海网站建设报价/发稿软文公司
Go操作各大消息队列教程
1 RabbitMQ
1.1 概念
①基本名词
当前市面上mq的产品很多,比如RabbitMQ、Kafka、ActiveMQ、ZeroMQ和阿里巴巴捐献给Apache的RocketMQ。甚至连redis这种NoSQL都支持MQ的功能。
- Broker:表示消息队列服务实体
- Virtual Host:虚拟主机。标识一批交换机、消息队列和相关对象。vhost是AMQP概念的基础,必须在链接时指定,RabbitMQ默认的vhost是 /。
- AMQP(Advanced Message Queuing Protocol)高级消息队列协议
- Exchange:交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
- Queue:消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
②常见模式
1. simple简单模式
消息的消费者(consumer) 监听(while) 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患 消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失)
2. worker工作模式
多个消费者从一个队列中争抢消息
- (隐患,高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize,与同步锁的性能不一样) 保证一条消息只能被一个消费者使用)
- 应用场景:红包;大项目中的资源调度(任务分配系统不需知道哪一个任务执行系统在空闲,直接将任务扔到消息队列中,空闲的系统自动争抢)
3. publish/subscribe发布订阅(共享资源)
消费者订阅消息,然后从订阅的队列中获取消息进行消费。
- X代表交换机rabbitMQ内部组件,erlang 消息产生者是代码完成,代码的执行效率不高,消息产生者将消息放入交换机,交换机发布订阅把消息发送到所有消息队列中,对应消息队列的消费者拿到消息进行消费
- 相关场景:邮件群发,群聊天,广播(广告)
4. routing路由模式
- 交换机根据路由规则,将消息路由到不同的队列中
- 消息生产者将消息发送给交换机按照路由判断,路由是字符串(info) 当前产生的消息携带路由字符(对象的方法),交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;
5. topic主题模式(路由模式的一种)
- 星号井号代表通配符
- 星号代表多个单词,井号代表一个单词
- 路由功能添加模糊匹配
- 消息产生者产生消息,把消息交给交换机
- 交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费
1.2 搭建(docker方式)
①拉取镜像
# 拉取镜像
docker pull rabbitmq:3.7-management
②创建并启动容器
# 创建并运行容器
docker run -d --name myrabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.7-management
#5672是项目中连接rabbitmq的端口(我这里映射的是5672),15672是rabbitmq的web管理界面端口(我映射为15672)# 输入网址http://ip:15672即可进入rabbitmq的web管理页面,账户密码:guest / guest
③web界面创建用户和virtual host
下面为了我们后续的操作,首先我们新建一个Virtual Host并且给他分配一个用户名,用来隔离数据,根据自己需要自行创建
- 新增virtual host
- 新增用户
- 点击新建好的用户,设置其host
- 最终效果
1.3 代码操作
①RabbitMQ struct:包含创建、消费、生产消息
package RabbitMQimport ("fmt""github.com/streadway/amqp""log"
)//amqp:// 账号 密码@地址:端口号/vhost
const MQURL = "amqp://ziyi:ziyi@10.253.50.145:5672/ziyi"type RabbitMQ struct {//连接conn *amqp.Connection//管道channel *amqp.Channel//队列名称QueueName string//交换机Exchange string//key Simple模式 几乎用不到Key string//连接信息Mqurl string
}//创建RabbitMQ结构体实例
func NewRabbitMQ(queuename string, exchange string, key string) *RabbitMQ {rabbitmq := &RabbitMQ{QueueName: queuename, Exchange: exchange, Key: key, Mqurl: MQURL}var err error//创建rabbitmq连接rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)rabbitmq.failOnErr(err, "创建连接错误!")rabbitmq.channel, err = rabbitmq.conn.Channel()rabbitmq.failOnErr(err, "获取channel失败")return rabbitmq
}//断开channel和connection
func (r *RabbitMQ) Destory() {r.channel.Close()r.conn.Close()
}//错误处理函数
func (r *RabbitMQ) failOnErr(err error, message string) {if err != nil {log.Fatalf("%s:%s", message, err)panic(fmt.Sprintf("%s:%s", message, err))}
}//简单模式step:1。创建简单模式下RabbitMQ实例
func NewRabbitMQSimple(queueName string) *RabbitMQ {return NewRabbitMQ(queueName, "", "")
}//订阅模式创建rabbitmq实例
func NewRabbitMQPubSub(exchangeName string) *RabbitMQ {//创建rabbitmq实例rabbitmq := NewRabbitMQ("", exchangeName, "")var err error//获取connectionrabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)rabbitmq.failOnErr(err, "failed to connecct rabbitmq!")//获取channelrabbitmq.channel, err = rabbitmq.conn.Channel()rabbitmq.failOnErr(err, "failed to open a channel!")return rabbitmq
}//订阅模式生成
func (r *RabbitMQ) PublishPub(message string) {//尝试创建交换机,不存在创建err := r.channel.ExchangeDeclare(//交换机名称r.Exchange,//交换机类型 广播类型"fanout",//是否持久化true,//是否字段删除false,//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定false,//是否阻塞 true表示要等待服务器的响应false,nil,)r.failOnErr(err, "failed to declare an excha"+"nge")//2 发送消息err = r.channel.Publish(r.Exchange,"",false,false,amqp.Publishing{//类型ContentType: "text/plain",//消息Body: []byte(message),})
}//订阅模式消费端代码
func (r *RabbitMQ) RecieveSub() {//尝试创建交换机,不存在创建err := r.channel.ExchangeDeclare(//交换机名称r.Exchange,//交换机类型 广播类型"fanout",//是否持久化true,//是否字段删除false,//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定false,//是否阻塞 true表示要等待服务器的响应false,nil,)r.failOnErr(err, "failed to declare an excha"+"nge")//2试探性创建队列,创建队列q, err := r.channel.QueueDeclare("", //随机生产队列名称false,false,true,false,nil,)r.failOnErr(err, "Failed to declare a queue")//绑定队列到exchange中err = r.channel.QueueBind(q.Name,//在pub/sub模式下,这里的key要为空"",r.Exchange,false,nil,)//消费消息message, err := r.channel.Consume(q.Name,"",true,false,false,false,nil,)forever := make(chan bool)go func() {for d := range message {log.Printf("Received a message:%s,", d.Body)}}()fmt.Println("退出请按 Ctrl+C")<-forever
}//话题模式 创建RabbitMQ实例
func NewRabbitMQTopic(exchagne string, routingKey string) *RabbitMQ {//创建rabbitmq实例rabbitmq := NewRabbitMQ("", exchagne, routingKey)var err errorrabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)rabbitmq.failOnErr(err, "failed to connect rabbingmq!")rabbitmq.channel, err = rabbitmq.conn.Channel()rabbitmq.failOnErr(err, "failed to open a channel")return rabbitmq
}//话题模式发送信息
func (r *RabbitMQ) PublishTopic(message string) {//尝试创建交换机,不存在创建err := r.channel.ExchangeDeclare(//交换机名称r.Exchange,//交换机类型 话题模式"topic",//是否持久化true,//是否字段删除false,//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定false,//是否阻塞 true表示要等待服务器的响应false,nil,)r.failOnErr(err, "topic failed to declare an excha"+"nge")//2发送信息err = r.channel.Publish(r.Exchange,//要设置r.Key,false,false,amqp.Publishing{//类型ContentType: "text/plain",//消息Body: []byte(message),})
}//话题模式接收信息
//要注意key
//其中* 用于匹配一个单词,#用于匹配多个单词(可以是零个)
//匹配 表示匹配imooc.* 表示匹配imooc.hello,但是imooc.hello.one需要用imooc.#才能匹配到
func (r *RabbitMQ) RecieveTopic() {//尝试创建交换机,不存在创建err := r.channel.ExchangeDeclare(//交换机名称r.Exchange,//交换机类型 话题模式"topic",//是否持久化true,//是否字段删除false,//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定false,//是否阻塞 true表示要等待服务器的响应false,nil,)r.failOnErr(err, "failed to declare an exchange")//2试探性创建队列,创建队列q, err := r.channel.QueueDeclare("", //随机生产队列名称false,false,true,false,nil,)r.failOnErr(err, "Failed to declare a queue")//绑定队列到exchange中err = r.channel.QueueBind(q.Name,//在pub/sub模式下,这里的key要为空r.Key,r.Exchange,false,nil,)//消费消息message, err := r.channel.Consume(q.Name,"",true,false,false,false,nil,)forever := make(chan bool)go func() {for d := range message {log.Printf("Received a message:%s,", d.Body)}}()fmt.Println("退出请按 Ctrl+C")<-forever
}//路由模式 创建RabbitMQ实例
func NewRabbitMQRouting(exchagne string, routingKey string) *RabbitMQ {//创建rabbitmq实例rabbitmq := NewRabbitMQ("", exchagne, routingKey)var err errorrabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)rabbitmq.failOnErr(err, "failed to connect rabbingmq!")rabbitmq.channel, err = rabbitmq.conn.Channel()rabbitmq.failOnErr(err, "failed to open a channel")return rabbitmq
}//路由模式发送信息
func (r *RabbitMQ) PublishRouting(message string) {//尝试创建交换机,不存在创建err := r.channel.ExchangeDeclare(//交换机名称r.Exchange,//交换机类型 广播类型"direct",//是否持久化true,//是否字段删除false,//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定false,//是否阻塞 true表示要等待服务器的响应false,nil,)r.failOnErr(err, "failed to declare an excha"+"nge")//发送信息err = r.channel.Publish(r.Exchange,//要设置r.Key,false,false,amqp.Publishing{//类型ContentType: "text/plain",//消息Body: []byte(message),})
}//路由模式接收信息
func (r *RabbitMQ) RecieveRouting() {//尝试创建交换机,不存在创建err := r.channel.ExchangeDeclare(//交换机名称r.Exchange,//交换机类型 广播类型"direct",//是否持久化true,//是否字段删除false,//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定false,//是否阻塞 true表示要等待服务器的响应false,nil,)r.failOnErr(err, "failed to declare an excha"+"nge")//2试探性创建队列,创建队列q, err := r.channel.QueueDeclare("", //随机生产队列名称false,false,true,false,nil,)r.failOnErr(err, "Failed to declare a queue")//绑定队列到exchange中err = r.channel.QueueBind(q.Name,//在pub/sub模式下,这里的key要为空r.Key,r.Exchange,false,nil,)//消费消息message, err := r.channel.Consume(q.Name,"",true,false,false,false,nil,)forever := make(chan bool)go func() {for d := range message {log.Printf("Received a message:%s,", d.Body)}}()fmt.Println("退出请按 Ctrl+C")<-forever
}//简单模式Step:2、简单模式下生产代码
func (r *RabbitMQ) PublishSimple(message string) {//1、申请队列,如果队列存在就跳过,不存在创建//优点:保证队列存在,消息能发送到队列中_, err := r.channel.QueueDeclare(//队列名称r.QueueName,//是否持久化false,//是否为自动删除 当最后一个消费者断开连接之后,是否把消息从队列中删除false,//是否具有排他性 true表示自己可见 其他用户不能访问false,//是否阻塞 true表示要等待服务器的响应false,//额外数据nil,)if err != nil {fmt.Println(err)}//2.发送消息到队列中r.channel.Publish(//默认的Exchange交换机是default,类型是direct直接类型r.Exchange,//要赋值的队列名称r.QueueName,//如果为true,根据exchange类型和routkey规则,如果无法找到符合条件的队列那么会把发送的消息返回给发送者false,//如果为true,当exchange发送消息到队列后发现队列上没有绑定消费者,则会把消息还给发送者false,//消息amqp.Publishing{//类型ContentType: "text/plain",//消息Body: []byte(message),})
}func (r *RabbitMQ) ConsumeSimple() {//1、申请队列,如果队列存在就跳过,不存在创建//优点:保证队列存在,消息能发送到队列中_, err := r.channel.QueueDeclare(//队列名称r.QueueName,//是否持久化false,//是否为自动删除 当最后一个消费者断开连接之后,是否把消息从队列中删除false,//是否具有排他性false,//是否阻塞false,//额外数据nil,)if err != nil {fmt.Println(err)}//接收消息msgs, err := r.channel.Consume(r.QueueName,//用来区分多个消费者"",//是否自动应答true,//是否具有排他性false,//如果设置为true,表示不能同一个connection中发送的消息传递给这个connection中的消费者false,//队列是否阻塞false,nil,)if err != nil {fmt.Println(err)}forever := make(chan bool)//启用协程处理go func() {for d := range msgs {//实现我们要处理的逻辑函数log.Printf("Received a message:%s", d.Body)//fmt.Println(d.Body)}}()log.Printf("【*】warting for messages, To exit press CCTRAL+C")<-forever
}func (r *RabbitMQ) ConsumeWorker(consumerName string) {//1、申请队列,如果队列存在就跳过,不存在创建//优点:保证队列存在,消息能发送到队列中_, err := r.channel.QueueDeclare(//队列名称r.QueueName,//是否持久化false,//是否为自动删除 当最后一个消费者断开连接之后,是否把消息从队列中删除false,//是否具有排他性false,//是否阻塞false,//额外数据nil,)if err != nil {fmt.Println(err)}//接收消息msgs, err := r.channel.Consume(r.QueueName,//用来区分多个消费者consumerName,//是否自动应答true,//是否具有排他性false,//如果设置为true,表示不能同一个connection中发送的消息传递给这个connection中的消费者false,//队列是否阻塞false,nil,)if err != nil {fmt.Println(err)}forever := make(chan bool)//启用协程处理go func() {for d := range msgs {//实现我们要处理的逻辑函数log.Printf("%s Received a message:%s", consumerName, d.Body)//fmt.Println(d.Body)}}()log.Printf("【*】warting for messages, To exit press CCTRAL+C")<-forever
}
②测试代码
1. simple简单模式
consumer.go
func main() {//消费者rabbitmq := RabbitMQ.NewRabbitMQSimple("ziyiSimple")rabbitmq.ConsumeSimple()
}
producer.go
func main() {//Simple模式 生产者rabbitmq := RabbitMQ.NewRabbitMQSimple("ziyiSimple")for i := 0; i < 5; i++ {time.Sleep(time.Second * 2)rabbitmq.PublishSimple(fmt.Sprintf("%s %d", "hello", i))}
}
2. worker模式
consumer.go
func main() {/*worker模式无非就是多个消费者去同一个队列中消费消息*///消费者1rabbitmq1 := RabbitMQ.NewRabbitMQSimple("ziyiWorker")go rabbitmq1.ConsumeWorker("consumer1")//消费者2rabbitmq2 := RabbitMQ.NewRabbitMQSimple("ziyiWorker")rabbitmq2.ConsumeWorker("consumer2")
}
producer.go
func main() {//Worker模式 生产者rabbitmq := RabbitMQ.NewRabbitMQSimple("ziyiWorker")for i := 0; i < 100; i++ {//time.Sleep(time.Second * 2)rabbitmq.PublishSimple(fmt.Sprintf("%s %d", "hello", i))}
}
3. publish/subscribe模式
consumer.go:
func main() {//消费者rabbitmq := RabbitMQ.NewRabbitMQPubSub("" + "newProduct")rabbitmq.RecieveSub()
}
producer.go
func main() {//订阅模式发送者rabbitmq := RabbitMQ.NewRabbitMQPubSub("" + "newProduct")for i := 0; i <= 20; i++ {rabbitmq.PublishPub("订阅模式生产第" + strconv.Itoa(i) + "条数据")fmt.Println(i)time.Sleep(1 * time.Second)}
}
4. router模式
consumer.go
func main() {//消费者rabbitmq := RabbitMQ.NewRabbitMQRouting("exZi", "imooc_one")rabbitmq.RecieveRouting()
}
producer.go
func main() {//路由模式生产者imoocOne := RabbitMQ.NewRabbitMQRouting("exZi", "imooc_one")imoocTwo := RabbitMQ.NewRabbitMQRouting("exZi", "imooc_two")for i := 0; i <= 10; i++ {imoocOne.PublishRouting("hello imooc one!" + strconv.Itoa(i))imoocTwo.PublishRouting("hello imooc two!" + strconv.Itoa(i))time.Sleep(1 * time.Second)fmt.Println(i)}
}
5. topic模式
consumer.go
func main() {/*星号井号代表通配符星号代表多个单词,井号代表一个单词路由功能添加模糊匹配消息产生者产生消息,把消息交给交换机交换机根据key的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费*///Topic消费者//rabbitmq := RabbitMQ.NewRabbitMQTopic("exImoocTopic", "#") //匹配所有的key:topic88和topic99rabbitmq := RabbitMQ.NewRabbitMQTopic("exImoocTopic", "imooc.topic88.three") //只匹配topic88的rabbitmq.RecieveTopic()
}
producer.go
func main() {//Topic模式生产者imoocOne := RabbitMQ.NewRabbitMQTopic("exImoocTopic", "imooc.topic88.three")imoocTwo := RabbitMQ.NewRabbitMQTopic("exImoocTopic", "imooc.topic99.four")for i := 0; i <= 10; i++ {imoocOne.PublishTopic("hello imooc topic three!" + strconv.Itoa(i))imoocTwo.PublishTopic("hello imooc topic four!" + strconv.Itoa(i))time.Sleep(1 * time.Second)fmt.Println(i)}
}
2 Kafka
2.1 基本概念
Kafka是分布式的,其所有的构件borker(server服务端集群)、producer(消息生产)、consumer(消息消费者)都可以是分布式的。
producer给broker发送数据,这些消息会存到kafka server里,然后consumer再向kafka server发起请求去消费这些数据。
kafka server在这个过程中像是一个帮你保管数据的中间商。所以kafka服务器也可以叫做broker(broker直接翻译可以是中间人或者经纪人的意思)。
在消息的生产时可以使用一个标识topic来区分,且可以进行分区;每一个分区都是一个顺序的、不可变的消息队列, 并且可以持续的添加。
同时为发布和订阅提供高吞吐量。据了解,Kafka每秒可以生产约25万消息(50 MB),每秒处理55万消息(110 MB)。
消息被处理的状态是在consumer端维护,而不是由server端维护。当失败时能自动平衡
参考:https://blog.csdn.net/lingfy1234/article/details/122900348
- 应用场景
- 监控
- 消息队列
- 流处理
- 日志聚合
- 持久性日志
- 基础概念
- topic:话题
- broker:kafka服务集群,已发布的消息保存在一组服务器中,称之为kafka集群。集群中的每一个服务器都是一个代理(broker)
- partition:分区,topic物理上的分组
- message:消息,每个producer可以向一个topic主题发布一些消息
1.⽣产者从Kafka集群获取分区leader信息
2.⽣产者将消息发送给leader
3.leader将消息写入本地磁盘
4.follower从leader拉取消息数据
5.follower将消息写入本地磁盘后向leader发送ACK
6.leader收到所有的follower的ACK之后向生产者发送ACK
2.2 常见模式
①点对点模式:火车站出租车抢客
发送者将消息发送到消息队列中,消费者去消费,如果消费者有多个,他们会竞争地消费,也就是说对于某一条消息,只有一个消费者能“抢“到它。类似于火车站门口的出租车抢客的场景。
②发布订阅模式:组间无竞争,组内有竞争
消费者订阅对应的topic(主题),只有订阅了对应topic消费者的才会接收到消息。
例如:
- 牛奶有很多种,光明牛奶,希望牛奶等,只有你订阅了光明牛奶,送奶工才会把光明牛奶送到对应位置,你也才会有机会消费这个牛奶
注意
:为了提高消费者的消费能力,kafka中引入了消费者组的概念。相当于是:不同消费者组之间因为订阅的topic不同,不会有竞争关系。但是消费者组内是有竞争关系。
例如:
- 成都、厦门的出租车司机分别组成各自的消费者组。
- 成都的出租车司机只拉成都的人,厦门的只拉厦门的人。(因此他们两个消费者组不是竞争关系)
- 成都市内的出租车司机之间是竞争关系。(消费者组内是竞争关系)
2.3 docker-compose部署
vim docker-compose.yml
version: '3'
services:zookeeper:image: confluentinc/cp-zookeeper:6.2.0ports:- "2181:2181"environment:ZOOKEEPER_CLIENT_PORT: 2181ZOOKEEPER_TICK_TIME: 2000kafka:image: confluentinc/cp-kafka:6.2.0ports:- "9092:9092"environment:KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181#KAFKA_ADVERTISED_LISTENERS后面改为自己本地宿主机的ip,例如我本地mac的ip为192.168.0.101KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.0.101:9092KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1depends_on:- zookeeper
# 进入到docker-compose.yml所在目录,执行下面命令
docker-compose up -d
# 查看部署结果,状态为up表明部署成功
docker-compose ps
2.4 代码操作
# 1. 创建对应topic
docker-compose exec kafka kafka-topics --create --topic test-topic --partitions 1 --replication-factor 1 --bootstrap-server 192.168.0.101:9092# 2. 查看topic列表
docker-compose exec kafka kafka-topics --list --zookeeper zookeeper:2181
①producer.go
package mainimport ("fmt""github.com/IBM/sarama"
)// 基于sarama第三方库开发的kafka clientfunc main() {config := sarama.NewConfig()config.Producer.RequiredAcks = sarama.WaitForAll // 发送完数据需要leader和follow都确认config.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partitionconfig.Producer.Return.Successes = true // 成功交付的消息将在success channel返回// 构造一个消息msg := &sarama.ProducerMessage{}msg.Topic = "web_log"msg.Value = sarama.StringEncoder("this is a test log")// 连接kafkaclient, err := sarama.NewSyncProducer([]string{"localhost:9092"}, config)if err != nil {fmt.Println("producer closed, err:", err)return}defer client.Close()// 发送消息pid, offset, err := client.SendMessage(msg)if err != nil {fmt.Println("send msg failed, err:", err)return}fmt.Printf("pid:%v offset:%v\n", pid, offset)
}
②consumer.go
package mainimport ("fmt""github.com/IBM/sarama"
)// kafka consumerfunc main() {consumer, err := sarama.NewConsumer([]string{"localhost:9092"}, nil)if err != nil {fmt.Printf("fail to start consumer, err:%v\n", err)return}partitionList, err := consumer.Partitions("web_log") // 根据topic取到所有的分区if err != nil {fmt.Printf("fail to get list of partition:err%v\n", err)return}fmt.Println(partitionList)for partition := range partitionList { // 遍历所有的分区// 针对每个分区创建一个对应的分区消费者pc, err := consumer.ConsumePartition("web_log", int32(partition), sarama.OffsetNewest)if err != nil {fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err)return}defer pc.AsyncClose()// 异步从每个分区消费信息go func(sarama.PartitionConsumer) {for msg := range pc.Messages() {fmt.Printf("Partition:%d Offset:%d Key:%v Value:%v", msg.Partition, msg.Offset, msg.Key, string(msg.Value))}}(pc)}//演示时使用select {}
}
③运行效果
相关文章:

Go操作各大消息队列教程(RabbitMQ、Kafka)
Go操作各大消息队列教程 1 RabbitMQ 1.1 概念 ①基本名词 当前市面上mq的产品很多,比如RabbitMQ、Kafka、ActiveMQ、ZeroMQ和阿里巴巴捐献给Apache的RocketMQ。甚至连redis这种NoSQL都支持MQ的功能。 Broker:表示消息队列服务实体Virtual Host&#x…...

对话出海企业:2023亚马逊云科技出海日圆桌论坛
在全球经济亟待复苏的今天,持续对外开放是中国未来经济发展重要的“两条腿”之一。在愈发饱和的国内市场,中国企业需要对外寻找全新机遇才能在未来不确定的市场博弈下生存下去。“出海”,也成为近几年最炙手可热的词汇之一,大量中…...

【图解算法数据结构】分治算法篇 + Java代码实现
文章目录 一、重建二叉树二、数值的整数次方三、打印从 1 到最大的 n 位数四、二叉搜索树的后序遍历序列五、数组中的逆序对 一、重建二叉树 public class Solution {int[] preorder;HashMap<Integer, Integer> dic new HashMap<>();public TreeNode buildTree(in…...

Ubuntu系统环境搭建(八)——Ubuntu开机自动执行命令
ubuntu环境搭建专栏🔗点击跳转 Ubuntu系统环境搭建(八)——Ubuntu开机自动执行命令 修改文件 vim /etc/rc.local以自启动mysql为例,在文件末尾添加 /usr/local/mysql8/bin/mysqld_safe --defaults-file/usr/local/etc/my.cnf …...

c++(8.29)auto关键字,lambda表达式,数据类型转换,标准模板库,list,文件操作+Xmind
作业: 封装一个学生的类,定义一个学生这样类的vector容器, 里面存放学生对象(至少3个) 再把该容器中的对象,保存到文件中。 再把这些学生从文件中读取出来,放入另一个容器中并且遍历输出该容器里的学生。…...

Docker学习笔记(持续更新)
Docker学习目录 1.基础1.1 Docker简介1.1.1 Why Docker?1.1.2 Docker理念1.1.3 容器与虚拟机1.1.4 Docker能做什么? 1.2 Docker的基本组成1.2.1 Docker的三要素1.2.2 Docker平台架构 1.基础 1.1 Docker简介 1.1.1 Why Docker? 在个人笔记本…...

无涯教程-Android - 应用组件
应用程序组件是Android应用程序的基本组成部分,这些组件需要在应用程序清单文件 AndroidManifest.xml 注册,该文件描述了应用程序的每个组件以及它们如何交互。 Android应用程序可以使用以下四个主要组件- Sr.NoComponents & 描述1 Activities 它们…...

树与图c++
1.树 前言 本文主要介绍的数据结构之树型结构的相关知识,树型数据结构是面试官面试的时候非常喜欢考的一种数据结构,树形结构的遍历也是大厂笔试非常喜欢设置的考点,这些内容都会在本篇文章中进行详细的介绍,并且还会介绍一些常…...

中小企业常用的 IT 项目管理软件有哪些?
越热门,越贵的IT项目管理软件越好用吗?对于预算有限的中小型企业来说,如何选择一款适合自己的项目管理工具着实是个头疼的问题。 首先适用于中小型企业使用的 IT 项目管理软件需要具备哪些特点呢? 1、简单易用:中小企…...

汇编原理计算方法:物理地址=段地址*16+偏移地址
文章目录 计算方法计算错误分析 计算方法 根据进制的不同选择不同的计算方法 注意:物理地址、段地址和偏移地址的进制统一,要么都是二进制,要么都是十六进制,一般而言多是十六进制 若是二进制表达,则将段地址左移四…...

jdk-8u371-linux-x64.tar.gz jdk-8u371-windows-x64.exe 【jdk-8u371】 全平台下载
jdk-8u371 全平台下载 jdk-8u371-windows-x64.exejdk-8u371-linux-x64.rpmjdk-8u371-linux-x64.tar.gzjdk-8u371-macosx-x64.dmgjdk-8u371-linux-aarch64.tar.gz 下载地址 迅雷云盘 链接:https://pan.xunlei.com/s/VNdLL3FtCnh45nIBHulh_MDjA1?pwdw4s6 百度…...

数据结构体--5.0图
目录 一、定义 二、图的顶点与边之间的关系 三、图的顶点与边之间的关系 四、连通图 五、连通图的生成树定义 一、定义 图(Graph)是由顶点的又穷非空集合合顶点之间边的集合组成,通常表示为:G(V,E&…...

深入剖析 Golang 程序启动原理 - 从 ELF 入口点到GMP初始化到执行 main!
大家好,我是飞哥! 在过去的开发工作中,大家都是通过创建进程或者线程来工作的。Linux进程是如何创建出来的? 、聊聊Linux中线程和进程的联系与区别! 和你的新进程是如何被内核调度执行到的? 这几篇文章就是…...

C语言——多文件编程
多文件编程 把函数声明放在头文件xxx.h中,在主函数中包含相应头文件在头文件对应的xxx.c中实现xxx.h声明的函数 防止头文件重复包含 当一个项目比较大时,往往都是分文件,这时候有可能不小心把同一个头文件 include 多次,或者头…...

Git学习part1
02.尚硅谷_Git&GitHub_为什么要使用版本控制_哔哩哔哩_bilibili 1.Git必要性 记录代码开发的历史状态 ,允许很多人同时修改文件(分布式)且不会丢失记录 2.版本控制工具应该具备的功能 1)协同修改 多人并行不悖的修改服务器端…...

2309C++均为某个类型
#include <常用> 构 A{空 f(){打印("啊");} }; 元<类 T>构 是点啊:假型{}; 元<>构 是点啊<A>:真型{}; 元<类 T>概念 是呀是点啊<T>::值;元<是呀...T>空 f(T&...t){(t.f(),...); }//均为元<类...T>要求 均为值&l…...

2023年打脸面试官之TCP--瞬间就懂
1.TCP 三次握手之为什么要三次呢?事不过三? 过程如下图: 先来解释下上述的各个标志的含义 序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节…...

设计模式-单例模式Singleton
单例模式 单例模式 (Singleton) (重点)1) 为什么要使用单例2) 如何实现一个单例2.a) 饿汉式2.b) 懒汉式2.c) 双重检查锁2.d) 静态内部类2.e) 枚举类2.f) 反射入侵2.g) 序列化与反序列化安全 3) 单例存在的问题3.a) 无法支持面向对象编程 单例模式 (Singleton) (重点) 一个类只…...

PPPoE连接无法建立的排查和修复
嗨,亲爱的读者朋友们!你是否曾经遇到过PPPoE连接无法建立的问题?今天我将为你详细解析排查和修复这个问题的步骤。 检查物理连接 首先,我们需要确保物理连接没有问题。请按照以下步骤进行检查: - 检查网线是否插好&…...

QT 发布软件基本操作
一、配置环境变量 找到Qt安装时的bin目录的路径:D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin,将目录拷贝至下述环境变量中。 打开计算机的高级系统设置 选中环境变量-->系统变量-->Path 点击编辑-->新建-->粘贴 二、生成发布软件的可执行程序 …...

CTFhub-SSRF-内网访问
CTFHub 环境实例 | 提示信息 http://challenge-8bf41c5c86a8c5f4.sandbox.ctfhub.com:10800/?url_ 根据提示,在url 后门添加 127.0.0.1/flag.php http://challenge-8bf41c5c86a8c5f4.sandbox.ctfhub.com:10800/?url127.0.0.1/flag.php ctfhub{a6bb51530c8f6be0…...

Cenos7安装小火车程序动画
运维Shell脚本小试牛刀(一) 运维Shell脚本小试牛刀(二) 运维Shell脚本小试牛刀(三)::$(cd $(dirname $0); pwd)命令详解 运维Shell脚本小试牛刀(四): 多层嵌套if...elif...elif....else fi_蜗牛杨哥的博客-CSDN博客 Cenos7安装小火车程序动画 一:替换…...

Node 执行命令时传参 process.argv
process 对象是一个全局变量,提供当前 Node.js 进程的有关信息,以及控制当前 Node.js 进程。 因为是全局变量,所以无需使用 require()。 process.argv 属性返回一个数组,这个数组包含了启动Node.js进程时的命令行参数,…...

【Vue】快速上手--Vue 3.0
什么是 Vue? Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的…...

PyTorch深度学习遥感影像地物分类与目标检测、分割及遥感影像问题深度学习优化实践技术应用
我国高分辨率对地观测系统重大专项已全面启动,高空间、高光谱、高时间分辨率和宽地面覆盖于一体的全球天空地一体化立体对地观测网逐步形成,将成为保障国家安全的基础性和战略性资源。未来10年全球每天获取的观测数据将超过10PB,遥感大数据时…...

04、添加 com.fasterxml.jackson.dataformat -- jackson-dataformat-xml 依赖报错
Correct the classpath of your application so that it contains a single, compatible version of com.fasterxml.jackson.dataformat.xml.XmlMapper 解决: 改用其他版本,我没写版本号,springboot自己默认的是 2.11.4 版本 成功启动项目…...

禅道项目管理系统 - 操作使用 (2023版)
1. 部门-用户-权限 新增部门 新增用户 设置权限 2. 项目集创建 项目集 - 添加项目集 3. 产品线创建 产品 - 产品线 4. 产品创建 产品 - 产品列表 - 添加产品 5. 产品计划创建 产品 - xx产品 - 计划 - 创建计划 我这里创建3个计划 (一期, 二期, 三期) 6. 研发需求 - 创建模块…...

C++的多重继承
派生类都只有一个基类,称为单继承(Single Inheritance)。除此之外,C++也支持多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。 多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承。 …...

ZooKeeper与Paxos
Apache ZooKeeper是由Apache Hadoop的子项目发展而来,于2010年11月正式成为了Apache的顶级项目。ZooKeeper为分布式应用提供了高效且可靠的分布式协调服务,提供了诸如统一命名服务、配置管理和分布式锁等分布式的基础服务。在解决分布式数据一致性方面&a…...

Cargo 静态编译
git clone --recursive https://github.com/kornelski/pngquant.git vi ~/.cargo/config.toml[http] debug true proxy "127.0.0.1:1080" 1.apt 更新 2.apt install cargo 3.修改源码的Cargo.toml [source.crates-io] #registry "https://code.aliyun.com…...