综合121-140
121. 到底什么是 TCP 连接?
展开 简单 VIP 网络
TCP连接定义: TCP连接是一个抽象概念,它是通信双方维护的一组状态信息,用于可靠地传输数据。
连接的本质:
- 不是物理连接:网络层面没有专用的物理线路
- 状态维护:双方各自维护连接状态信息
- 虚拟电路:在不可靠的网络上建立可靠的通信通道
连接状态信息包括:
- 序列号(发送、接收)
- 窗口大小
- 最大段长度(MSS)
- 往返时间(RTT)
- 拥塞控制参数
连接标识(四元组):
(源IP, 源端口, 目标IP, 目标端口)
生命周期:
- 建立:三次握手创建连接
- 数据传输:可靠的双向数据传输
- 关闭:四次挥手终止连接
特点:
- 面向连接的协议
- 全双工通信
- 字节流服务
- 可靠性保证
122. 如何处理重复消息?
展开 中等 VIP 消息队列 后端
重复消息产生原因:
- 网络重传
- 生产者重试
- 消费者重复拉取
- 系统故障恢复
解决方案:
1. 幂等性设计
// 使用唯一ID确保操作幂等
@Service
public class OrderService {
public void processOrder(String orderId, OrderInfo order) {
if (orderExists(orderId)) {
return; // 已处理过,直接返回
}
// 处理订单逻辑
saveOrder(orderId, order);
}
}
2. 去重表
- 消息ID作为主键
- 处理前先检查是否存在
- 利用数据库唯一约束
3. Redis分布式锁
public boolean processMessage(String messageId) {
String lockKey = "msg_lock:" + messageId;
if (redisTemplate.setIfAbsent(lockKey, "1", Duration.ofMinutes(5))) {
try {
// 处理消息逻辑
return true;
} finally {
redisTemplate.delete(lockKey);
}
}
return false; // 重复消息
}
4. 状态机模式
- 设计合理的状态转换
- 确保状态变更的幂等性
最佳实践:
- 业务层面设计幂等接口
- 使用全局唯一ID
- 合理设置去重窗口期
- 监控重复消息比例
123. Java 中什么情况会导致死锁?如何避免?
展开 中等 Java并发 Java
死锁的四个必要条件:
- 互斥条件:资源不能被多个线程同时使用
- 持有等待:线程持有资源的同时等待其他资源
- 不可剥夺:资源只能被持有者主动释放
- 循环等待:形成资源等待的环路
常见死锁场景:
1. 锁顺序死锁
// 错误示例:可能导致死锁
public class DeadlockExample {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void method1() {
synchronized(lock1) {
synchronized(lock2) {
// 业务逻辑
}
}
}
public void method2() {
synchronized(lock2) {
synchronized(lock1) { // 与method1顺序相反
// 业务逻辑
}
}
}
}
2. 动态锁顺序死锁
// 根据对象hash值排序避免死锁
public void transfer(Account from, Account to, int amount) {
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
if (fromHash < toHash) {
synchronized(from) {
synchronized(to) {
transfer(from, to, amount);
}
}
} else {
synchronized(to) {
synchronized(from) {
transfer(from, to, amount);
}
}
}
}
避免死锁的方法:
1. 固定锁顺序
- 所有线程按相同顺序获取锁
2. 超时机制
if (lock1.tryLock(1000, TimeUnit.MILLISECONDS)) {
try {
if (lock2.tryLock(1000, TimeUnit.MILLISECONDS)) {
try {
// 业务逻辑
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
3. 使用并发工具类
- CountDownLatch
- Semaphore
- CyclicBarrier
4. 减少锁的使用
- 使用无锁数据结构
- 减小锁的粒度
- 使用读写锁
124. 如何保证消息的有序性?
展开 中等 VIP 消息队列 后端
有序性级别:
1. 全局有序
- 整个系统中所有消息严格有序
- 性能代价大,一般不推荐
2. 分区有序
- 同一分区内消息有序
- 平衡了性能和有序性
3. 业务有序
- 相关联的消息有序
- 最常用的方案
实现方案:
1. 单队列方案
// 生产者发送到同一队列
producer.send("order-queue", message);
// 消费者单线程消费
@RabbitListener(queues = "order-queue", concurrency = "1")
public void handleMessage(OrderMessage message) {
// 处理消息
}
2. 分区路由
// 根据业务key路由到固定分区
public class OrderProducer {
public void sendOrder(OrderMessage message) {
String partitionKey = message.getUserId();
producer.send("order-topic", partitionKey, message);
}
}
3. 消息编号
@Component
public class OrderedMessageConsumer {
private Map<String, Long> lastSequenceMap = new ConcurrentHashMap<>();
public void processMessage(String userId, Long sequence, Object message) {
Long lastSequence = lastSequenceMap.get(userId);
if (lastSequence == null || sequence == lastSequence + 1) {
// 处理消息
lastSequenceMap.put(userId, sequence);
} else {
// 乱序处理:缓存或重试
handleOutOfOrder(userId, sequence, message);
}
}
}
RocketMQ有序消息示例:
// 生产者
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
String orderId = (String) arg;
int index = orderId.hashCode() % mqs.size();
return mqs.get(Math.abs(index));
}
}, orderId);
// 消费者
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(
List<MessageExt> messages,
ConsumeOrderlyContext context) {
// 按顺序处理消息
return ConsumeOrderlyStatus.SUCCESS;
}
});
注意事项:
- 有序性和性能是矛盾的
- 合理选择有序性级别
- 考虑消费者故障的影响
- 避免消息积压
125. 说一下 Netty 的应用场景?
展开 中等 VIP Netty 后端
Netty简介: Netty是基于NIO的客户端-服务器框架,提供异步的事件驱动的网络应用程序框架。
核心特性:
- 高性能的NIO实现
- 事件驱动架构
- 丰 富的协议支持
- 内存管理优化
- 线程模型优秀
主要应用场景:
1. RPC框架
// Dubbo底层使用Netty
// gRPC的Java实现也使用Netty
public class RPCServer {
private EventLoopGroup bossGroup = new NioEventLoopGroup(1);
private EventLoopGroup workerGroup = new NioEventLoopGroup();
public void start() throws Exception {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new RPCServerInitializer());
ChannelFuture future = bootstrap.bind(8080).sync();
}
}
2. 游戏服务器
- 实时性要求高
- 大量并发连接
- 自定义协议支持
3. 即时通讯
// WebSocket聊天服务器
public class ChatServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(64 * 1024));
pipeline.addLast(new WebSocketServerProtocolHandler("/chat"));
pipeline.addLast(new ChatServerHandler());
}
}
4. HTTP服务器
- 高并发Web服务
- API网关
- 代理服务器
5. 消息队列
- RocketMQ使用Netty作为通信层
- Kafka的客户端使用Netty
6. 分布式系统通信
- 微服务间通信
- 集群节点通信
- 分布式存储系统
7. 物联网(IoT)
- MQTT协议实现
- 设备数据收集
- 边缘计算
8. 大数据传输
- Hadoop的数据传输
- 流式数据处理
- 文件传输服务
选择Netty的原因:
- 比原生NIO更易用
- 高性能、低延迟
- 内存管理优秀
- 丰富的编解码器
- 稳定的API设计
知名项目使用Netty:
- Dubbo、Elasticsearch
- Cassandra、HBase
- Spark、Flink
126. 什么是 Spring IOC?
展开 中等 VIP 后端 Spring
IOC定义: IOC(Inversion of Control)控制反转,是一种设计原则,将对象创建和依赖管理的控制权从应用程序转移到框架。
核心概念:
1. 依赖注入(DI)
- 构造器注入
- Setter注入
- 字段注入
2. IOC容器
- BeanFactory:基础容器
- ApplicationContext:高级容器
传统方式 vs IOC方式:
传统方式:
public class UserService {
private UserDao userDao = new UserDaoImpl(); // 硬编码依赖
public User getUser(Long id) {
return userDao.findById(id);
}
}
IOC方式:
@Service
public class UserService {
@Autowired
private UserDao userDao; // 由Spring注入
public User getUser(Long id) {
return userDao.findById(id);
}
}
IOC的优势:
1. 降低耦合度
- 对象不直接创建依赖
- 易于测试和维护
2. 提高可扩展性
- 通过配置切换实现
- 支持多种注入方式
3. 统一管理
- 对象生命周期管理
- 单例、原型等作用域
Bean的生命周期:
1. 实例化 → 2. 属性注入 → 3. Aware接口回调 →
4. BeanPostProcessor前置处理 → 5. 初始化方法 →
6. BeanPostProcessor后置处理 → 7. 使用 → 8. 销毁
配置方式:
1. XML配置
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>
2. 注解配置
@Component
public class UserService {
@Autowired
private UserDao userDao;
}
3. Java配置
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService(userDao());
}
}
容器类型:
- BeanFactory:延迟初始化,轻量级
- ApplicationContext:立即初始化,功能丰富
127. 你了解 Java 线程池的原理吗?
展开 中等 Java并发 Java
线程池核心参数:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
工作流程:
1. 当前线程数 < corePoolSize → 创建新线程执行任务
2. 核心线程已满 → 任务加入队列等待
3. 队列已满 + 当前线程数 < maximumPoolSize → 创建临时线程
4. 线程数达到maximumPoolSize → 执行拒绝策略
线程池状态:
// 线程池状态转换
RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED
核心源码分析:
public void execute(Runnable command) {
int c = ctl.get();
// 1. 工作线程数小于核心线程数
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 加入工作队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 创建临时线程或拒绝
else if (!addWorker(command, false))
reject(command);
}
常用线程池类型:
1. FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3. SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Worker线程管理:
private final class Worker extends AbstractQueuedSynchronizer
implements Runnable {
final Thread thread;
Runnable firstTask;
public void run() {
runWorker(this);
}
}
优势:
- 降低资源消耗(线程复用)
- 提高响应速度(无需创建线程)
- 提高线程的可管理性