这两年最火的是什么?物联网,现在连我家的电灯泡都是联网的。大量设备连网最考验的就是我们服务器的连接数量和保持情况。最近会陆续更新一系列的文章,使用Netty做IoT服务端的设计和技术讨论文章。
技术选型
在Java界做socket开发最好的框架就是Netty,他本身是一个基于nio的封装,并解决了在Linux环境下nio的一些已知BUG。理想情况下前置程序应该只负责终端设备的连接保持和数据的转发,具体的业务交由后置程序来实现处理。
先来看一下我初步设计的程序架构图:
- Netty在最前端负责接收连接请求和上下行数据,实验数据使用1G内存docker容器运行,一个容器可以保持并处理5万个终端的连接。
- 为了加快Netty的处理速度,从终端接收上来的数据会直接放到disruptor内存队列中。
- disruptor的消费者会根据数据的类型来选择,注册终端、通过rabitmq向后置处理程序提交、通过kafka向后置提交日志
- 同时还需要做基本的心跳检查
通讯协议设计
通许协议的设计主要从几个方面考虑:
- 首先要能解决TCP的粘包、半包问题
- 通讯数据量要尽可能的小,终端设置的物联卡一般流量包都不会太大。
- 编码格式,目前一般都使用UTF8格式
为了解决第一问题,一般协议会考虑使用:
- 定长数据包方案优点是处理简单,缺点是为了照顾大数据包。会存在大量包里携带空数据。适合比较简单的数据/控制
- 特定字符分割方式。大多采用n来分包,比第一种相对灵活。比如redis的通讯就是用的这种
- 首字节指定包长度方式。大量基于C开发的终端设备在使用,相对第一种更灵活不浪费流量
- 闭合包方式,最就是目前互联网协议里用得最多的json。可以使用基础器来找{}的匹配,优点是通用性好,缺点是多少会浪费流量。
如果终端程序和服务端都是自行开发,推荐使用谷歌家的protobuf,Netty有良好的支持。如果双终端是第三方开发的为了降低联调难度推荐使用json。这次我们选择使用Json来进行设计我们的协议
业务 | 请求 | 应答 | 备注 |
---|---|---|---|
登录 | {“msgType”: 10, “devId”: “11010112345”, “version”:”1.0”, “txNo”: “1234567890123”, “sign”: “md5(devId txNo key)”} | {“msgType”: 11, “result”: 1, “txNo”: “1234567890123”} | 终端=>服务器 |
心跳 | {“msgType”: 20, “txNo”: “1234567890123”} | {“msgType”: 21, “txNo”: “1234567890123”} | 双向 |
控制 | {“msgType”: 30, “txNo”: “1234567890123”, “data”: “”} | {“msgType”: 31, “txNo”: “1234567890123”, “result”:””} | 服务器=>终端 |
数据上报 | {“msgType”: 40, “txNo”: “1234567890123”, “data”: “”} | {“msgType”: 41, “txNo”: “1234567890123”} | 终端=>服务器 |
注销 | {“msgType”: 50, “txNo”: “1234567890123” } | {“msgType”: 51, “txNo”: “1234567890123” } | 双向,主动断开链接 |
说明:
- txNo为唯一序号,使用13位时间戳即可,应答时带回同一个时间戳即可。
- 登录接口里的sign主要是为了防止接口泄漏后,有人恶意请求接口模拟设备踢掉真实设备
- 心跳包 从服务器向终端时,终端如果无法跑NTP协议可以用此包里的txno做校时
- 注销功能主要 是为了客户端正常下线使用。减少服务器保持连接的时间减少资源占用。