蛮荆

网络编程框架的作用和价值

2022-10-25

问题

既然 Linux 提供了 epoll 这样的 I/O 多路复用机制,接近内核且高性能,为什么开发者不直接使用 epoll 进行编程?而是在应用层折腾出了类似 Netty (Java), gnet (Golang) 这样的网络编程框架呢?

毕竟,每多一层封装,系统的整体性能就会有 (主要是数据复制带来的) 损耗。

或者换个说法: 网络编程框架的作用和价值体现在哪里?

这个问题,就类似: “有了汇编语言,为什么还要有高级语言” ?

答案其实和简单,总结出来无非是 3 点:

  1. 简化复杂的网络编程
  2. 自定义功能和扩展性
  3. 降低开发成本、提升代码可维护性

1. 简化复杂的网络编程

编程框架可以屏蔽细节,简化并统一 API,使应用开发者可以专注处理业务逻辑,降低心智负担,提高交付效率和质量。

上面是直接使用 epoll 提供的 API 进行编程时,(Socket) 文件描述符的相关操作的简化图,除此之外,开发者还需要其他的各种底层操作:

  • Reactor 模型的实现 (为了有效管理)
  • I/O 事件处理
  • 各类网络协议解析
  • 多线程实现 (为了高性能)

但是有了编程框架之后,就完全不一样了: 框架会处理好所有底层的这些 “脏活累活”,以 TCP 协议为例,最终传递到应用开发者的就是事件名称以及对应的应用数据,应用开发者只需要重复三板斧操作就可以了:

  1. 读数据
  2. 处理业务逻辑
  3. 写数据

所以网络编程框架,本质上可以看作是应用层的 TCP/IP 协议栈,只不过处理的 I/O 事件没有内核层的 TCP/IP 协议栈那么多而已。

提升性能和并发处理

虽然 epoll 提供了高效的 I/O 事件通知机制,但是面对大量并发网络连接时,如何调度事件、充分利用多线程的同时避免竞争条件等问题,epoll 并不负责。这时就需要一个网络编程框架来把这些必要的基础性工作完成,例如封装 Reactor 模型。

此外,框架内部通常实现了很多性能优化,比如常见的 内存池零拷贝缓冲区减少上下文切换的无锁编程 等机制,这些优化细节大大提高了网络应用的性能。

解析复杂的网路协议

一个系统的通信往往由多种网络协议组成:

  • 面向 C 端的 HTTP、WebSocket
  • 面向 B 端的 TCP、UDP
  • 系统内部众多服务之间互相调用的 RPC

应用开发者不可能逐个去实现解析各种网络协议,代码质量难以得到保证,且属于重复性造轮子。

网络编程框架把解析的活儿干完,应用开发者可以专注于 “一把梭” 就可以了。

毕竟,对于大多数项目来说,最复杂的地方就是去实现一套绝对灵活的 “极品” 业务逻辑规则,而不是相对固定的网络协议解析。

错误/异常处理

网络编程不同于单机编程,除了程序外,整个通信链路中容易出现问题的地方也很多,下面的错误是不是似曾相识:

  • connection timed out
  • connection refused
  • no route to host
  • broken pipe
  • address already in use
  • network is unreachable
  • host unreachable

如果这些错误全部交给应用开发者处理,那么在开始编写业务逻辑之前,代码可能已经变成了这个样子:

解决跨平台问题

Linuxepoll 仅仅是众多平台 I/O 多路复用实现机制其中的一种,如果开发者的系统需要运行在多个不同平台上,就要针对每个平台编写一套系统。

网络编程框架可以根据运行平台环境,(例如通过条件编译) 自动选择对应的 I/O 多路复用机制,真正做到代码 “一次编写,随地运行”。


2. 自定义功能和扩展性

既然是业务系统,那就说明每个模块、功能必要有优先级排序,几个重要且明显的需求就出来了:

  • 优先运行系统的核心功能
  • 紧急任务可以临时加塞到任务队列
  • 可以根据系统运行环境,自定义不同的配置参数

而这些需求,不可能直接和 epoll API 的调用代码堆叠到一起,此时,就需要借助网络编程框架来进行一个优雅且合理的代码实现分层。

技术性扩展功能

现代服务端网络程序中,一些基础组件是必备的,这时就可以直接借助框架来无缝集成,

最常见的也是最必要的就是 应用层的心跳检测机制 了,这一点无需解释太多。

此外还有:

  • SSL/TLS 安全层扩展集成
  • 传输数据压缩
  • 自动重连/断开
  • 负载均衡
  • 网络连接统计、状态上报
  • 针对 TCP 的粘包/拆包问题编码器/解码器

图片来源: https://netty.io/


3. 降低开发成本和提升代码可维护性

epoll 实现在内核中,提供的 API 自然是 C/C++ 语言版本。

图片来源: BOSS 直聘

即使现在 AI 已经可以写代码了,可以编写优秀的 C/C++ 程序的程序员相对较为稀缺,且开发周期较长。相反,使用 Java/Go 直接编写业务代码的程序员有足够的市场供应,这样就可以降低招聘成本和周期,快速迭代产品,投入市场进行验证。毕竟对于一个公司/项目来说,活下去最重要,而活下去靠的是什么?赚钱的业务。

通过网络编程框架,让技术水平一般的程序员,快速写出可以投入生产环境的 “及格代码”。

图片来源: https://www.oracle.com/


小结

编程框架用于分层,而分层的核心目的就是: 使用最合适的技术完成对应的工作 (合适的就是最好的 ~🐶~)。

大佬们即使用汇编语言,也可以写出健壮的高性能程序,因为他们早就超越了代码的层次,已经和计算机软硬件融为一体,真正做到 “代码千行过,Bug 不沾身”。

但是软件开发行业,尤其是其中覆盖面最广的应用软件开发领域,不能只靠精英程序员的推动,而是需要更多普通程序员的参与,这也就是为什么软件要分为系统软件、中间件、应用软件,而每种类型的软件都使用适合的编程语言、语言框架用来开发。

转载申请

本作品采用 知识共享署名 4.0 国际许可协议 进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,商业转载请联系作者获得授权。