
分布式系统的浪潮下,中间件早已成为企业级架构的核心基础设施。从RPC框架、消息队列,到缓存、数据库中间件,每一个支撑海量流量的系统背后,都离不开稳定可靠的中间件。而决定一个中间件能走多远、能扛多大压力的核心,永远绕不开三个灵魂级的设计原则:高可用、高性能、可扩展。
高可用的核心本质,是系统在面对各类异常场景(硬件故障、网络波动、流量洪峰、程序bug)时,依然能够持续对外提供服务的能力。行业内通常用SLA(服务等级协议)来量化这一能力,核心目标是减少非计划停机时间,阻止故障扩散。
这里需要明确一个常见的认知误区:高可用≠多实例。很多人以为部署两个节点就实现了高可用,实则不然。如果两个节点共用同一个单点数据库,数据库挂了,两个节点都会失效;如果没有故障隔离机制,一个节点故障导致流量全量打到第二个节点,直接把健康节点也打垮,引发雪崩,这根本算不上真正的高可用。
分布式系统的故障是必然发生的,我们无法杜绝故障,但可以阻止故障的扩散。这就像轮船的舱壁设计,一个舱室进水,不会导致整艘船沉没,这就是经典的舱壁模式(Bulkhead Pattern)。
故障隔离的核心实现方案分为三类:
项目基础依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<groupId>com.jam</groupId>
<artifactId>middleware-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>middleware-demo</name>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.1.0-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.49</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.115.Final</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot3-starter</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
业务服务实现
package com.jam.demo.service;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 订单服务
* @author ken
*/
@Slf4j
@Service
publicclass OrderService {
/**
* 创建订单核心接口
* @param orderId 订单ID
* @return 订单创建结果
*/
@Bulkhead(name = "orderService", type = Bulkhead.Type.THREADPOOL)
@CircuitBreaker(name = "orderService", fallbackMethod = "createOrderFallback")
public String createOrder(String orderId) {
log.info("开始创建订单,订单ID:{}", orderId);
// 核心订单创建逻辑
return"订单创建成功,订单ID:" + orderId;
}
/**
* 订单创建降级方法
* @param orderId 订单ID
* @param e 异常对象
* @return 降级结果
*/
private String createOrderFallback(String orderId, Exception e) {
log.error("订单创建失败,触发降级,订单ID:{}", orderId, e);
return"当前系统繁忙,请稍后重试";
}
}
接口层实现
package com.jam.demo.controller;
import com.jam.demo.service.OrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 订单接口
* @author ken
*/
@RestController
@RequestMapping("/order")
@RequiredArgsConstructor
@Tag(name = "订单管理", description = "订单相关接口")
publicclass OrderController {
privatefinal OrderService orderService;
/**
* 创建订单接口
* @param orderId 订单ID
* @return 订单创建结果
*/
@PostMapping("/create")
@Operation(summary = "创建订单", description = "创建新的订单")
public String createOrder(
@Parameter(description = "订单ID", required = true) @RequestParam String orderId) {
return orderService.createOrder(orderId);
}
}
单点是高可用的天敌,任何单点故障都会导致整个系统不可用。冗余备份的核心是消除单点,而故障自动转移则是在节点故障时,无需人工干预,自动把流量切换到健康的备份节点,保障服务持续可用。
核心实现方案分为三个环节:

系统的承载能力永远有上限,当流量超过系统承载阈值时,最有效的保护方式就是限流:只放行系统能承载的流量,超出的流量直接拒绝。同时对非核心业务进行降级,释放资源给核心业务,确保核心服务的可用性。
核心实现方案分为两类:
package com.jam.demo.limit;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicLong;
/**
* 令牌桶限流实现
* @author ken
*/
@Slf4j
publicclass TokenBucket {
/**
* 令牌桶容量
*/
privatefinallong capacity;
/**
* 令牌生成速率(个/秒)
*/
privatefinallong rate;
/**
* 当前令牌数量
*/
privatefinal AtomicLong currentTokens;
/**
* 上次令牌生成时间
*/
privatefinal AtomicLong lastRefillTime;
public TokenBucket(long capacity, long rate) {
this.capacity = capacity;
this.rate = rate;
this.currentTokens = new AtomicLong(capacity);
this.lastRefillTime = new AtomicLong(System.currentTimeMillis());
}
/**
* 尝试获取令牌
* @param requiredTokens 需要的令牌数量
* @return 是否获取成功
*/
public synchronized boolean tryAcquire(long requiredTokens) {
refillTokens();
long current = currentTokens.get();
if (current >= requiredTokens) {
currentTokens.addAndGet(-requiredTokens);
returntrue;
}
returnfalse;
}
/**
* 补充令牌
*/
private void refillTokens() {
long now = System.currentTimeMillis();
long lastTime = lastRefillTime.get();
long elapsedTime = now - lastTime;
if (elapsedTime > 0) {
long newTokens = (elapsedTime * rate) / 1000;
if (newTokens > 0) {
long totalTokens = Math.min(currentTokens.get() + newTokens, capacity);
currentTokens.set(totalTokens);
lastRefillTime.set(now);
}
}
}
}
高可用不能以数据丢失为代价,冗余备份的同时,必须保障数据的一致性。核心是在一致性与可用性之间找到平衡,根据业务场景选择合适的复制策略。
核心复制策略对比:
这里需要明确CAP定理的核心:分区容错性(P)是分布式系统的前提,网络分区必然会发生,因此只能在一致性(C)和可用性(A)之间做平衡,不存在绝对的CA系统。
高性能的核心本质,是在有限的硬件资源下,系统实现更低的请求延迟、更高的并发吞吐量。核心目标是减少无效开销,最大化硬件资源的利用率。
这里需要纠正一个常见的认知误区:高性能≠高并发。高并发是系统能同时处理的请求数,高性能是高并发的基础,没有高性能的设计,高并发只是空中楼阁。同时,高性能也不是一味堆硬件,而是通过合理的设计,把硬件的性能发挥到极致。
网络通信是中间件性能的第一个瓶颈,80%的中间件性能问题都出在网络IO模型上。从BIO到NIO,再到IO多路复用,本质上都是为了减少线程阻塞,降低线程上下文切换的开销,最大化CPU的利用率。
四种核心IO模型的核心差异:
基于IO多路复用的Reactor模型,是目前高性能网络组件的标准实现,分为三种经典模式:

package com.jam.demo.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
/**
* Netty高性能服务端
* @author ken
*/
@Slf4j
publicclass NettyServer {
privatefinalint port;
public NettyServer(int port) {
this.port = port;
}
/**
* 启动服务端
* @throws InterruptedException 中断异常
*/
public void start() throws InterruptedException {
// 主Reactor线程组,仅负责处理连接建立
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 从Reactor线程组,负责处理IO事件
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(new StringDecoder(CharsetUtil.UTF_8))
.addLast(new StringEncoder(CharsetUtil.UTF_8))
.addLast(new ServerHandler());
}
});
ChannelFuture future = bootstrap.bind(port).sync();
log.info("Netty服务端启动成功,端口:{}", port);
future.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new NettyServer(8080).start();
}
}
package com.jam.demo.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
/**
* 服务端业务处理器
* @author ken
*/
@Slf4j
publicclass ServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
log.info("收到客户端消息:{}", msg);
ctx.writeAndFlush("服务端已收到消息:" + msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error("通道发生异常", cause);
ctx.close();
}
}
Java的GC是影响系统性能的核心因素,频繁的Full GC会导致STW(Stop The World),引发系统卡顿、吞吐量下降。同时,数据在内存与磁盘、网络之间的拷贝,也会带来大量的性能开销。内存优化的核心,就是减少GC开销,减少数据拷贝次数。
核心实现方案分为三类:
多线程并发是提升CPU利用率的核心,但线程之间的锁竞争,会导致线程阻塞、上下文切换开销增大,甚至引发死锁。并发优化的核心,就是降低锁竞争的粒度,甚至实现无锁设计。
核心实现方案分为四类:
package com.jam.demo.disruptor;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.util.DaemonThreadFactory;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* 消息事件
* @author ken
*/
@Data
public class MessageEvent {
private String message;
}
package com.jam.demo.disruptor;
import com.lmax.disruptor.EventHandler;
import lombok.extern.slf4j.Slf4j;
/**
* 消息事件处理器
* @author ken
*/
@Slf4j
public class MessageEventHandler implements EventHandler<MessageEvent> {
@Override
public void onEvent(MessageEvent event, long sequence, boolean endOfBatch) {
log.info("消费消息:{},序列号:{}", event.getMessage(), sequence);
}
}
package com.jam.demo.disruptor;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.util.DaemonThreadFactory;
import lombok.extern.slf4j.Slf4j;
/**
* Disruptor服务启动类
* @author ken
*/
@Slf4j
publicclass DisruptorServer {
public static void main(String[] args) {
// 环形缓冲区大小,必须是2的幂
int bufferSize = 1024 * 1024;
// 创建Disruptor实例
Disruptor<MessageEvent> disruptor = new Disruptor<>(
MessageEvent::new,
bufferSize,
DaemonThreadFactory.INSTANCE
);
// 设置事件处理器
disruptor.handleEventsWith(new MessageEventHandler());
// 启动Disruptor
disruptor.start();
// 获取环形缓冲区
RingBuffer<MessageEvent> ringBuffer = disruptor.getRingBuffer();
// 生产消息
for (int i = 0; i < 100; i++) {
long sequence = ringBuffer.next();
try {
MessageEvent event = ringBuffer.get(sequence);
event.setMessage("测试消息-" + i);
} finally {
ringBuffer.publish(sequence);
}
}
// 关闭Disruptor
disruptor.shutdown();
}
}
分布式系统中,网络传输的对象必须经过序列化和反序列化,这个过程的性能开销,直接影响整个系统的吞吐量和延迟。序列化优化的核心,就是减小序列化后的体积,提升序列化/反序列化的速度。
核心实现方案分为两类:
数据结构是程序的基础,不同的数据结构在不同场景下,性能差异能达到几个数量级。算法优化的核心,就是选择时间复杂度和空间复杂度最优的数据结构,适配业务场景。
核心优化方案分为三类:
package com.jam.demo.bloom;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 商品实体
* @author ken
*/
@Data
@TableName("t_product")
publicclass Product {
@TableId(type = IdType.AUTO)
private Long id;
private String productName;
private Long price;
private Integer stock;
}
package com.jam.demo.bloom;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 商品Mapper
* @author ken
*/
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
package com.jam.demo.bloom;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 商品服务
* @author ken
*/
@Slf4j
@Service
@RequiredArgsConstructor
publicclass ProductService {
privatefinal ProductMapper productMapper;
privatefinal RedisTemplate<String, Object> redisTemplate;
private BloomFilter<Long> bloomFilter;
privatestaticfinal String PRODUCT_KEY_PREFIX = "product:";
privatestaticfinallong EXPECTED_INSERTIONS = 1000000L;
privatestaticfinaldouble FPP = 0.001;
@PostConstruct
public void initBloomFilter() {
log.info("开始初始化布隆过滤器");
List<Long> productIdList = productMapper.selectList(null).stream().map(Product::getId).toList();
bloomFilter = BloomFilter.create(Funnels.longFunnel(), EXPECTED_INSERTIONS, FPP);
for (Long productId : productIdList) {
bloomFilter.put(productId);
}
log.info("布隆过滤器初始化完成,共加载{}个商品ID", productIdList.size());
}
/**
* 根据ID查询商品
* @param id 商品ID
* @return 商品信息
*/
public Product getProductById(Long id) {
// 布隆过滤器拦截不存在的ID,避免缓存穿透
if (!bloomFilter.mightContain(id)) {
log.info("布隆过滤器拦截不存在的商品ID:{}", id);
returnnull;
}
// 查询缓存
String key = PRODUCT_KEY_PREFIX + id;
Product product = (Product) redisTemplate.opsForValue().get(key);
if (product != null) {
return product;
}
// 查询数据库
product = productMapper.selectById(id);
if (product != null) {
redisTemplate.opsForValue().set(key, product);
}
return product;
}
}
MySQL表结构
CREATE TABLE`t_product` (
`id`bigintNOTNULL AUTO_INCREMENT COMMENT'商品ID',
`product_name`varchar(255) NOTNULLCOMMENT'商品名称',
`price`bigintNOTNULLCOMMENT'商品价格(分)',
`stock`intNOTNULLDEFAULT'0'COMMENT'商品库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';
可扩展的核心本质,是系统在面对需求变化、流量增长、功能迭代时,无需重构核心架构,仅通过最小化的代码改动,就能快速支撑新的需求。核心目标是解耦依赖,实现开闭原则——对扩展开放,对修改关闭。
这里需要纠正一个常见的认知误区:可扩展≠可伸缩。可伸缩是指通过增加机器就能线性提升系统性能,属于水平扩展,是可扩展的一个子集;可扩展还包括功能的垂直扩展,新增功能无需修改核心代码。
SPI(Service Provider Interface)是一种服务发现机制,核心是把核心接口和实现分离,核心系统仅依赖接口,具体的实现由第三方插件提供,运行时动态加载,实现了核心逻辑和扩展逻辑的完全解耦,是插件化架构的核心基础。
Java原生的SPI机制,通过META-INF/services目录下的配置文件指定接口的实现类,由ServiceLoader动态加载;Spring的SPI机制通过META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件加载;Dubbo的SPI机制在原生SPI的基础上,增加了IOC和AOP的支持,功能更强大。
核心接口定义
package com.jam.demo.spi;
/**
* 序列化接口
* @author ken
*/
public interface Serializer {
/**
* 序列化
* @param obj 待序列化对象
* @return 序列化后的字节数组
*/
byte[] serialize(Object obj);
/**
* 反序列化
* @param data 字节数组
* @param clazz 目标类
* @return 反序列化后的对象
* @param <T> 泛型类型
*/
<T> T deserialize(byte[] data, Class<T> clazz);
/**
* 获取序列化类型名称
* @return 类型名称
*/
String getName();
}
Fastjson2实现类
package com.jam.demo.spi.impl;
import com.alibaba.fastjson2.JSON;
import com.jam.demo.spi.Serializer;
/**
* Fastjson2序列化实现
* @author ken
*/
publicclass Fastjson2Serializer implements Serializer {
@Override
publicbyte[] serialize(Object obj) {
return JSON.toJSONBytes(obj);
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
return JSON.parseObject(data, clazz);
}
@Override
public String getName() {
return"fastjson2";
}
}
Protobuf实现类
package com.jam.demo.spi.impl;
import com.google.protobuf.Message;
import com.jam.demo.spi.Serializer;
/**
* Protobuf序列化实现
* @author ken
*/
publicclass ProtobufSerializer implements Serializer {
@Override
publicbyte[] serialize(Object obj) {
if (!(obj instanceof Message)) {
thrownew IllegalArgumentException("Protobuf序列化对象必须实现Message接口");
}
return ((Message) obj).toByteArray();
}
@Override
@SuppressWarnings("unchecked")
public <T> T deserialize(byte[] data, Class<T> clazz) {
if (!Message.class.isAssignableFrom(clazz)) {
thrownew IllegalArgumentException("Protobuf反序列化目标类必须实现Message接口");
}
try {
Message defaultInstance = (Message) clazz.getMethod("getDefaultInstance").invoke(null);
return (T) defaultInstance.newBuilderForType().mergeFrom(data).build();
} catch (Exception e) {
thrownew RuntimeException("Protobuf反序列化失败", e);
}
}
@Override
public String getName() {
return"protobuf";
}
}
SPI配置文件在resources/META-INF/services目录下创建文件com.jam.demo.spi.Serializer,内容如下:
com.jam.demo.spi.impl.Fastjson2Serializer
com.jam.demo.spi.impl.ProtobufSerializer
SPI服务加载器
package com.jam.demo.spi;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.ServiceLoader;
/**
* 序列化器工厂
* @author ken
*/
publicclass SerializerFactory {
privatestaticfinal Map<String, Serializer> SERIALIZER_MAP = Maps.newHashMap();
static {
// 加载所有SPI实现
ServiceLoader<Serializer> serviceLoader = ServiceLoader.load(Serializer.class);
for (Serializer serializer : serviceLoader) {
SERIALIZER_MAP.put(serializer.getName(), serializer);
}
}
/**
* 根据名称获取序列化器
* @param name 序列化器名称
* @return 序列化器实例
*/
public static Serializer getSerializer(String name) {
return SERIALIZER_MAP.get(name);
}
}
微内核架构(也叫插件化架构),把系统分为两部分:核心系统(微内核)和插件模块。核心系统仅负责插件的生命周期管理、插件之间的通信、核心配置的管理,是系统最稳定的部分,不会轻易改动;所有的业务逻辑、功能扩展都以插件的形式存在,插件之间相互独立,可插拔,新增功能仅需开发新的插件,无需修改核心系统。
微内核架构的核心优势:稳定性高,核心系统改动极少;可扩展性强,新增功能仅需新增插件;可维护性高,插件之间完全解耦,出问题仅需禁用对应插件,不影响整个系统。Eclipse、IDEA、Dubbo、RocketMQ都采用了微内核的设计思想。
可扩展的前提是标准化,只有统一的接口规范、统一的协议格式,才能实现不同实现之间的无缝替换,新增扩展不会影响现有系统。协议设计的核心,是预留扩展字段,保证向前兼容,新增功能无需修改协议的核心结构,旧版本客户端能正常访问新版本服务端,新版本服务端也能兼容旧版本客户端。
核心实现方案分为两类:
当系统流量增长,单节点性能达到瓶颈时,最有效的扩展方式就是水平扩展,通过增加机器节点,线性提升系统的承载能力。水平扩展的核心前提是无状态设计,节点之间没有依赖,任何一个节点都能处理所有请求,请求可以分发到任意一个节点,新增节点仅需注册到集群中,就能承接流量,无需修改任何配置。
核心实现方案分为三类:
package com.jam.demo.hash;
import com.google.common.collect.Lists;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* 一致性哈希实现
* @author ken
*/
publicclass ConsistentHash {
/**
* 虚拟节点数量
*/
privatestaticfinalint VIRTUAL_NODE_COUNT = 160;
/**
* 哈希环
*/
privatefinal SortedMap<Long, String> hashRing = new TreeMap<>();
/**
* 真实节点列表
*/
privatefinal List<String> realNodes = Lists.newArrayList();
/**
* 添加节点
* @param node 节点地址
*/
public void addNode(String node) {
realNodes.add(node);
// 添加虚拟节点
for (int i = 0; i < VIRTUAL_NODE_COUNT; i++) {
String virtualNodeName = node + "&&VN" + i;
long hash = hash(virtualNodeName);
hashRing.put(hash, node);
}
}
/**
* 移除节点
* @param node 节点地址
*/
public void removeNode(String node) {
realNodes.remove(node);
// 移除虚拟节点
for (int i = 0; i < VIRTUAL_NODE_COUNT; i++) {
String virtualNodeName = node + "&&VN" + i;
long hash = hash(virtualNodeName);
hashRing.remove(hash);
}
}
/**
* 根据key获取对应的节点
* @param key 路由key
* @return 节点地址
*/
public String getNode(String key) {
if (hashRing.isEmpty()) {
returnnull;
}
long hash = hash(key);
// 顺时针查找第一个节点
SortedMap<Long, String> subMap = hashRing.tailMap(hash);
Long targetHash = subMap.isEmpty() ? hashRing.firstKey() : subMap.firstKey();
return hashRing.get(targetHash);
}
/**
* MD5哈希算法
* @param key 待哈希的key
* @return 哈希值
*/
private long hash(String key) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] digest = md5.digest(key.getBytes());
return ((long) (digest[3] & 0xFF) << 24)
| ((long) (digest[2] & 0xFF) << 16)
| ((long) (digest[1] & 0xFF) << 8)
| (digest[0] & 0xFF);
} catch (NoSuchAlgorithmException e) {
thrownew RuntimeException("MD5算法不存在", e);
}
}
}
很多开发者会陷入一个误区,认为三个原则是相互独立的,甚至是相互冲突的。比如为了高可用做了多副本同步复制,会影响性能;为了高性能做了很多定制化优化,会影响可扩展性。实则不然,三个原则是相辅相成的,优秀的中间件设计,一定是在三个原则之间找到了最优的平衡。
Kafka就是三大原则完美平衡的典范:通过多副本机制、ISR同步、故障自动转移实现高可用;通过顺序写磁盘、零拷贝、批量传输实现高性能;通过分区水平扩展、插件化架构实现可扩展,最终成为了工业界公认的高性能、高可靠消息队列。
不管你是在使用开源中间件,想要深入理解背后的原理,排查线上问题;还是想要自研中间件,打造适合自己业务的基础设施,都要围绕这三个原则来思考,从底层逻辑出发,结合业务场景,选择最合适的实现方案。