🔍 项目六:日志收集系统(Netty + Elasticsearch)
项目概述
项目名称:itstack-demo-netty-2-06
核心功能:使用Netty收集日志数据,实时存储到Elasticsearch
技术要点:Spring Data Elasticsearch、Repository模式、ES文档映射
为什么需要这个项目?
实际问题:
- 分布式系统日志分散在各个服务器,难以查询
- 日志量大,传统数据库查询慢
- 需要全文检索和复杂查询
- 需要实时收集和分析日志
解决方案:
- ✅ Netty高性能接收:支持大量日志数据
- ✅ Elasticsearch存储:支持全文检索和分析
- ✅ Spring Data简化开发:无需编写复杂的ES操作代码
- ✅ 实时性:日志实时收集、实时查询
适用场景:
- 日志收集系统(ELK架构的一部分)
- 数据同步系统(MySQL → Elasticsearch)
- 实时搜索引擎
- 大数据分析平台
核心知识点
1. 系统架构
┌─────────────┐
│ 客户端应用 │ (业务系统、日志采集器)
└──────┬──────┘
│ Netty TCP
│ (传输User对象)
↓
┌─────────────┐
│ Netty服务器 │ (接收数据)
└──────┬──────┘
│ Spring Data
│ (调用Repository)
↓
┌─────────────┐
│Elasticsearch│ (存储和检索)
└─────────────┘
2. Elasticsearch简介
什么是Elasticsearch?
Elasticsearch(ES)是一个基于Lucene的分布式搜索和分析引擎。
| 特性 | 说明 |
|---|---|
| 全文检索 | 支持复杂的搜索查询 |
| 分布式 | 支持水平扩展 |
| 实时性 | 近实时的搜索和分析 |
| RESTful API | 简单易用的HTTP接口 |
| JSON文档 | 使用JSON格式存储数据 |
ES概念对比:
MySQL → Elasticsearch
数据库(Database) → 索引(Index)
表(Table) → 类型(Type)
行(Row) → 文档(Document)
列(Column) → 字段(Field)
3. 定义ES文档
User.java
@Document(indexName = "stack", type = "group_user")
public class User {
@Id
private String id;
private String name; // 姓名
private Integer age; // 年龄
private String level; // 级别
private Date entryDate; // 入职时间
private String mobile; // 电话
private String email; // 邮箱
private String address; // 地址
}
关键注解:
@Document:定义索引和类型indexName:索引名称(类似数据库名)type:类型名称(类似表名)
@Id:标记主键字段
4. Spring Data Elasticsearch
UserRepository.java
public interface UserRepository extends ElasticsearchRepository<User, String> {
// 自定义查询方法
Page<User> findByName(String name, Pageable pageable);
}
自动提供的方法:
save(User user) // 保存
deleteById(String id) // 删除
findById(String id) // 查询
findAll() // 查询所有
count() // 统计数量
方法命名规则:
findByName(String name) // 按姓名查询
findByAge(Integer age) // 按年龄查询
findByNameAndAge(String name, Integer age) // 多条件查询
findByAgeBetween(Integer min, Integer max) // 范围查询
findByNameLike(String name) // 模糊查询
5. Netty Handler集成ES
MyServerHandler.java
@Service("myServerHandler")
public class MyServerHandler extends ChannelInboundHandlerAdapter {
@Autowired
private UserService userService; // 注入ES服务
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 1. 接收消息
logger.info("服务端接收到消息:" + JSON.toJSONString(msg));
// 2. 解析协议
TransportProtocol transportProtocol = (TransportProtocol) msg;
// 3. 保存到Elasticsearch
userService.save((User) transportProtocol.getObj());
}
}
关键点:
- 使用
@Service注解,让Spring管理Handler - 通过
@Autowired注入UserService - 接收到数据后直接保存到ES
6. 配置文件
application.properties
## 服务端口
server.port = 8080
## Netty服务配置
netty.host = 127.0.0.1
netty.port = 7397
## Elasticsearch配置
spring.data.elasticsearch.cluster-name=es-itstack
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
spring.data.elasticsearch.repositories.enabled=true
配置说明:
cluster-name:ES集群名称(需与ES配置一致)cluster-nodes:ES节点地址(9300是传输端口)repositories.enabled:启用Spring Data ES Repository
7. 解决Netty版本冲突
Application.java
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
// 解决Netty和ES的Netty版本冲突
System.setProperty(
"es.set.netty.runtime.available.processors", "false"
);
SpringApplication.run(Application.class, args);
}
}
为什么需要:
- Elasticsearch内部也使用Netty
- 会与项目的Netty冲突
- 设置系统属性,禁用ES的Netty处理器数量检测
项目总结
核心技术:
- ✅ Netty高性能接收:使用Netty接收大量数据
- ✅ Elasticsearch存储:支持全文检索和分析
- ✅ Spring Data简化开发:无需编写复杂的ES操作代码
- ✅ 对象序列化传输:使用Protostuff高效传输
适用场景:
- 日志收集系统(ELK架构的一部分)
- 数据同步系统(MySQL → Elasticsearch)
- 实时搜索引擎
- 大数据分析平台
🎓 核心知识点总结
1. Netty与SpringBoot整合
关键技术:
CommandLineRunner:SpringBoot启动完成后执行@Value:读取配置文件@Autowired:依赖注入ShutdownHook:优雅关闭
最佳实践:
@SpringBootApplication
public class Application implements CommandLineRunner {
@Value("${netty.port}")
private int port;
@Autowired
private NettyServer nettyServer;
@Override
public void run(String... args) throws Exception {
nettyServer.start(port);
Runtime.getRuntime().addShutdownHook(
new Thread(() -> nettyServer.destroy())
);
}
}
2. 粘包/半包问题
三种解决方案:
-
固定长度:
FixedLengthFrameDecoderchannel.pipeline().addLast(new FixedLengthFrameDecoder(100)); -
分隔符:
LineBasedFrameDecoder、DelimiterBasedFrameDecoderchannel.pipeline().addLast(new LineBasedFrameDecoder(1024)); -
长度字段:
LengthFieldBasedFrameDecoderchannel.pipeline().addLast(new LengthFieldBasedFrameDecoder( 1024, 0, 4, 0, 4 ));
3. 序列化方案选择
决策树:
需要跨语言?
├─ 是 → Protobuf
└─ 否 → 需要定义proto文件?
├─ 可以接受 → Protobuf
└─ 不想定义 → Protostuff
性能排序:
Protobuf ≈ Protostuff > JSON > XML > Java序列化
4. Pipeline设计原则
处理链顺序:
入站(Inbound):
字节流 → 帧解码器 → 消息解码器 → 业务Handler
出站(Outbound):
业务Handler → 消息编码器 → 字节流
示例:
// 入站处理链
channel.pipeline().addLast(new FrameDecoder()); // 1
channel.pipeline().addLast(new MessageDecoder()); // 2
channel.pipeline().addLast(new BusinessHandler()); // 3
// 出站处理链(逆序执行)
channel.pipeline().addLast(new MessageEncoder()); // 3
5. 文件传输最佳实践
核心技术:
RandomAccessFile:支持随机读写- 分片传输:避免内存溢出
- 断点续传:提升用户体验
- 空字节处理:避免文件损坏
关键代码:
// 定位到指定位置
randomAccessFile.seek(position);
// 读取数据
int readSize = randomAccessFile.read(bytes);
// 去掉空字节
if (readSize < bytes.length) {
byte[] copy = new byte[readSize];
System.arraycopy(bytes, 0, copy, 0, readSize);
}
6. WebSocket核心流程
握手流程:
HTTP请求 → 验证 → 101状态码 → WebSocket连接建立
消息类型:
TextWebSocketFrame:文本消息BinaryWebSocketFrame:二进制消息PingWebSocketFrame:心跳检测PongWebSocketFrame:心跳响应CloseWebSocketFrame:关闭连接
7. Elasticsearch集成
核心概念:
@Document:定义索引和类型@Id:定义主键ElasticsearchRepository:提供CRUD方法- 方法命名规则:自动生成查询
最佳实践:
@Document(indexName = "logs", type = "app_log")
public class AppLog {
@Id
private String id;
private String message;
private Date timestamp;
}
public interface AppLogRepository
extends ElasticsearchRepository<AppLog, String> {
Page<AppLog> findByMessage(String message, Pageable pageable);
}

被折叠的 条评论
为什么被折叠?



