我们为什么选择 Encore 构建新一代物联网平台?
发布日期:
我们为什么选择 Encore 构建新一代物联网平台?
从 Beego 到 Gin,再到 Encore,ThingsPanel 走过的这些年,让我们重新思考了一件事:做一个物联网平台,真的需要这么复杂吗?
一个看似简单的设备控制,背后可能是一大堆问题
先说一个物联网开发中很常见的场景。
客户在 ThingsPanel 页面上点击一个按钮,想远程打开一台设备。
按钮点下去了,页面也提示操作成功。
但设备没有反应。
客户问:“为什么我点了开关,设备却没有动作?”
对于客户来说,这应该是一件再简单不过的事。
但开发人员可能要检查一连串问题:
- 前端请求有没有正确到达后端?
- 用户有没有这台设备的操作权限?
- 控制指令有没有正确生成?
- 消息有没有发到 MQTT Broker?
- 设备在线吗?有没有收到指令?
- 设备执行之后,有没有返回结果?
- 数据库里的设备状态为什么没有更新?
如果中间还涉及网关、协议转换、消息队列、规则引擎,排查起来就更麻烦了。
有时候,每个模块的日志看起来都正常,但整个业务就是没有跑通。
最让人头疼的不是某个功能特别难写,而是一个很简单的功能,需要跨过太多环节才能完成。
这也是我们在持续开发 ThingsPanel 的过程中,越来越重视的问题。
我们一直在思考:
- 能不能让开发者少关注一点基础设施,多关注设备和业务?
- 能不能让一个功能从 API 到数据库、消息处理、日志追踪,有更加统一的开发方式?
- 能不能让一个新加入团队的工程师,不必先研究大量项目内部约定,就能快速开始开发?
- 能不能前期开发快,后期不会变成屎山?
- 能不能提供高稳定性,解决单点故障问题?让设备别掉线
- 能不能简化架构开发,让系统更可维护?
- 能不能少做一些重复工作,让技术人员专注业务
- 能不能超高并发,解决一个城市的设备接入问题。
这些问题,也促使我们重新审视后端技术架构。
一、最早我们选择 Beego,因为那时候最重要的是把产品做出来
ThingsPanel 的早期版本,我们使用过 Go 语言的 Beego 框架。
现在回头看,这并不是什么错误的决定。
当时我们需要尽快把物联网平台的基本功能做出来。
用户管理、设备管理、产品管理、数据查询、接口开发……
Beego 提供了一套比较完整的 Web 开发方式,有路由、控制器等常见组件,对于快速启动一个项目比较方便。
尤其在项目早期,很多需求其实并不复杂。
创建一台设备、保存设备信息、查询设备列表、提供几个接口。
有一个成熟的 Web 框架,就能解决不少问题。
但物联网平台有一个特点:它的复杂度并不会随着第一版开发完成而停止增长。
刚开始,一台设备可能只有几个属性。
后来需要设备物模型,需要支持不同厂商的数据格式,需要设备远程控制。
再后来,需要规则引擎、告警、自动化、OTA 升级、设备分组、租户权限、数据转发。
客户的需求也开始出现明显差异。
这个客户希望支持 Modbus,那个客户需要对接自己的 MQTT Broker,还有客户要求平台部署到自己的服务器里。
问题逐渐暴露出来。
原来设计好的模块边界开始变得模糊。
一个设备功能的修改,可能涉及控制器、服务层、数据库操作,以及多个业务模块。
原本只为了完成某个需求而写的代码,慢慢被其他功能依赖。
业务越做越多,开发者需要了解的历史逻辑也越来越多。
这时候我们发现:
Beego 帮我们快速建立了一个 Web 后端,但没有替我们解决一个复杂物联网平台应该如何长期演进的问题。
当然,这不是 Beego 独有的问题。
真正的原因是,我们最初对业务复杂度的认识还不够充分。
一个项目刚开始时,很难预料它几年后会变成什么样。
于是,我们开始寻找更灵活的方案。
二、后来换成 Gin,框架变轻了,但系统没有因此变简单
后来,我们选择了 Gin。
Gin 在 Go 开发者中非常流行。
它轻量、性能好、社区成熟,而且给开发者留下了更多自由度。
我们希望通过这次调整,让后端代码更加清晰,也更容易根据 ThingsPanel 的特点组织业务模块。
换成 Gin 后,一些事情确实变好了。
HTTP 接口开发更加直接,路由和中间件也比较灵活。
但随着 ThingsPanel 持续发展,我们逐渐意识到另一件事:
Gin 很擅长处理 HTTP 请求,但一个物联网平台远远不只是 HTTP 请求。
比如,要开发一个设备告警功能。
我们需要接收设备数据,根据规则判断是否异常,生成告警记录,通知用户,必要时触发其他设备的动作。
这些事情很难靠一个 Web 框架直接解决。
我们仍然需要自己决定:
消息怎么传递?
模块之间怎么调用?
失败之后怎么重试?
接口规范怎么统一?
日志怎么关联?
数据库变更怎么管理?
服务拆分以后怎么部署?
Gin 没有义务替我们解决这些问题。它本来就不是为此设计的。
于是,开发团队需要不断引入其他工具,自己搭建基础设施,自己制定开发规范。
一个功能可能会使用 Gin、GORM、Redis、MQTT、定时任务组件,以及各种自己封装的服务。
这些技术本身没有什么问题。
问题在于,当所有东西都需要自己拼装时,系统的复杂度也会由我们自己承担。
而且,项目越大,这个成本越明显。
一个新同事加入团队,可能花不少时间才能搞清楚:
为什么这个模块这样调用?
为什么有的业务直接操作数据库,有的要走服务接口?
为什么有些逻辑通过消息触发,有些又在 HTTP 请求里同步执行?
为什么一个新的 API 需要手动补好几个地方?
我们可以不断补充文档,可以完善规范,可以重构代码。
但是要兼顾要考虑的地方也越来越多,除了开发业务功能,要考虑架构,考虑设计一致性,可维护性,还得发版本,高可用高并发!
如果所有规范都依靠开发者记住,团队就需要不断花时间维持这些约定。
更现实的是,当客户项目催着交付时,大家往往首先保证功能能用。
技术债务并不一定来自能力不足的开发者。很多时候,它只是一个项目在快速发展过程中,不断做出局部合理选择的结果。
我们也开始反思:
是不是我们的方向还不够对?
三、真正的问题,不是 Beego 或 Gin 不够好,而是我们把太多精力花在了业务之外
做 ThingsPanel 这些年,我们越来越清楚,开发一个成熟的物联网平台并不容易。
你需要考虑设备接入、海量数据、设备控制、规则引擎、可视化、告警、权限、安全、系统集成。
还要面对不同的行业、不同的硬件、不同的部署环境。
ThingsPanel 不是一个只做几个管理页面的后台系统。
它需要让真实设备稳定接入,让数据持续流动,让客户能够基于这些数据开展业务。
但我们发现,工程师有时会把大量精力放在重复的后端工作上。
开发一个新模块,要考虑接口、参数校验、权限、文档、日志。
增加一个服务,要考虑服务之间怎么通信。
需要异步处理,就要考虑消息队列、消费者、重试。
开发环境变了,要重新处理数据库和依赖。
出了问题,又要把多个模块的日志拼起来,才有可能还原完整过程。
每一件事单独看都不算特别难。
但全部加在一起,就是一笔很大的成本。
我们希望工程师把时间花在设备接入、数据处理和客户需求上,而不是一遍遍重复搭建后端基础设施,或者考虑和业务关系不大的基础工作。
我们也不希望每增加一个功能,就增加一套只有原作者才能解释清楚的内部约定。
于是,我们开始重新寻找答案。
这次,我们不再只关注框架性能有多高、路由有多快。
我们开始关注另一个问题:
有没有一种框架,可以从一开始就帮助开发者把 API、服务、数据库和基础设施组织好?
在这个过程中,我们关注到了 Encore。
四、第一次认真了解 Encore,我们最感兴趣的不是性能,而是它居然能理解应用
Encore 也是面向 Go 和 TypeScript 的后端开发框架。
但它和 Gin、Beego 有一个很大的不同。
普通 Web 框架主要帮助我们处理请求。
Encore 则希望进一步理解整个后端应用。
例如,在 Encore.go 里,我们可以通过代码声明一个 API、一个服务,或者一个 PostgreSQL 数据库。
Encore 会分析这些声明,知道应用里有哪些服务、有哪些接口,以及它们之间是什么关系。
听起来似乎只是少写了一些配置。
但对我们来说,这背后有几个非常有价值的变化。
1. 开发一个接口,不必重复做那么多基础工作
在 Gin 项目中,接口的定义、业务逻辑、文档生成、调用方式,往往需要开发者按照项目约定分别组织。
Encore 可以根据 Go 代码中的 API 声明识别接口及其请求、响应类型,并提供对应的 API 文档和服务调用能力。
这不意味着业务代码不用写了。
但一些原本需要自己维护的基础工作,可以交给框架。
假设我们要给 ThingsPanel 增加一个设备查询接口。
开发者应该主要思考:
这个接口提供哪些设备信息?
如何检查租户权限?
怎么保证数据查询正确?
而不是先考虑要怎么把它接入一套手工维护的接口体系。
对于一个拥有大量 API 的物联网平台来说,这种统一性很有吸引力。
2. 一条请求经过哪些服务,可以直接看到
这可能是我们最看重的能力之一。
回到开头的设备控制问题。
假设未来我们的设备控制流程涉及设备管理服务、控制服务和数据库操作。
在 Encore 能够追踪的服务调用链中,我们可以看到请求经过了哪些 API、服务以及数据库查询,每一步花了多长时间,哪里发生了错误。
这比单纯打印一堆日志更容易理解。
尤其是开发规则引擎、自动化、通知等跨模块功能时,调用关系清晰非常重要。
当然,设备进入 MQTT Broker 之后的完整通信过程,仍然需要我们自己做好消息追踪和关联。
Encore 不是自动理解所有物联网协议。
但它可以先让应用服务这一部分变得更加透明。
以前排查问题,工程师可能要努力还原系统究竟发生了什么;现在,我们希望系统本身就能够更清楚地告诉工程师发生了什么。
3. 新人不需要先搭建一大套开发环境
开发体验也是我们非常重视的一点。
在传统项目中,一个新同事可能需要先准备数据库、缓存、消息队列和各种配置。
即使团队已经准备了 Docker Compose,随着项目发展,环境文件、数据库版本、初始化脚本仍然需要维护。
而 Encore 提供了一个很直接的命令:
encore run
对于 Encore 原生支持并且已经在代码中声明的资源,本地运行时可以自动准备相应的开发环境。
例如 PostgreSQL、消息发布订阅环境等。
同时,还可以打开本地开发控制台,查看 API、服务结构和调用追踪。
当然,运行数据库仍然需要 Docker 等必要条件,ThingsPanel 特有的 MQTT Broker、时序数据库及其他组件也不可能全部自动消失。
但至少对于常规后端基础设施,团队可以少维护不少重复配置。
对小团队来说,一个新人能更快开始写业务代码,比多学会一套环境编排技巧更有价值。
4. 服务可以逐步拆分,而不是一开始就把所有东西做成微服务
我们也很喜欢 Encore 对服务组织方式的设计。
有些团队做新项目,一开始就规划十几个甚至几十个微服务。
结果业务还没有多少,服务发现、网络调用、部署、监控就已经很复杂。
另外一些团队则走向相反的方向,所有代码放在一起,后来想拆又发现模块之间存在大量耦合。
Encore 给我们提供了另外一种思路。
先按照清晰的业务边界组织服务。
例如设备管理、遥测数据、设备控制、自动化、告警通知。
它们可以先在相对简单的应用结构中协作,再根据业务规模和部署需要决定如何演进。
我们需要的不是微服务数量越来越多,而是每个服务的职责越来越清楚。
对于正在长期演进的 ThingsPanel,这一点特别重要。
五、物联网平台的特殊性,让我们更需要这种开发方式
物联网和普通互联网业务有很多不同。
最明显的区别是:很多时候,系统的另一端不是人,而是一台真实设备。
设备可能断网。
消息可能重复。
命令可能超时。
不同厂商的数据格式可能完全不同。
同样一个“打开开关”的操作,不同设备可能有不同的指令格式、下发方式和结果反馈方式。
这会给后端带来很多特殊问题。
第一,设备接入方式会不断变化,业务不应该跟着反复修改
ThingsPanel 需要支持 MQTT、Modbus、HTTP、TCP 等不同的设备接入方式。
未来还可能有更多协议和行业设备。
我们不希望每接入一种新设备,平台的设备管理、规则引擎和数据服务都跟着修改一遍。
更理想的方式是:
底层接入服务负责理解不同设备的协议。
上层业务服务尽量处理统一的设备、属性、事件和指令。
这本来就是 ThingsPanel 持续建设插件架构和统一消息处理机制的原因。
而 Encore 可以帮助我们把上层 API 和业务服务的边界管理得更加清楚。
它不会替代 MQTT Broker,也不会替代协议解析。
我们希望把真正需要自己研发的物联网能力做好,把通用的后端开发工作尽可能交给成熟框架。
第二,设备数据不能只考虑“收到了”,还要考虑“后面发生了什么”
例如,一台温度传感器上报了 85℃。
平台接收到数据后,可能需要更新设备最新状态、保存历史数据、判断是否超过阈值、生成告警,再通知相关人员。
一个数据可能引发多个后续动作。
如果告警通知失败了怎么办?
如果消息重复送达怎么办?
如果数据存储成功了,但事件发布失败了怎么办?
这些问题并不会因为换成 Encore 就自动解决。
但 Encore 提供的服务 API、Pub/Sub 和调用追踪等能力,可以帮助我们更规范地组织部分异步业务。
对于重复消息的幂等处理、数据一致性和失败补偿,我们仍然需要认真设计。
这是物联网平台不可省略的工程工作。
区别在于,我们不必每开发一个异步业务,就从头建立一套完全不同的处理方式。
第三,客户要的是系统稳定好用,而不是知道我们用了多少技术
做物联网平台,经常需要面向具体行业交付。
客户可能只关心三件事:
设备能不能快速接入?
出了问题能不能找到原因?
新需求能不能尽快实现?
他们并不关心我们用了 Beego、Gin 还是 Encore。
更不会因为用了 Kubernetes,就认为平台一定更好。
假如客户提出一个设备告警联动需求,我们希望开发者能够快速找到相关模块,实现业务,完成测试,并且知道这个功能会影响哪些服务。
而不是把时间花在搞清楚系统内部到底有多少层封装上。
技术选型最终应该为业务服务。
对 ThingsPanel 来说,更好的架构,应该让一个普通开发者也能更容易理解、修改和扩展平台。
六、这次我们不准备把旧系统全部推倒重来
每次讨论架构升级,都会遇到一个现实问题:
已经写好的功能怎么办?
ThingsPanel 经过多年开发,已经积累了设备管理、规则引擎、数据处理、可视化和多种设备接入能力。
这些能力不是换一个框架就能重新获得的。
如果为了采用 Encore,把现有系统全部重写一遍,未必是一个理性的决定。
重写不仅需要时间,还可能带来新的兼容性问题。
特别是物联网平台,很多历史接口已经被客户系统、设备网关和第三方应用使用。
不能因为我们换了架构,就要求客户全部重新对接。
所以我们的方向不是简单重写 ThingsPanel,而是逐步建立一套更标准的新后端架构,在保留现有业务能力的基础上持续演进。
我们希望优先从 API 规范、设备数据、设备控制等核心业务入手。
让新接口的输入输出更加统一,让权限校验更加清晰,让服务之间的调用更容易理解。
对于原有成熟功能,可以通过适配方式继续使用。
新架构逐步完善以后,再按照业务模块迁移,而不是一次性替换整个系统。
这个过程也不是单纯把旧接口包装一下就结束。
我们需要重新审视那些过去积累下来的问题。
比如不同接口的错误码不一致。
比如设备控制的成功状态与设备实际执行结果需要明确区分。
比如客户重复点击按钮时,如何避免重复下发有风险的控制指令。
比如一个用户到底可以查询和控制哪些租户、哪些项目中的设备。
这些都是新架构必须认真面对的问题。
Encore 能帮助我们约束服务结构、统一部分开发方式,但业务规范仍然需要我们自己建立。
这次架构升级真正想解决的,不只是代码怎么写,而是未来的代码应该怎样持续发展。
七、为什么没有直接选择更复杂的微服务框架?
我们也考虑过一个问题:
既然 ThingsPanel 越来越复杂,是不是应该直接使用更完整的微服务技术体系?
比如把所有服务独立部署,再配上 Kubernetes、服务治理、配置中心、链路追踪和完整的 DevOps 平台。
这些方案当然可以解决很多问题。
但它们也有代价。
需要更多工程经验,需要维护更多基础设施,也需要团队长期投入资源。
对于大型企业,这些投入可能非常合理。
但我们希望 ThingsPanel 不只适合拥有专业运维团队的大企业。
它也应该适合中小企业、集成商、设备厂商以及独立开发者。
有人可能只想在一台服务器上运行平台。
有人希望部署到自己的内网。
有人需要按需扩展设备接入服务。
还有人想基于 ThingsPanel 开发一个专门的行业应用。
如果平台本身过于复杂,用户还没有开始做业务,就已经被部署和二次开发难住了。
那就背离了我们开发开源物联网平台的初衷。
Encore 吸引我们的地方,恰恰不是要求每个项目都使用复杂的微服务部署。
而是允许我们从清晰的服务结构开始,尽量减少常见后端工作中的重复劳动。
尤其是它的 Go 开发框架和工具链是开源的,支持构建 Docker 镜像,自行部署。
这对于国内开发者很重要。
我们可以在自己选择的基础设施上运行应用,不必把全部业务绑定到云平台。
当然,自托管仍需要自己准备数据库、消息系统、数据库迁移、定时任务调度、部署和监控。Encore 不会自动创建阿里云、腾讯云的所有资源。
但这仍然是一个有价值的方向。
开发框架帮助我们降低开发复杂度,实际部署环境由我们根据客户需要决定。
八、还有一个越来越重要的原因:我们希望 ThingsPanel 更容易被 AI 理解和开发
现在,AI 辅助编程正在改变软件开发方式。
但用 AI 开发一个大型老项目,有一个很现实的问题。
AI 可以很快生成代码,却不一定理解项目中的全部历史约定。
它可能写出一个能运行的接口,却没有正确处理租户权限。
也可能重新实现一个早已存在的功能。
甚至因为不了解服务之间的依赖,修改一个地方却影响了其他模块。
代码生成速度越快,架构不清晰带来的问题可能越明显。
所以,我们越来越认为:
适合 AI 时代的后端架构,首先应该是一套能被清楚理解的架构。
Encore 通过代码声明 API、服务和基础设施资源,并能够生成应用结构信息。
它还提供面向 AI 开发工具的集成能力,让工具更容易获取项目的接口定义和服务关系。
这并不能保证 AI 写出的业务代码一定正确。
但至少,我们可以减少一部分需要靠猜测才能理解的系统信息。
对于 ThingsPanel 这样的开源平台,我们希望未来不仅核心团队能快速开发,社区贡献者、行业开发者,以及使用 AI 工具进行二次开发的人,也能更容易上手。
一个清楚、标准、有边界的后端系统,可能比一套只有少数核心开发者才能完全理解的复杂架构更有生命力。
九、我们知道 Encore 不是万能的,但它更接近我们想要的方向
选择 Encore,并不意味着我们认为它能解决 ThingsPanel 的所有问题。
比如 MQTT 设备连接、海量消息接入、协议解析、高频时序数据写入,这些仍然需要专业的物联网组件和针对性的优化。
TDengine、Cassandra 等数据系统也不是声明一个 Encore SQL 数据库就能自动完成接入。
复杂的私有化部署、高可用、数据一致性、租户安全和业务权限,仍然需要工程团队认真设计与验证。
Encore 也有自己的开发约定,采用它意味着需要学习新的组织方式,并处理现有系统的迁移成本。
这次选择不是没有代价。
但我们认为,这个代价值得研究和实践。
因为我们真正想改变的,是整个团队开发后端的方式。
以前,我们需要不断补充规范,让开发者尽量按照相同方式写代码。
现在,我们希望框架本身能够承担更多约束和基础工作。
以前,增加一个模块可能同时意味着增加一批配置和维护任务。
现在,我们希望开发者更关注这个模块究竟解决什么业务问题。
以前,我们依赖少数熟悉系统的工程师排查复杂问题。
现在,我们希望一个普通开发者也能更容易看懂服务关系和执行过程。
架构的价值,不应该是让系统看起来更先进,而应该是让以后每一次开发都比以前更容易。
最后:我们不想让下一代 ThingsPanel 变成一个只有专家才能维护的平台
从 Beego 到 Gin,再到今天开始探索和实践 Encore,我们最大的收获,不是发现了某一个完美的框架。
而是越来越清楚,自己真正想做什么。
我们希望 ThingsPanel 能够支持更多设备、更多协议、更多行业场景。
希望它能适应从小规模项目到大型物联网系统的不同需求。
也希望开发者能够方便地扩展平台,把它应用到工厂、能源、农业、楼宇和更多实际场景。
但与此同时,我们不希望它变成一个越来越庞大、越来越复杂、只有少数人才能维护的系统。
我们希望新同事加入团队后,可以更快开始开发。
希望社区开发者想增加一个功能时,不用先研究几个月的底层代码。
希望客户提出一个新需求时,工程师可以把主要精力放在解决问题上。
希望系统出了问题时,我们能更快知道问题发生在哪里。
更希望 ThingsPanel 未来的架构足够清楚,让更多普通开发者,甚至借助 AI 的独立开发者,也能够参与建设。
这也是我们选择 Encore 的重要原因。
我们不是为了使用新技术而更换框架,而是希望未来的 ThingsPanel,能用更简单的方式做更多复杂的事情。
回过头看,从 Beego 到 Gin,我们解决了不少当时的问题。
但这一次,我们想走得更远一点。
让物联网业务保持强大,让后端开发尽可能简单。
这就是我们正在探索的新一代 ThingsPanel。
相关项目与技术资料
ThingsPanel 官网:https://thingspanel.io
ThingsPanel GitHub:https://github.com/ThingsPanel
Encore 官网:https://encore.dev
Encore Go 文档:https://encore.dev/docs/go
Encore 自托管文档:https://encore.dev/docs/self-host/build
我们也会继续分享新架构的设计、迁移实践,以及过程中遇到的真实问题。





