在上一篇《你好,NIO》博客中的提到了NIO使用中半包粘包问题,解决方案是用帧状态机,这一篇单独摘出来细说一下。为什么凑齐一帧必须用"状态机"?
先说下我最初的想法,估计也是大多数人的第一反应:
java ByteBuffer header = ByteBuffer.allocate(4);
while (header.hasRemaining()) {
channel.read(header); // 头没凑齐就一直读
}
header.flip();
int len = header.getInt();
ByteBuffer body = ByteBuffer.allocate(len);
while (body.hasRemaining()) {
channel.read(body); // 内容没凑齐就一直读
}
// 凑齐了,处理消息
看起来天经地义——"没读满就继续读,直到凑齐为止",BIO 时代不就这么写的吗?但注意:这里是非阻塞通道(configureBlocking(false))。非阻塞的 read() 在没有数据时不会等待,而是立刻返回 0。
于是这段代码在两个场景下会出大事:
半包(一条消息被拆成多次到达)—— CPU 空转客户端发了一条 3000 字节的消息,真实网络上它被 TCP 拆成多个段,先后到达:
本文记录一次完整的 SkyWalking 落地过程:服务端部署(11.0.0)、本地项目接入(JDK 8~21)、以及真正天天用得到的排障方法。
环境:CentOS 7 + JDK 17(服务端)+ Elasticsearch 7.9.3 + SkyWalking 11.0.0 + Java Agent 9.8.0(本地 Java 21 项目)
系列文章
- 第一篇:选型、架构与前置准备
- 第二篇:部署 canal-server 与 adapter
- 第三篇:踩坑与注意事项 ← 本篇
按实际踩到的顺序排列,也是推荐的排查顺序:
| # | 现象 | 根因 | 解法 |
|---|---|---|---|
| 0 | Plugin 'mysql_native_password' is not loaded | MySQL 8.4 默认禁用该插件 | 改用 caching_sha2_password |
| 1 | command 'show master status' has an error! | MySQL 8.4 移除了该语句 | 固定位点启动 |
| 2 | 认证失败 / 驱动不兼容 | adapter 自带 5.1.48 驱动 | 换 8.0.33 驱动 |
| 3 | None of the configured nodes are available | es7 适配器默认走 TransportClient | mode: rest |
| 4 | Illegal character in scheme name at index 0 | hosts 缺协议头 | 改成 http://ip:9200 |
| 5 | 启动 NullPointerException | application.yml 缺 accessKey | 补上空键 |
| 6 | 全量同步 NPE / 导入 0 条 | 索引没建或没有 mapping | 先建带 mapping 的索引 |
| 7 | ETL 成功但 count=0 | ES 未 refresh | _refresh 或等 1 秒 |
| 8 | CentOS 7 装不上 JDK | 系统 EOL,yum 源失效 | 用二进制包 |
坑 0 严格说是 MySQL 侧的坑,但它决定了后面所有配置的方向,所以放第一位。
系列文章
- 第一篇:选型、架构与前置准备
- 第二篇:部署 canal-server 与 adapter ← 本篇
- 第三篇:8 个踩坑记录与生产环境注意事项
前置条件:MySQL 已开 ROW binlog,
canal账号已建好,JDK 8 已安装(见第一篇)。 本篇所有配置文件里的<MYSQL_HOST>、<CANAL_DB_PASSWORD>等占位符,请替换成你自己的值。
系列文章
- 第一篇:选型、架构与前置准备 ← 本篇
- 第二篇:部署 canal-server 与 adapter,跑通全量 + 增量
- 第三篇:8 个踩坑记录与生产环境注意事项
环境:CentOS 7.9 + MySQL 8.4.1 + Elasticsearch 7.9.3 + Canal 1.1.7
如果你正打算做 MySQL → ES 的实时同步,建议三篇按顺序看。第三篇的坑,基本都是第二篇部署时踩出来的。
业务库在 MySQL,但搜索、列表页、运营后台需要走 Elasticsearch。常见做法有三种:
| 方案 | 实时性 | 侵入性 | 说明 |
|---|---|---|---|
| 业务代码双写 | 高 | 高 | 每个写库的地方都要写 ES,容易漏、容易不一致 |
| 定时全量刷 | 低 | 低 | 数据量大时扛不住,延迟以分钟/小时计 |
| 解析 binlog | 准实时(秒级) | 零侵入 | Canal 就是这一类 |
Canal 的原理很朴素:把自己伪装成 MySQL 的从库(slave),订阅 binlog,把变更解析成结构化消息。
业务代码一行不用改,MySQL 侧只需要保证 binlog_format=ROW。这是它相比"代码双写"最大的优势——存量代码零改造,也不会因为某处漏写导致数据不一致。
代价是:你多了一个需要运维的中间件,而且它的版本兼容性对 MySQL 版本很敏感(这一点第三篇会详细展开)。