Netty实战应用学习笔记-中级拓展篇(六)

🔍 项目六:日志收集系统(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. 粘包/半包问题

三种解决方案

  1. 固定长度FixedLengthFrameDecoder

    channel.pipeline().addLast(new FixedLengthFrameDecoder(100));
    
  2. 分隔符LineBasedFrameDecoderDelimiterBasedFrameDecoder

    channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
    
  3. 长度字段LengthFieldBasedFrameDecoder

    channel.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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值