一、什么是中间件?

中间件,本质上是别人已经写好的开源程序,并且这个开源程序具备特定的业务属性。

在行业里,中间件一般都是大厂为了解决特定的业务功能而研发的程序,在内部用着效果不错,然后开源出来供大家使用。

简单来说,中间件就是帮我们解决特定场景问题的 “成熟工具”,让我们不用重复造轮子,专注于业务逻辑开发。

二、中间件的常见类型

中间件的种类很多,其中最核心、最常用的主要有两大类:缓存类型和消息队列

类型代表产品核心解决问题应用场景
缓存中间件Redis、Memcached提升数据读取速度,降低数据库压力高频查询、秒杀库存、用户会话存储
消息队列中间件RabbitMQ、Kafka、RocketMQ系统解耦、异步通信、流量削峰订单异步通知、日志收集、秒杀削峰

三、什么是缓存?

3.1 理解缓存

有一句很经典的话:性能不够,缓存来凑

缓存存在的意义,就是为了优化我们程序的性能

要理解缓存,我们需要先了解计算机硬件的性能差异:

  • CPU:用来计算的,速度极快。
  • 内存(如 16G、32G):
    • 优点:读写速度特别快。
    • 缺点:不具备数据持久化的能力,且单位成本高。
  • 磁盘(如 500G、1T):
    • 优点:数据稳定,可持久化存储。
    • 缺点:读写速度特别慢。
硬件组件核心功能关键参数示例优点缺点访问速度层级
CPU数据计算与指令执行-速度极致(纳秒级)无存储能力,仅临时运算1 级(最快)
内存临时数据存储与读写16G、32G、64G读写速度极快(微秒级),适配 CPU 运算节奏断电数据丢失,单位成本高,容量有限2 级
磁盘持久化数据存储500G SSD、1T HDD容量大,单位成本低,断电数据不丢失读写速度慢(毫秒级),磁盘 IO 是性能

3.2 缓存的核心工作模式

缓存的核心思想是:将高频访问数据从 “慢速磁盘” 迁移到 “高速内存”,磁盘仅作为数据持久化备份,彻底规避频繁的磁盘 IO 操作(先将数据放在内存中,磁盘做备份)。

模式 1:传统无缓存架构(性能瓶颈明显)
用户请求 ---> 应用程序(Java/Spring Boot) ---> 数据库(MySQL,存储于磁盘) ---> 返回数据至用户

问题:每次请求都需触发磁盘 IO,高频访问场景下,数据库会成为系统瓶颈,响应延迟急剧上升。

模式 2:引入缓存的优化架构(核心实战流程)
用户请求 ---> 应用程序 ---> 缓存(如Redis,存储于内存) ---> 命中则直接返回
                          ↓(未命中)
                     数据库 ---> 读取数据 ---> 写入缓存 ---> 返回数据至用户

3.3 关键操作:缓存预热

缓存预热是在系统启动或低峰期,主动将高频访问数据提前查询并写入缓存的操作,是缓存架构中不可或缺的一步,简单来说就是提前将高频访问数据查询出来,放到内存中,当用户再次访问这些数据时,程序直接从内存中读取,速度得到极大提升。

(1)核心价值
  1. 避免系统启动初期,大量请求直接穿透缓存访问数据库,导致数据库压力骤增;
  2. 保证核心业务数据(如首页商品列表、热门活动信息)始终在缓存中,确保用户请求的低延迟响应。

3.4 缓存实战

(1) 实体类:User

定义用户实体,包含姓名、手机号等核心字段:

public class User {
    /**
     * 姓名
     */
    private String name;
    /**
     * 手机号
     */
    private String phone;

    private int money;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }
}
(2) 模拟数据源:UserList

模拟磁盘中的用户列表,提供 “根据用户名查手机号” 的基础方法:

import java.util.ArrayList;
import java.util.List;

public class UserList {
    /**
     * 用来模拟从磁盘中读取的用户列表
     */
    public static List<User> users = new ArrayList<User>();

    // 静态代码块初始化模拟数据
    static {
        User user = new User();
        user.setName("甄聪");
        user.setPhone("18733225351");

        User user2 = new User();
        user2.setName("张三");
        user2.setPhone("10086");

        users.add(user);
        users.add(user2);
    }

    // 按照姓名查询手机号
    public static String getPhoneByUserName(String userName) {
        for (User user : users) {
            if (user.getName().equals(userName)) {
                return user.getPhone();
            }
        }
        return null;
    }
}
(3)缓存管理工具:CacheManager

实现缓存的核心操作(新增、查询、删除),基于HashMap模拟缓存容器:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 缓存管理
 * 缓存中间件
 */
public class CacheManager {
    /**
     * 字符串类型缓存(key:用户名,value:手机号)
     */
    private static Map<String, String> CACHE = new HashMap<String, String>();

    /**
     * 场景扩展:朋友圈点赞缓存(key:朋友圈id,value:点赞用户列表)
     */
    private static Map<String, List<User>> USERLSTACAHE = new HashMap<>();

    /**
     * 向列表型缓存新增元素
     * @param key 缓存key
     * @param user 待新增用户
     */
    public static void addList(String key, User user) {
        List<User> users = USERLSTACAHE.getOrDefault(key, new ArrayList<>());
        users.add(user);
        USERLSTACAHE.put(key, users);
    }

    // 取消点赞(示例预留方法,可补充实现)
    public static void deleteUser(String key, User user) {}

    /**
     * 刷新缓存(新增/更新)
     * @param key 缓存key
     * @param value 缓存value
     */
    public static void put(String key, String value) {
        CACHE.put(key, value);
    }

    /**
     * 查询缓存
     * @param key 缓存key
     * @return 缓存value(未命中返回null)
     */
    public static String get(String key) {
        return CACHE.get(key);
    }

    /**
     * 删除缓存
     * @param key 缓存key
     */
    public static void remove(String key) {
        CACHE.remove(key);
    }
}
(4)核心业务逻辑:Test

实现 “缓存优先查询”+“缓存预热” 的核心逻辑:

import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 缓存预热:提前加载所有用户数据到缓存
        putAllCache();

        // 多次查询,验证缓存命中效果
        getPhoneByUserName("甄聪");
        getPhoneByUserName("甄聪");
        getPhoneByUserName("甄聪");
        getPhoneByUserName("张三");
    }

    /**
     * 根据用户名查询手机号(缓存优先)
     * @param name 用户名
     * @return 手机号
     */
    public static String getPhoneByUserName(String name) {
        // 1. 先查询缓存
        String phone = CacheManager.get(name);

        // 2. 缓存未命中:从数据源查询,并写入缓存
        if (phone == null) {
            System.out.println("缓存未命中,从数据源查询:" + name);
            phone = UserList.getPhoneByUserName(name);
            // 写入缓存,后续查询可直接命中
            CacheManager.put(name, phone);
        } else {
            System.out.println("缓存命中:" + name);
        }
        return phone;
    }

    /**
     * 缓存预热:系统启动时加载所有用户数据到缓存
     */
    public static void putAllCache() {
        // 将UserList中的所有用户数据提前放入缓存
        List<User> users = UserList.users;
        for (User user : users) {
            CacheManager.put(user.getName(), user.getPhone());
        }
        System.out.println("缓存预热完成,共加载" + users.size() + "条用户数据");
    }
}
(5)代码执行流程

缓存命中:查询时直接从CacheManagerCACHEMap 中获取数据,无需访问数据源;

缓存未命中:先从UserList(模拟磁盘)查询数据,再将结果写入缓存,确保下次查询命中。

putAllCache() 方法在程序启动时执行,将UserList中的所有用户数据(姓名 - 手机号)提前写入缓存。

输出如下(缓存预热后所有查询均命中):

缓存预热完成,共加载2条用户数据
缓存命中:甄聪
缓存命中:甄聪
缓存命中:甄聪
缓存命中:张三

若注释putAllCache()(关闭缓存预热),首次查询 “甄聪” 会输出 “缓存未命中”,后续查询则命中。

四、什么是消息队列?

消息队列:一般我们会简称它为MQ(Message Queue)。

Message Query(MQ),消息队列中间件,很多初学者认为,MQ通过消息的发送和接受来实现程序的异步和解耦,mq主要用于异步操作,这个不是mq的真正目的,只不过是mq的应用,mq真正的目的是为了通讯。

他屏蔽了复杂的通讯协议,像常用的dubbo,http协议都是同步的。

这两种协议很难实现双端通讯,A调用B,B也可以主动调用A,而且不支持长连接。mq做的就是在这些协议上构建一个简单协议——生产者、消费者模型,mq带给我们的不是底层的通讯协议,而是更高层次的通讯模型。他定义了两个对象:发送数据的叫做生产者,接受消息的叫做消费者,我们可以无视底层的通讯协议,我们可以自己定义生产者消费者。

五、完整总结

本文从基础认知、底层逻辑、实战代码、协同场景、避坑要点五个维度,完整覆盖了缓存与消息队列两大核心中间件:

  1. 缓存中间件:核心是 “空间换时间”,通过内存存储高频数据,解决系统性能瓶颈,关键要点是缓存预热、缓存避坑;
  2. 消息队列中间件:核心是 “异步解耦 + 削峰填谷”,通过队列实现系统间通信,关键要点是模式选择、可靠性保障、幂等性处理;
  3. 协同价值:缓存聚焦 “读性能优化”,消息队列聚焦 “写 / 通信高可用”,二者结合可构建高性能、高可用的后端系统。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐