RabbitMQ详解(从入门到实战)

一、RabbitMQ是什么?

1. 一句话解释

RabbitMQ是开源的消息中间件 ,就像邮局系统

  • 生产者是寄信人

  • 消费者是收信人

  • RabbitMQ是邮局

  • 交换器是分拣中心

  • 队列是邮箱

2. 为什么需要RabbitMQ?

复制代码
// 没有消息队列的系统
class 传统订单系统 {
    public void 创建订单() {
        1. 保存订单到数据库
        2. 发短信通知用户
        3. 扣减库存
        4. 记录日志
        5. 更新推荐系统
        // 问题:一个失败,整个回滚!
        // 问题:系统紧耦合!
    }
}

// 有RabbitMQ的系统
class 现代订单系统 {
    public void 创建订单() {
        1. 保存订单
        2. 发消息到RabbitMQ:"订单已创建"
        3. 立即返回成功
        
        // RabbitMQ负责通知:
        // 短信服务:收到消息 → 发短信
        // 库存服务:收到消息 → 扣库存
        // 日志服务:收到消息 → 记日志
        // 各自处理,互不影响
    }
}

二、核心概念(必须理解!)

1. 六大核心组件

复制代码
生产者(Producer)→ 交换器(Exchange)→ 队列(Queue)→ 消费者(Consumer)
       │                       │                     │
      消息(Message)          绑定(Binding)        通道(Channel)

2. 详细解释每个组件

java 复制代码
// 1. 消息(Message)
消息 = {
    headers: {},      // 头部信息
    properties: {},   // 属性(优先级、持久化等)
    body: "内容"      // 消息体
}

// 2. 生产者(Producer)
class 生产者 {
    public void 发送消息() {
        // 创建消息
        消息 msg = new 消息("订单1001创建");
        
        // 发送到交换器
        channel.basicPublish(
            "订单交换器",  // 交换器
            "订单.创建",   // 路由键
            null,        // 属性
            msg.getBytes()  // 消息体
        );
    }
}

// 3. 交换器(Exchange)- 最重要!
交换器是"邮局的分拣中心"
接收生产者消息,根据规则路由到队列

// 4. 队列(Queue)
队列是"邮箱"
存储消息,等待消费者取走

// 5. 消费者(Consumer)
class 消费者 {
    public void 消费消息() {
        // 从队列取消息
        消息 msg = channel.basicGet("订单队列");
        
        // 处理消息
        处理订单(msg);
        
        // 确认消费完成
        channel.basicAck(msg);
    }
}

// 6. 绑定(Binding)
绑定是"分拣规则"
告诉交换器:什么样的消息放到哪个队列

三、四种交换器类型(核心!)

1. Direct Exchange(直连交换器)- 精确匹配

复制代码
比喻:快递员送快递,必须门牌号完全匹配

生产者 → Direct Exchange → 队列
                    ↓
                根据 Routing Key 精确匹配
                
例子:
消息1: Routing Key = "订单.创建" → 队列1
消息2: Routing Key = "订单.支付" → 队列2
消息3: Routing Key = "订单.创建" → 队列1

代码示例

java 复制代码
// 1. 声明Direct交换器
channel.exchangeDeclare(
    "订单交换器",  // 交换器名
    "direct",     // 类型
    true          // 持久化
);

// 2. 绑定队列
channel.queueBind(
    "订单创建队列",  // 队列
    "订单交换器",   // 交换器  
    "订单.创建"     // Routing Key
);

channel.queueBind(
    "订单支付队列",
    "订单交换器",
    "订单.支付"
);

// 3. 发送消息
channel.basicPublish(
    "订单交换器",
    "订单.创建",  // 精确匹配
    null,
    "订单1001创建".getBytes()
);
// 结果:消息进入"订单创建队列"

2. Topic Exchange(主题交换器)- 模式匹配

复制代码
比喻:用通配符订阅感兴趣的话题
* 匹配一个单词
# 匹配零个或多个单词

生产者 → Topic Exchange → 队列
                    ↓
                根据模式匹配
                
例子:
队列1绑定键: "订单.*.创建"
  - 匹配: "订单.电商.创建" ✓
  - 匹配: "订单.外卖.创建" ✓
  - 不匹配: "订单.创建" ❌(少一个单词)

队列2绑定键: "订单.#"
  - 匹配: "订单.创建" ✓
  - 匹配: "订单.电商.创建" ✓
  - 匹配: "订单.电商.外卖.创建" ✓

代码示例

java 复制代码
// 1. 声明Topic交换器
channel.exchangeDeclare("日志交换器", "topic", true);

// 2. 绑定队列
// 错误日志队列
channel.queueBind("错误队列", "日志交换器", "*.error");

// 支付日志队列  
channel.queueBind("支付队列", "日志交换器", "payment.*");

// 用户行为队列
channel.queueBind("用户队列", "日志交换器", "user.#");

// 3. 发送消息
// 消息1:进入错误队列
channel.basicPublish("日志交换器", "payment.error", null, 
    "支付出错".getBytes());

// 消息2:进入支付队列
channel.basicPublish("日志交换器", "payment.success", null,
    "支付成功".getBytes());

// 消息3:进入用户队列
channel.basicPublish("日志交换器", "user.login", null,
    "用户登录".getBytes());

3. Fanout Exchange(扇出交换器)- 广播

java 复制代码
比喻:小区广播,所有人都能听到

生产者 → Fanout Exchange → 所有绑定队列
                     ↓
                  消息复制多份
                  
例子:
消息 → Fanout Exchange → 队列1
                      → 队列2
                      → 队列3
所有队列都收到同样的消息

代码示例

java 复制代码
// 1. 声明Fanout交换器
channel.exchangeDeclare("新闻交换器", "fanout", true);

// 2. 绑定队列(不需要Routing Key)
channel.queueBind("短信队列", "新闻交换器", "");  // 空字符串
channel.queueBind("邮件队列", "新闻交换器", "");
channel.queueBind("App队列", "新闻交换器", "");

// 3. 发送消息
channel.basicPublish("新闻交换器", "", null,  // Routing Key为空
    "系统升级通知".getBytes());

// 结果:三个队列都收到消息

4. Headers Exchange(头交换器)- 匹配Header

复制代码
根据消息的Header属性匹配
x-match: all(所有header必须匹配)
x-match: any(任意header匹配)

生产者 → Headers Exchange → 队列
                     ↓
                 根据Header匹配

代码示例

java 复制代码
// 1. 声明Headers交换器
channel.exchangeDeclare("header交换器", "headers", true);

// 2. 绑定队列(设置匹配规则)
Map<String, Object> 匹配条件 = new HashMap<>();
匹配条件.put("format", "pdf");
匹配条件.put("type", "report");
匹配条件.put("x-match", "all");  // 必须同时满足

channel.queueBind("报告队列", "header交换器", "", 匹配条件);

// 3. 发送消息(设置Header)
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .headers(匹配条件)
    .build();

channel.basicPublish("header交换器", "", props,
    "月度报告.pdf".getBytes());

四、Spring Boot整合实战

1. 添加依赖

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 配置文件

复制代码
# application.yml
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    # 连接池配置
    connection-timeout: 3000
    template:
      retry:
        enabled: true
        max-attempts: 3
    # 确认机制
    publisher-confirm-type: correlated
    publisher-returns: true
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认
        prefetch: 10  # 每次取多少条

3. 配置类

复制代码
@Configuration
public class RabbitMQConfig {
    
    // 1. 声明交换器
    @Bean
    public TopicExchange orderExchange() {
        return new TopicExchange("order.exchange", true, false);
    }
    
    @Bean
    public FanoutExchange logExchange() {
        return new FanoutExchange("log.exchange", true, false);
    }
    
    // 2. 声明队列
    @Bean
    public Queue orderCreateQueue() {
        return new Queue("order.create.queue", true);
    }
    
    @Bean
    public Queue orderPayQueue() {
        return new Queue("order.pay.queue", true);
    }
    
    @Bean
    public Queue smsQueue() {
        return new Queue("sms.queue", true);
    }
    
    // 3. 声明死信队列
    @Bean
    public Queue orderDeadLetterQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", "order.exchange");
        args.put("x-dead-letter-routing-key", "order.dead");
        args.put("x-message-ttl", 10000);  // 10秒过期
        return new Queue("order.dlx.queue", true, false, false, args);
    }
    
    // 4. 绑定队列到交换器
    @Bean
    public Binding orderCreateBinding() {
        return BindingBuilder.bind(orderCreateQueue())
            .to(orderExchange())
            .with("order.create");
    }
    
    @Bean
    public Binding orderPayBinding() {
        return BindingBuilder.bind(orderPayQueue())
            .to(orderExchange())
            .with("order.pay");
    }
    
    @Bean
    public Binding smsBinding() {
        return BindingBuilder.bind(smsQueue())
            .to(logExchange());
    }
}

4. 生产者

java 复制代码
@Component
@Slf4j
public class OrderProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    // 发送订单创建消息
    public void sendOrderCreate(Order order) {
        // 设置消息属性
        MessageProperties props = MessagePropertiesBuilder.newInstance()
            .setContentType(MessageProperties.CONTENT_TYPE_JSON)
            .setHeader("orderId", order.getId())
            .setExpiration("10000")  // 10秒过期
            .build();
        
        Message message = MessageBuilder
            .withPayload(order)
            .andProperties(props)
            .build();
        
        // 发送消息
        CorrelationData correlationData = new CorrelationData(order.getId());
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.create",
            message,
            correlationData
        );
        
        log.info("发送订单创建消息: {}", order.getId());
    }
    
    // 发送延迟消息
    public void sendDelayMessage(Order order, int delaySeconds) {
        MessageProperties props = MessagePropertiesBuilder.newInstance()
            .setExpiration(String.valueOf(delaySeconds * 1000))
            .build();
            
        Message message = MessageBuilder
            .withPayload(order)
            .andProperties(props)
            .build();
            
        // 发送到死信交换器
        rabbitTemplate.convertAndSend(
            "order.exchange.dlx",
            "order.delay",
            message
        );
    }
}

5. 消费者

java 复制代码
@Component
@Slf4j
public class OrderConsumer {
    
    // 1. 订单创建消费者
    @RabbitListener(queues = "order.create.queue")
    @RabbitHandler
    public void handleOrderCreate(Order order, 
                                  Channel channel,
                                  @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        try {
            log.info("收到订单创建消息: {}", order);
            
            // 业务处理
            processOrder(order);
            
            // 手动确认
            channel.basicAck(deliveryTag, false);
            
        } catch (Exception e) {
            log.error("处理订单失败", e);
            
            // 处理失败
            // 重试3次
            if (getRetryCount(order) < 3) {
                // 拒绝消息,重新入队
                channel.basicNack(deliveryTag, false, true);
            } else {
                // 超过重试次数,进入死信队列
                channel.basicNack(deliveryTag, false, false);
            }
        }
    }
    
    // 2. 订单支付消费者
    @RabbitListener(queues = "order.pay.queue")
    public void handleOrderPay(Order order) {
        log.info("处理订单支付: {}", order);
        paymentService.process(order);
    }
    
    // 3. 死信队列消费者
    @RabbitListener(queues = "order.dlx.queue")
    public void handleDeadLetter(Order order) {
        log.warn("收到死信消息: {}", order);
        // 处理死信:发邮件通知、记录日志等
        alertService.sendAlert("订单处理失败: " + order.getId());
    }
}

五、高级特性

1. 消息确认机制

复制代码
// 生产者确认
@Component
public class ProducerConfirmCallback implements RabbitTemplate.ConfirmCallback {
    
    @Override
    public void confirm(CorrelationData correlationData, 
                       boolean ack, 
                       String cause) {
        if (ack) {
            log.info("消息发送成功: {}", correlationData.getId());
        } else {
            log.error("消息发送失败: {}, 原因: {}", 
                correlationData.getId(), cause);
            // 重试逻辑
        }
    }
}

// 配置确认回调
@Configuration
public class RabbitConfig {
    
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setConfirmCallback(new ProducerConfirmCallback());
        return template;
    }
}

2. 消息可靠性保证

复制代码
// 完整可靠性方案
@Component
public class ReliableMessageService {
    
    // 1. 数据库存储消息(保证不丢)
    @Transactional
    public void sendReliableMessage(Message message) {
        // 1.1 保存到数据库
        messageDao.save(message);
        
        try {
            // 1.2 发送到RabbitMQ
            rabbitTemplate.convertAndSend(
                message.getExchange(),
                message.getRoutingKey(),
                message.getBody()
            );
            
            // 1.3 更新状态为已发送
            message.setStatus("SENT");
            messageDao.update(message);
            
        } catch (Exception e) {
            // 发送失败,记录日志
            log.error("消息发送失败", e);
        }
    }
    
    // 2. 定时任务重发失败消息
    @Scheduled(fixedDelay = 60000)  // 每分钟执行
    public void retryFailedMessages() {
        List<Message> failedMessages = messageDao.findByStatus("FAILED");
        
        for (Message message : failedMessages) {
            if (message.getRetryCount() < 3) {
                sendReliableMessage(message);
                message.setRetryCount(message.getRetryCount() + 1);
            } else {
                message.setStatus("DEAD");
                // 进入死信处理
            }
            messageDao.update(message);
        }
    }
}

3. 延迟队列实现

复制代码
// 方案1:使用TTL+死信队列
@Configuration
public class DelayQueueConfig {
    
    // 延迟交换器
    @Bean
    public DirectExchange delayExchange() {
        return new DirectExchange("delay.exchange");
    }
    
    // 延迟队列(设置TTL)
    @Bean
    public Queue delayQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", "order.exchange");
        args.put("x-dead-letter-routing-key", "order.cancel");
        args.put("x-message-ttl", 300000);  // 5分钟
        return new Queue("delay.queue", true, false, false, args);
    }
    
    @Bean
    public Binding delayBinding() {
        return BindingBuilder.bind(delayQueue())
            .to(delayExchange())
            .with("delay.order");
    }
}

// 方案2:使用RabbitMQ延迟插件
@Bean
public CustomExchange delayPluginExchange() {
    Map<String, Object> args = new HashMap<>();
    args.put("x-delayed-type", "direct");
    return new CustomExchange(
        "delayed.exchange",
        "x-delayed-message",  // 使用插件
        true,
        false,
        args
    );
}

六、实际应用场景

场景1:电商订单系统

复制代码
@Component
public class OrderSystem {
    
    // 订单创建
    public void createOrder(OrderDTO dto) {
        // 1. 保存订单
        Order order = orderService.create(dto);
        
        // 2. 发送订单创建消息
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.create",
            order
        );
        
        // 3. 发送延迟消息(30分钟未支付取消)
        MessageProperties props = new MessageProperties();
        props.setExpiration("1800000");  // 30分钟
        
        Message message = new Message(
            order.getId().toString().getBytes(),
            props
        );
        
        rabbitTemplate.send(
            "delay.exchange",
            "order.delay",
            message
        );
    }
    
    // 消费者:库存服务
    @RabbitListener(queues = "inventory.queue")
    public void handleInventory(Order order) {
        inventoryService.deductStock(order);
    }
    
    // 消费者:短信服务
    @RabbitListener(queues = "sms.queue")
    public void handleSMS(Order order) {
        smsService.sendOrderSuccess(order.getUserId());
    }
    
    // 消费者:取消未支付订单
    @RabbitListener(queues = "order.cancel.queue")
    public void handleCancelOrder(String orderId) {
        orderService.cancelIfNotPaid(orderId);
    }
}

场景2:日志收集系统

复制代码
@Component
public class LogSystem {
    
    // 发送日志
    public void sendLog(Log log) {
        String routingKey = log.getLevel() + "." + log.getModule();
        
        rabbitTemplate.convertAndSend(
            "log.topic.exchange",
            routingKey,
            log
        );
    }
    
    // 消费者:错误日志(发告警)
    @RabbitListener(queues = "error.log.queue")
    public void handleErrorLog(Log log) {
        if ("ERROR".equals(log.getLevel())) {
            alertService.sendAlert(log);
        }
    }
    
    // 消费者:支付日志(统计)
    @RabbitListener(queues = "payment.log.queue")
    public void handlePaymentLog(Log log) {
        if (log.getModule().contains("payment")) {
            statsService.collectPaymentStats(log);
        }
    }
    
    // 消费者:所有日志(存储到ES)
    @RabbitListener(queues = "all.log.queue")
    public void handleAllLog(Log log) {
        esService.indexLog(log);
    }
}

场景3:微服务事件驱动

复制代码
// 用户服务
@Component
public class UserService {
    
    public void register(User user) {
        // 1. 保存用户
        userDao.save(user);
        
        // 2. 发布用户注册事件
        UserRegisteredEvent event = new UserRegisteredEvent(user);
        rabbitTemplate.convertAndSend(
            "user.event.exchange",
            "user.registered",
            event
        );
    }
}

// 积分服务(监听用户注册)
@Component
public class PointsService {
    
    @RabbitListener(queues = "user.registered.queue")
    public void handleUserRegistered(UserRegisteredEvent event) {
        // 新用户注册,送积分
        pointsService.addWelcomePoints(event.getUserId());
    }
}

// 推荐服务
@Component
public class RecommendService {
    
    @RabbitListener(queues = "user.registered.queue")
    public void handleUserRegistered(UserRegisteredEvent event) {
        // 新用户注册,初始化推荐列表
        recommendService.initRecommendations(event.getUserId());
    }
}

七、集群和高可用

1. 集群模式

复制代码
# 1. 启动3个RabbitMQ节点
# 节点1
RABBITMQ_NODENAME=rabbit1
RABBITMQ_NODE_PORT=5672

# 节点2
RABBITMQ_NODENAME=rabbit2
RABBITMQ_NODE_PORT=5673

# 节点3
RABBITMQ_NODENAME=rabbit3
RABBITMQ_NODE_PORT=5674

# 2. 加入集群
# 在节点2执行
rabbitmqctl stop_app
rabbitmqctl join_cluster rabbit1@hostname
rabbitmqctl start_app

# 3. 设置镜像队列
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

2. Spring Boot集群配置

复制代码
spring:
  rabbitmq:
    addresses: host1:5672,host2:5673,host3:5674
    username: guest
    password: guest
    virtual-host: /
    # 集群配置
    connection-timeout: 3000
    publisher-confirm-type: correlated
    publisher-returns: true
    cache:
      channel:
        size: 25
      connection:
        mode: connection
        size: 1

八、监控和管理

1. 管理界面

复制代码
访问:http://localhost:15672
默认账号:guest/guest

功能:
1. 查看队列深度
2. 查看消息速率
3. 管理用户权限
4. 导入导出配置
5. 查看连接信息

2. 关键监控指标

复制代码
# 命令行监控
# 查看队列
rabbitmqctl list_queues name messages_ready messages_unacknowledged

# 查看交换器
rabbitmqctl list_exchanges name type

# 查看绑定
rabbitmqctl list_bindings

# 查看连接
rabbitmqctl list_connections

# 查看消费者
rabbitmqctl list_consumers

3. Prometheus监控

复制代码
# 启用Prometheus插件
rabbitmq-plugins enable rabbitmq_prometheus

# 访问指标
http://localhost:15692/metrics

# 关键指标:
rabbitmq_queue_messages_ready
rabbitmq_queue_messages_unacknowledged
rabbitmq_queue_messages
rabbitmq_connections
rabbitmq_channels

九、常见问题解决

问题1:消息堆积怎么办?

复制代码
// 解决方案
@Component
public class MessageBacklogHandler {
    
    // 1. 增加消费者
    @Bean
    public SimpleRabbitListenerContainerFactory containerFactory() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConcurrentConsumers(10);  // 并发消费者
        factory.setMaxConcurrentConsumers(20);  // 最大并发
        return factory;
    }
    
    // 2. 监控队列深度
    @Scheduled(fixedRate = 60000)
    public void monitorQueueDepth() {
        Queue queue = rabbitAdmin.getQueueInfo("order.queue");
        if (queue != null && queue.getMessageCount() > 10000) {
            // 告警
            alertService.sendAlert("队列堆积: " + queue.getMessageCount());
            
            // 动态扩容
            scaleConsumers();
        }
    }
    
    // 3. 死信队列处理
    @RabbitListener(queues = "dead.letter.queue")
    public void handleDeadLetter(Message message) {
        // 记录、告警、人工处理
        logService.logDeadLetter(message);
    }
}

问题2:消息重复消费

复制代码
// 解决方案:幂等性处理
@Component
public class IdempotentConsumer {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @RabbitListener(queues = "order.queue")
    public void handleOrder(Order order, Channel channel, 
                           @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        
        String messageId = order.getMessageId();
        
        // 1. 检查是否已处理
        String key = "processed:" + messageId;
        if (redisTemplate.hasKey(key)) {
            // 已处理,直接确认
            channel.basicAck(deliveryTag, false);
            return;
        }
        
        try {
            // 2. 处理业务
            processOrder(order);
            
            // 3. 标记为已处理
            redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS);
            
            // 4. 确认消息
            channel.basicAck(deliveryTag, false);
            
        } catch (Exception e) {
            // 处理失败
            channel.basicNack(deliveryTag, false, true);
        }
    }
}

十、性能优化

1. 生产环境配置

复制代码
spring:
  rabbitmq:
    # 连接池
    cache:
      channel:
        size: 25
        checkout-timeout: 1000
      connection:
        mode: connection
        size: 1
    
    # 确认机制
    publisher-confirm-type: correlated
    publisher-returns: true
    
    # 消费者
    listener:
      simple:
        acknowledge-mode: manual
        prefetch: 100
        concurrency: 10
        max-concurrency: 20
        retry:
          enabled: true
          max-attempts: 3
          initial-interval: 1000

2. 最佳实践

复制代码
// 1. 消息大小控制
public void sendMessage(Order order) {
    // 消息不要太大,建议<1MB
    if (order.getSize() > 1024 * 1024) {
        compressOrder(order);
    }
    // 发送...
}

// 2. 批量发送
public void batchSend(List<Order> orders) {
    for (int i = 0; i < orders.size(); i += 100) {
        List<Order> batch = orders.subList(i, Math.min(i + 100, orders.size()));
        rabbitTemplate.convertAndSend("exchange", "key", batch);
    }
}

// 3. 合理设置TTL
public void sendWithTTL(Order order, int ttlSeconds) {
    MessageProperties props = new MessageProperties();
    props.setExpiration(String.valueOf(ttlSeconds * 1000));
    // 发送...
}

十一、一句话总结

RabbitMQ = 邮局系统 + 灵活路由 + 企业级可靠

核心是交换器模型

  • Direct:精确送快递

  • Topic:模式订阅

  • Fanout:小区广播

  • Headers:属性匹配

适用场景:需要灵活路由、复杂消息模式、与企业系统集成、快速开发的场景。

相关推荐
2501_936960366 小时前
c语言期末速成8——文件
c语言·开发语言
唐装鼠6 小时前
Rust Box<T> 和引用(deepseek)
开发语言·rust
计算机学姐6 小时前
基于php的非物质文化遗产推广系统
开发语言·vue.js·mysql·tomcat·php·postman
古城小栈6 小时前
Spring Boot 3.3 整合 AI 工具链:自动生成接口文档
人工智能·spring boot·后端
翔云 OCR API6 小时前
文档识别接口:赋能企业高效办公与加速信息的数字化转型
开发语言·人工智能·python·计算机视觉·ocr·语音识别
踏浪无痕6 小时前
为什么 Spring Cloud Gateway 必须用 WebFlux?
后端·面试·架构
宋情写6 小时前
Java基础篇01-环境搭建+入门体验
java·开发语言
cike_y6 小时前
Mybatis-万能的Map&模糊查询
java·开发语言·mybatis·安全开发
郝学胜-神的一滴6 小时前
Linux的pthread_self函数详解:多线程编程中的身份标识器
linux·运维·服务器·开发语言·c++·程序人生