Go操作RabbitMQ_go调用rabbitmq,2024年最新大数据开发开发面试题及答案

package mq

import (
“fmt”
“log”

“github.com/streadway/amqp”
)

// MQURL amqp://user:password@host:port/vhost
// amqp://是固定参数,这个信息是固定不变的。后面两个是用户名密码ip地址端口号Virtual Host
// 如果vhost是“/”就输入/%2F,/%2F代表斜杠
const MQURL = “amqp://test:123123@127.0.0.1:5672/%2F”

// RabbitMQ rabbitMQ结构体
type RabbitMQ struct {
conn *amqp.Connection
channel *amqp.Channel
//队列名称
QueueName string
//交换机名称
Exchange string
//bind Key 名称
Key string
//连接信息
Mqurl string
}

// NewRabbitMQ 创建结构体实例
func NewRabbitMQ(queueName string, exchange string, key string) *RabbitMQ {
return &RabbitMQ{QueueName: queueName, Exchange: exchange, Key: key, Mqurl: MQURL}
}

// Destory 断开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))
}
}

// NewRabbitMQPub 创建Pub模式下RabbitMQ实例
func NewRabbitMQPub(exchangeName string) *RabbitMQ {
//创建RabbitMQ实例
rabbitmq := NewRabbitMQ(“”, exchangeName, “”)
var err error
//获取connection
rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)
rabbitmq.failOnErr(err, “failed to connect rabb”+
“itmq!”)
//获取channel
rabbitmq.channel, err = rabbitmq.conn.Channel()
rabbitmq.failOnErr(err, “failed to open a channel”)
return rabbitmq
}

// PublishPub Pub模式队列生产
func (r *RabbitMQ) PublishPub(message string) {
//1.尝试创建交换机
err := r.channel.ExchangeDeclare(
//交换机名称
r.Exchange,
//交换机类型
“fanout”,
//是否持久化
true,
//是否自动删除
false,
//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
//是否阻塞处理
false,
//其他属性
nil,
)
r.failOnErr(err, “Failed to declare an exchange”)

//2.发送消息
//发送消息
//参数:
//1.交换机
//2.队列,
//3.是否强制性路由到队列,如果为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还
//给发送者
//4.是否立即发送
//5.其他属性
//6.发送消息的内容
//调用channel 发送消息到队列中
r.channel.Publish(
r.Exchange,
“”,
//如果为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还给发送者
false,
//如果为true,当exchange发送消息到队列后发现队列上没有消费者,则会把消息返还给发送者
false,
amqp.Publishing{
ContentType: “text/plain”,
Body: []byte(message),
})
}

// ConsumePub pub 模式下消费者
func (r *RabbitMQ) ConsumePub() {
//1.试探性创建交换机
err := r.channel.ExchangeDeclare(
// 交换机名称
r.Exchange,
//交换机类型
“fanout”,
//是否持久化
true,
// 是否自动删除
false,
//YES表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
//
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.Exchange,
false,
nil)

//消费消息
messages, err := r.channel.Consume(
q.Name,
“”,
true,
false,
false,
false,
nil,
)

forever := make(chan bool)
//启用协程处理消息
go func() {
for d := range messages {
log.Printf(“Received a message: %s”, d.Body)
}
}()
fmt.Println(“退出请按 CTRL+C\n”)
<-forever

}

3.2.1.2. mainPubPublish.go

package main

import (
“fmt”
“qiutian.com/study/02.rabbitmq/Pub/mq”
“strconv”
“time”
)

func main() {
rabbitmq := mq.NewRabbitMQPub(“newExchange”)
for i := 0; i < 100; i++ {
rabbitmq.PublishPub(“订阅模式生产第” +
strconv.Itoa(i) + “条” + “数据”)
fmt.Println(“订阅模式生产第” +
strconv.Itoa(i) + “条” + “数据”)
time.Sleep(1 * time.Second)
}
}

3.2.1.3. mainPubRecieve1.go

package main

import (
“qiutian.com/study/02.rabbitmq/Pub/mq”
)

func main() {
rabbitmq := mq.NewRabbitMQPub(“” +
“newExchange”)
rabbitmq.ConsumePub()
}

3.2.1.4. mainPubRecieve2.go

package main

import (
“qiutian.com/study/02.rabbitmq/Pub/mq”
)

func main() {
rabbitmq := mq.NewRabbitMQPub(“” +
“newExchange”)
rabbitmq.ConsumePub()
}

4. 路由模式(routing)

路由模式(Routing Mode)是消息队列中一种常见的消息路由模式,它允许消息的发送者(Producer)将消息发送到指定的交换机(Exchange),而交换机则根据消息的路由键(Routing Key)将消息路由到符合条件的队列(Queue)。路由模式提供了一种灵活的消息路由机制,使得消息的分发可以根据不同的路由规则进行定制。

优点:

  1. 灵活性高: 路由模式允许根据消息的属性或条件将消息路由到不同的队列或消费者,从而实现灵活的消息处理和路由策略。
  2. 解耦性强: 路由模式可以将消息的生产者和消费者解耦,使它们之间相互独立。生产者只需将消息发送到交换机,而不需要知道具体的消费者是谁,消费者也只需订阅感兴趣的消息,而不需要知道消息的生产者是谁,从而实现了系统的解耦。
  3. 灵活的消息过滤和路由: 路由模式允许根据消息的属性、标签或主题将消息路由到不同的队列或消费者,实现精确的消息过滤和路由,从而提高了系统的灵活性和可扩展性。
  4. 支持多种路由策略: 路由模式支持多种路由策略,如直连路由、主题路由、分发路由等,可以根据不同的业务需求选择合适的路由策略,从而满足不同的应用场景。

缺点:

  1. 配置复杂: 路由模式的配置相对复杂,需要定义交换机、队列和绑定关系,以及配置路由规则等,如果配置不当可能会导致消息路由错误或丢失。
  2. 性能损耗: 路由模式需要对消息进行额外的路由和过滤操作,可能会增加系统的消息处理延迟和性能消耗,特别是在消息量较大的情况下。
  3. 消息堆积风险: 如果路由模式配置不当或消息处理能力不足,可能会导致消息堆积的风险,从而影响系统的稳定性和可靠性。
  4. 维护成本高: 路由模式需要维护交换机、队列和绑定关系等配置信息,特别是在系统规模较大或消息路由策略较复杂的情况下,维护成本可能会较高。

使用场景:

  1. 微服务架构下的消息路由:在微服务架构中,不同的服务可能需要处理不同类型的消息。通过路由模式,可以将消息根据服务的需求进行路由,确保每个服务只接收到与其相关的消息,从而实现解耦和灵活性。
  2. 实时监控与日志分析:在监控系统或日志分析系统中,需要根据不同的指标或日志级别对数据进行分类和分析。路由模式可以根据数据的属性或标签将数据路由到不同的处理节点,以实现实时监控和日志分析。
  3. 任务调度与负载均衡:在任务调度系统中,任务可能具有不同的优先级或类型,需要根据任务的属性进行分发和调度。通过路由模式,可以将任务路由到不同的工作节点,实现任务的负载均衡和优先级调度。
  4. 多级消息过滤与订阅:在订阅发布系统中,订阅者可能对不同类型或主题的消息感兴趣。路由模式可以根据消息的属性或主题将消息路由到不同的订阅者,实现精确的消息过滤和订阅。
  5. 数据同步与复制:在分布式系统中,可能需要将数据复制到不同的节点或数据中心,以实现数据的备份和灾备。通过路由模式,可以将数据根据不同的条件进行路由和复制,确保数据的一致性和可靠性。
4.1. 目录结构

4.2. 代码实现
4.2.1. rabbitmq.go

package mq

import (
“fmt”
“log”

“github.com/streadway/amqp”
)

// MQURL amqp://user:password@host:port/vhost
// amqp://是固定参数,这个信息是固定不变的。后面两个是用户名密码ip地址端口号Virtual Host
// 如果vhost是“/”就输入/%2F,/%2F代表斜杠
const MQURL = “amqp://test:123123@127.0.0.1:5672/%2F”

// RabbitMQ rabbitMQ结构体
type RabbitMQ struct {
conn *amqp.Connection
channel *amqp.Channel
//队列名称
QueueName string
//交换机名称
Exchange string
//bind Key 名称
Key string
//连接信息
Mqurl string
}

// NewRabbitMQ 创建结构体实例
func NewRabbitMQ(queueName string, exchange string, key string) *RabbitMQ {
return &RabbitMQ{QueueName: queueName, Exchange: exchange, Key: key, Mqurl: MQURL}
}

// Destory 断开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))
}
}

// NewRabbitMQRouting 路由模式 创建RabbitMQ实例
func NewRabbitMQRouting(exchangeName string, routingKey string) *RabbitMQ {
//创建RabbitMQ实例
rabbitmq := NewRabbitMQ(“”, exchangeName, routingKey)
var err error
//获取connection
rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)
rabbitmq.failOnErr(err, “failed to connect rabbitmq!”)
//获取channel
rabbitmq.channel, err = rabbitmq.conn.Channel()
rabbitmq.failOnErr(err, “failed to open a channel”)
return rabbitmq
}

// PublishRouting 路由模式发送消息
func (r *RabbitMQ) PublishRouting(message string) {
//1.尝试创建交换机
err := r.channel.ExchangeDeclare(
//交换机名称
r.Exchange,
//交换机类型
“direct”,
//是否持久化
true,
//是否自动删除
false,
//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
//是否阻塞处理
false,
//其他属性
nil,
)
r.failOnErr(err, “Failed to declare an exchange”)

//2.发送消息
//发送消息
//参数:
//1.交换机
//2.队列,
//3.是否强制性路由到队列,如果为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还
//给发送者
//4.是否立即发送
//5.其他属性
//6.发送消息的内容
//调用channel 发送消息到队列中
r.channel.Publish(
r.Exchange,
r.Key,
//如果为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还给发送者
false,
//如果为true,当exchange发送消息到队列后发现队列上没有消费者,则会把消息返还给发送者
false,
amqp.Publishing{
ContentType: “text/plain”,
Body: []byte(message),
})
}

// ConsumeRouting routing 模式下消费者
func (r *RabbitMQ) ConsumeRouting() {
//1.试探性创建交换机
err := r.channel.ExchangeDeclare(
// 交换机名称
r.Exchange,
//交换机类型
“direct”,
//是否持久化
true,
// 是否自动删除
false,
//YES表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
//
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,
// routing模式下要指定key
r.Key,
r.Exchange,
false,
nil)

//消费消息
messages, err := r.channel.Consume(
q.Name,
“”,
true,
false,
false,
false,
nil,
)

forever := make(chan bool)
//启用协程处理消息
go func() {
for d := range messages {
log.Printf(“Received a message: %s”, d.Body)
}
}()
fmt.Println(“退出请按 CTRL+C\n”)
<-forever

}

4.2.2. mainRoutingPublish.go

package main

import (
“fmt”
“qiutian.com/study/02.rabbitmq/Routing/mq”
“strconv”
“time”
)

func main() {

routingOne := mq.NewRabbitMQRouting(“newRoutingExchange”, “newRoutingKey_one”)
routingTwo := mq.NewRabbitMQRouting(“newRoutingExchange”, “newRoutingKey_two”)

for i := 0; i < 100; i++ {
if i%2 == 0 {
routingOne.PublishRouting("Hello World! " + strconv.Itoa(i))
} else {
routingTwo.PublishRouting("Hello World! " + strconv.Itoa(i))
}
time.Sleep(time.Second)
fmt.Println(i)
}
}

4.2.3. mianRoutingRecieve1.go

package main

import (
“qiutian.com/study/02.rabbitmq/Routing/mq”
)

func main() {
routingOne := mq.NewRabbitMQRouting(“newRoutingExchange”, “newRoutingKey_one”)
routingOne.ConsumeRouting()
}

4.2.4. mianRoutingRecieve2.go

package main

import (
“qiutian.com/study/02.rabbitmq/Routing/mq”
)

func main() {
routingTwo := mq.NewRabbitMQRouting(“newRoutingExchange”, “newRoutingKey_two”)
routingTwo.ConsumeRouting()
}

5. 话题模式(Topic)

红色Queue:绑定的是usa.#,因此凡是以usa.开头的routing key都会被匹配到

黄色Queue:绑定的是#.news,因此凡是以.news结尾的routing key都会被匹配

话题模式(Topic Mode)是消息队列中一种高级的消息路由模式,它基于消息的主题(Topic)进行消息的订阅和分发。在话题模式中,消息的发送者(Producer)将消息发送到特定的主题,而消息的接收者(Consumer)则根据自己感兴趣的主题进行订阅,从而接收相关的消息。

工作原理:

  1. 主题定义: 在话题模式中,主题由一个或多个单词组成,每个单词之间用点号(.)分隔,例如:stock.usd.nyse
  2. 通配符匹配: 话题模式支持两种通配符,分别是***(星号)和#**(井号)。
    • *:表示匹配一个单词,例如:stock.*.nyse可以匹配stock.usd.nysestock.eur.nyse等。
  • #:表示匹配零个或多个单词,例如:stock.usd.#可以匹配stock.usd.nysestock.usd.nasdaq以及stock.usd等。
  1. 消息路由: 消息的发送者在发送消息时指定一个主题,而消息的接收者则可以使用通配符来订阅感兴趣的主题。消息队列根据主题的匹配规则将消息路由到符合条件的订阅者。

应用场景:

  1. 多级消息过滤: 话题模式可以根据消息的主题进行多级的消息过滤和匹配,从而实现精确的消息路由和分发。
  2. 发布/订阅系统: 话题模式常用于发布/订阅系统中,其中消息的发送者作为发布者,向不同的主题发送消息,而消息的接收者作为订阅者,根据自己的需求订阅感兴趣的主题。
  3. 事件驱动架构: 话题模式可以用于构建事件驱动的架构,其中各个服务之间通过消息队列进行事件的发布和订阅,从而实现服务之间的解耦和灵活的消息传递。

优点:

  • 灵活性高: 话题模式支持灵活的主题定义和通配符匹配,可以根据不同的业务需求实现精确的消息路由和分发。
  • 解耦性强: 话题模式可以将消息的生产者和消费者解耦,使它们之间相互独立,从而提高系统的可维护性和可扩展性。

缺点:

  • 配置复杂: 话题模式的配置相对复杂,需要定义主题和通配符规则,并确保发送者和接收者之间的匹配规则一致,否则可能导致消息路由错误或丢失。
  • 性能损耗: 话题模式需要对消息进行额外的匹配和路由操作,可能会增加系统的消息处理延迟和性能消耗,特别是在消息量较大的情况下
5.1. 目录结构

5.2. 代码实现
5.2.1. rabbitmq.go

package mq

import (
“fmt”
“log”

“github.com/streadway/amqp”
)

// MQURL amqp://user:password@host:port/vhost
// amqp://是固定参数,这个信息是固定不变的。后面两个是用户名密码ip地址端口号Virtual Host
// 如果vhost是“/”就输入/%2F,/%2F代表斜杠
const MQURL = “amqp://test:123123@127.0.0.1:5672/%2F”

// RabbitMQ rabbitMQ结构体
type RabbitMQ struct {
conn *amqp.Connection
channel *amqp.Channel
//队列名称
QueueName string
//交换机名称
Exchange string
//bind Key 名称
Key string
//连接信息
Mqurl string
}

// NewRabbitMQ 创建结构体实例
func NewRabbitMQ(queueName string, exchange string, key string) *RabbitMQ {
return &RabbitMQ{QueueName: queueName, Exchange: exchange, Key: key, Mqurl: MQURL}
}

// Destory 断开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))
}
}

// NewRabbitMQTopic 话题模式 创建RabbitMQ实例
func NewRabbitMQTopic(exchangeName string, routingKey string) *RabbitMQ {
//创建RabbitMQ实例
rabbitmq := NewRabbitMQ(“”, exchangeName, routingKey)
var err error
//获取connection
rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)
rabbitmq.failOnErr(err, “failed to connect rabbitmq!”)
//获取channel
rabbitmq.channel, err = rabbitmq.conn.Channel()
rabbitmq.failOnErr(err, “failed to open a channel”)
return rabbitmq
}

// PublishTopic 话题模式发送消息
func (r *RabbitMQ) PublishTopic(message string) {
//1.尝试创建交换机
err := r.channel.ExchangeDeclare(
//交换机名称
r.Exchange,
//交换机类型
“topic”,
//是否持久化
true,
//是否自动删除
false,
//true表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
//是否阻塞处理
false,
//其他属性
nil,
)
r.failOnErr(err, “Failed to declare an exchange”)

//2.发送消息
//发送消息
//参数:
//1.交换机
//2.队列,
//3.是否强制性路由到队列,如果为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还
//给发送者
//4.是否立即发送
//5.其他属性
//6.发送消息的内容
//调用channel 发送消息到队列中
r.channel.Publish(
r.Exchange,
r.Key,
//如果为true,根据自身exchange类型和routekey规则无法找到符合条件的队列会把消息返还给发送者
false,
//如果为true,当exchange发送消息到队列后发现队列上没有消费者,则会把消息返还给发送者
false,
amqp.Publishing{
ContentType: “text/plain”,
Body: []byte(message),
})
}

// ConsumeTopic topic 模式下消费者
func (r *RabbitMQ) ConsumeTopic() {
//1.试探性创建交换机
err := r.channel.ExchangeDeclare(
// 交换机名称
r.Exchange,
//交换机类型
“topic”,
//是否持久化
true,
// 是否自动删除
false,
//YES表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
//
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,
// topic模式下要指定key
r.Key,
r.Exchange,
false,
nil)

//消费消息
messages, err := r.channel.Consume(
q.Name,
“”,
true,
false,
false,
false,
nil,
)

forever := make(chan bool)
//启用协程处理消息
go func() {
for d := range messages {
log.Printf(“Received a message: %s”, d.Body)
}
}()
fmt.Println(“退出请按 CTRL+C\n”)
<-forever

}

5.2.2. mainTopicPublish.go

package main

import (
“fmt”
“qiutian.com/study/02.rabbitmq/Topic/mq”
“strconv”
“time”
)

func main() {

topicOne := mq.NewRabbitMQTopic(“newTopicExchange”, “newTopicKey.one”)
topicTwo := mq.NewRabbitMQTopic(“newTopicExchange”, “newTopicKey.two”)

for i := 0; i < 100; i++ {
// 发布消息
// 参数:交换机,路由键,消息
topicOne.PublishTopic("Hello World! " + strconv.Itoa(i))
topicTwo.PublishTopic("Hello World! " + strconv.Itoa(i))
// 模拟耗时操作
time.Sleep(time.Second)
fmt.Println(i)
}
}

5.2.3. mainTopicRecieve1.go

package main

import (
“qiutian.com/study/02.rabbitmq/Topic/mq”
)

func main() {
topicOne := mq.NewRabbitMQTopic(“newTopicExchange”, “#”)
topicOne.ConsumeTopic()
}

5.2.4. mainTopicRecieve2.go

package main

import (
“qiutian.com/study/02.rabbitmq/Topic/mq”
)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
img

opicExchange", “#”)
topicOne.ConsumeTopic()
}

5.2.4. mainTopicRecieve2.go

package main

import (
“qiutian.com/study/02.rabbitmq/Topic/mq”
)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-ssRTZbAT-1712518612000)]
[外链图片转存中…(img-MLGtEts8-1712518612000)]
[外链图片转存中…(img-ffLsYljZ-1712518612001)]
[外链图片转存中…(img-Mm6GiMpS-1712518612001)]
[外链图片转存中…(img-oS1IOf4f-1712518612001)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
[外链图片转存中…(img-NTobs3LX-1712518612002)]

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值