业务网关的设计与实践

news2025/1/19 7:03:10

在过去的两年里,主要在做业务网关的开发。今年春节后选择转岗去做更偏近业务的开发。公司的业务是金融相关,一直觉得金融相关的业务是有一定门槛并且是对职业生涯有帮助的,所以趁这个机会来深入了解这块业务。

仔细回想,在做业务网关的两年里,并没有遇到真正大的技术挑战。在这两年里,真正得到成长的是作为一个基础设施项目的owner,明确系统的定位,思考并把控系统的发展方向。因此,尝试在这篇文章中把自己过去两年的工作做一些总结记录。

文章目录

  • 业务网关概述
    • 定位
    • 应用架构
  • 发展方向
    • 方寸之间做文章
    • 高性能
    • 稳定性
    • 解耦
    • 轻运维
    • 白屏化

业务网关概述

上图为简易的请求链路图,其主要展示了请求从用户端发出到到达业务应用之间的链路。在完整的系统架构图中通常会把这部分概括为网关层,细分的话会先后经历CDN节点、流量网关、业务网关。CDN的作用大家都清楚,这里就不做赘述。流量网关和业务网关则各自有自己的定位和职责。

定位

  • 流量网关:主要职责是流量的统一入口。
    流量网关具备7层负载均衡的能力。同时作为流量的入口,大多数情况下不会配置太精确的路由匹配,通常是根据前缀的模糊匹配将请求转发到不同的业务集群。在定位上,流量网关离业务比较远,迭代频率非常低。通常一家公司不同业务共用一套流量网关。

  • 业务网关:主要职责是业务集群流量的统一入口。
    在大多数情况下,业务集群都是提供rpc接口的微服务集群,由业务网关进行统一的api管理,对外提供api。所以业务网关基本的能力就是精确的api管理能力(路由能力)以及协议转换能力。
    此外,业务网关,相对流量网关而言,和业务关系紧密得多(要注意的是紧密不代表耦合)。业务集群中需要的通用的业务能力会抽象出来在业务网关实现,比如鉴权、限流、封禁等通用能力。在一些规模比较大的公司中,业务线之间差异会很大,可能会各自维护多套不同的业务网关。比如字节跳动旗下,有短视频业务、tob业务、小说业务,这些业务差异太大,很难共用一套业务网关。

应用架构

上图是业务网关的应用架构图。从架构上,我把其分为四层。

  • 最外层为接口层。这一层主要是对外实现不同的协议,以及对应的路由管理模块。目前支持的协议为http协议和ws协议。路由模块基于前缀树的数据结构开发而成,基于公司特定的业务需求在http method+path的基础上支持更细粒度的路由能力。简单来说就是,一个http method+path对应多个后端接口,根据请求参数转发。每个具体的路由对应一个handler来处理接收的请求。handler由filter和invoker组合而成。其中filter为应用可配置的中间件,invoker为调用层的实现。
  • 接口层下面是应用层,这一层是业务网关“业务”二字的主要体现。在业务网关中,以filter的形式实现了可插拔、可扩展的能力。其中包括基础能力如单机限流、accesslog、metrics和trace等,这里的metrics和trace指的都是面向请求级别的指标,不包含网关内部指标观测;还有业务能力如鉴权、业务限流、合规、封禁、防篡改等能力。在实现上基础能力为默认加载,业务能力则根据业务方配置进行加载。
  • 应用层下面是调用层,调用层的实现为invoker。调用层负责在应用层处理完成后调用业务应用接口,其职责包括业务应用的连接管理(池化实现),http到grpc的协议转换,在业务集群中实现不同的负载均衡策略,针对业务实例的状态进行熔断。invoker上为了未来的扩展,同样预留了invoker middleware的机制,可以在其上扩展能力。其中动态熔断的能力就是以invoker middleware的形式进行扩展。
  • 除此三层外还有支持层的实现。其内容包括一些抽象出来的基础能力,包括服务发现、统一灰度能力、动态日志、诊断工具等,在golang中,就是通常我们会放在pkg下面的公有能力。

发展方向

前面提到做业务网关的两年里,真正的成长是作为owner来把控系统的发展方向,下面是我对业务网关的定位和发展方向的一些思考。绝大部分的思考都是站在技术角度,出于业务网关自身以及整体架构的健康发展。只有第一点不同,业务优先级要高于技术优先级,我们作为研发,要避免技术自嗨。

方寸之间做文章

作为研发,我们都知道要在迭代时要保证系统的合理性。

在过去的工作中,我也一直以非常高的技术标准要求自己。但是最近两年我的一点感悟是,大部分情况下,我们技术只是手段而不是目的,是为了达成业务目标。所以要避免技术自嗨,一切以业务为主。

有些情况下业务需求可能会要求一些不合理的架构或者设计,那作为研发,我们只能在不合理中做到尽量的合理。也就是这一小节的标题,方寸之间做文章。当然,这不能作为降低标准的理由。在绝大部分情况下,技术的合理性和业务目标是不冲突的,合理的系统设计只会对业务有助力。

高性能

业务网关作为基础组件,性能要求本身就比较高。再加上当前公司对外提供的是高频交易服务,用户对响应时间是极其敏感的。性能要求比互联网行业的要高一个数量级。在构建高性能网关方面,我们有以下几点经验。

  • 内存缓存
    本地缓存是构建高性能网关的最重要一点。
    一个核心链路的请求在业务网关的处理中,至少要经过鉴权、封禁、合规等业务处理。而上述依赖的业务数据都是由中台的相关服务来维护,业务网关通过rpc与其交互获取业务数据。在aws中同az网络调用的rtt是0.3ms,请求redis的网络rtt是0.6ms。也就是说即使上述依赖服务server端做了redis缓存,一次rpc调用至少增加耗时0.9ms。这是完全不可接受的。
    因此在业务网关中大量采用了本地缓存的方式来缓存业务数据,业务网关和依赖服务之间通过rpc懒加载建立缓存数据+kafka通知数据更新的方式来保证数据的最终一致性。
    针对不同类型的业务数据,业务网关中还采用了不同的数据结构进行维护。涉及到技术细节,这里不做展开。
  • 控制日志量
    日志是非常影响性能。即使是异步日志,当请求量大时,也会对性能造成比较大的影响。
    在业务网关中,将日志分为access.log和app.log。access.log为请求级别,通过byte拼接而成。app.log中几乎没有额外日志,只会打印出错时的err日志。通常,access.log和具体的err日志足以应对绝大多数情况。
    除此之外,我还开发了动态日志的功能,可以根据配置动态地把某些请求的请求和响应打印出来。动态日志的能力支持多维度的组合,包括path、来源ip、服务等维度,可以严格地把动态日志的量控制在最小范围内。配合动态日志,虽然日志量控制严格,但是目前尚未日志出现难以满足要求的情况。
  • 序列化优化
    在大多数应用中,除io外,最耗时的基本都是序列化和反序列化。在网关中,因为涉及到协议转换,序列化和反序列化的占比相对更高。在业务网关序列化优化方面,主要有两个方向。
  1. 底层序列化优化。这方面的优化通常是选择性能更高的序列化库,或者是优化序列化的过程。这部分内容小公司可能很难有人力去自研。目前grpc的泛化协议转换普遍是通过dynamic message的方式进行的,整个过程对cpu和内存都有额外的损耗。我对此有些想法,但确实没有精力投入开发
  2. 业务层的序列化优化。在响应方面,业务网关对外提供了不同的组装方式,而之前针对不同的组装方式的序列化都是一视同仁,这会带来不小的浪费。在发现这个问题后,我将响应的序列化提升至更上层(invoker层迁移到filter中),针对不同的组装方分别进行处理,大大提升了效率,并提升了部分接口的响应时间。
    针对序列化这块,这里不展开,后面有时间再补充。

稳定性

作为基础组件和流量入口,业务网关的稳定性要求是非常高的。

我们都知道,计算+数据=服务。对应到生产环境的服务,计算可以认为是我们的进程,最常见的进程类型就是处理请求并给出响应;数据则是来自各种依赖,广义的存储比如rds、redis等,消息队列,配置中心比如nacos、etcd等,还有其他的服务。

稳定性方面,进程+依赖当然是一个整体,互相影响。但从治理的角度出发,我们不妨把进程和依赖分开来看。

普遍来看,我认为,进程的稳定性治理要远简单于依赖的稳定性治理。进程中只是接受请求处理并给出响应,消耗的资源仅有cpu、内存、网卡,因此只要进程有足够的资源,就不会有问题。一方面,我们有自适应限流等算法保证资源紧张时拒绝请求;另一方面,有hpa等自动扩容策略。另外,当进程出现问题时,通过重启也能快速地进行恢复(绝大部分都是无状态服务)。

但依赖的治理则要复杂的多,比如rds有慢查询,redis有大key、热key、缓存击穿,依赖服务不可用导致服务雪崩等。进程和各种依赖之间还有一层网络通信,这都让依赖服务的稳定性治理复杂很多。不同的依赖都需要根据具体的场景进行分析。但是还是有一些通用的方法论:1. 区分强依赖和弱依赖;2. 熔断降级。

关于自适应限流和动态熔断等方法,可以见服务治理小记。

解耦

业务网关的定位就是作为业务线的流量入口,并提供通用的业务能力。业务网关的价值很大一部分也体现在通用业务能力上。在实际的迭代过程中,通用的业务能力其实没有明确的界限。业务线会提各种需求过来,如何保证能解决业务问题,又不和业务系统耦合,当然要case by case的分析。但我也总结出了几条原则来帮助其他同学。

  • 接口级别的控制能力
    业务网关对外提供能力的最小粒度为接口级别。如果业务线提出的需求需要根据请求的参数做不同的控制,那这部分功能很大可能和业务耦合得比较深并且不具备通用性。
  • SDK封装业务能力
    在业务网关的早期,业务能力的迭代,采用了业务方提供prd网关研发开发的模式。在这种模式下,网关研发需要比较深入地理解业务需求,这带来了比较高的理解和沟通成本。同时,这些业务需求后续迭代的时候都需要业务方和网关研发比较紧密地协作。虽然是通用业务能力,但是却带来了组织上的耦合。
    针对这个问题,我提供的解决思路是让业务方研发提供sdk来提供业务能力,业务网关仅仅是提供一个占位符。比如在去年的一个安全防篡改的需求中,需求对请求进行解密,响应进行加密。这个需求中,就由安全同学提供sdk提供加解密的能力。业务网关只是在此之上封装能力并提供给各业务方。
    当然,有些业务需求并没有所谓的业务方研发,通常就需要网关同学在维护对应的业务sdk。

轻运维

在理想的情况下,我的期望是业务网关的基本的能力迭代的足够完善后,就将其托管给业务开发和业务运维。由于业务网关以filter的形式扩展业务能力,所以业务开发可以在不了解整体架构的情况通过filter机制自行迭代业务能力。作为业务网关的研发,只提供技术支持排查框架的问题及优化。这种情况下,我们的研发在运维、维护、oncall上的时间成本占比就会很少。可以有精力去开发更多的产品。

但以公司当前的情况来看,该想法至少一两年内难以实现。退而求其次,我通过以下的手段来控制运维和维护的成本。

  • 控制集群数量
    出于隔离的目的,业务网关应该为每个业务线单独隔离部署。但是随着集群数量的增多,运维成本会随之成倍的提高。因此,对于核心的、请求量大的业务,会独立部署业务网关集群;对于边缘业务,会多个业务线共用集群。
  • 提高业务自助排障能力
    提高业务的自助排障能力可以大幅地减少维护成本。通常来说,这部分都离不开文档。但是我个人认为,应该通过设计来减少文档在自助排障能力中的比重,而不是无脑地让业务同学来查看繁重的文档。我们在系统层面做了一下设计。
  1. 日志隔离
    在业务网关中,我们按照属性将日志分为access.log和app.log。
    access.log中有除具体请求参数和响应结果外的所有数据。绝大部分情况下,业务方只看access.log就足够。acces.log中最常用的字段就是错误码和调用时间。一者表示请求的结果,一者表示请求的耗时。错误码在下面会单独介绍。
    app.log中包含除access.log外的所有日志,没有再按日志级别进行分组,这是因为app.log的量很少。在性能和稳定性中都提到过,app.log中的日志是严格控制的,只有报错的详细信息的err日志。通过动态日志的方式来进行弥补。
  2. 错误码拆分
    在最开始的设计中,错误码分为业务错误码和系统错误码。
    其中业务错误码为5位,常见的例如鉴权失败、被封禁等等都属于业务错误码。业务错误码是比较好识别的。
    系统错误码为4位,最开始只有5000和6000。5000表示内部错误,只有在序列化失败和内部逻辑错误等场景,基本不出现。6000表示依赖错误,调用上游和依赖服务(用户服务、封禁服务等)的非业务层报错,比如超时、连接失败等都会用6000表示。6000的错误码是比较常见的,尤其是开发、测试环境的oncall中基本都是因为6000导致。针对此问题,我对系统错误码进行了拆分,比如60xx表示上游服务的系统错误、61xx表示用户服务的系统错误。这样配合文档,就极大地降低了运维成本。

白屏化

白屏化是基础组件迭代的一个重要方向。
在早期,业务网关只对外提供接口来开放操作能力,这对使用方的同学来说其实是有相当的门槛的。在迭代到快两年的时候,开始开发管理后台的项目,将业务网关控制面的能力通过页面的方式开放出去。其实大部分基础组件都会经历这个过程。比如常见的kafka、redis、etcd等中间件,最开始的时候只有命令行cli工具可用,随着公司发展,一般都会迭代出相应的控制面。
因为业务网关本身不是个复杂的组件,管理后台中也没有比较难理解的逻辑,所以管理后台的整个迭代过程还是比较简单的。这里就不赘述。

以上是我做业务网关的一些总结。时间匆忙,后续会慢慢完善。
也欢迎大家来评论交流。


如果觉得本文对您有帮助,可以请博主喝杯咖啡~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1568938.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构和算法:十大排序

排序算法 排序算法用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用,因为有序数据通常能够被更高效地查找、分析和处理。 排序算法中的数据类型可以是整数、浮点数、字符或字符串等。排序的判断规则可根据需求设定,如数字大小、字符 ASCII…

【算法】单单单单单调栈,接接接接接雨水

【算法】单单单单单调栈,接接接接接雨水 今天没有小故事。 参考以及题单来源: 代码随想录 (programmercarl.com) Part 1 啥是单调栈? 1.啥啥啥是单调栈? 栈的特性想必各位再熟悉不过了:先进后出。栈仅仅有一个出口&a…

递归算法讲解2

前情提要 上一篇递归算法讲解在这里 递归算法讲解(结合内存图) 没看过的小伙伴可以进去瞅一眼,谢谢! 递归算法的重要性 递归算法是非常重要的,如果想要进大厂,以递归算法为基础的动态规划是必考的&…

Android安卓开发 - 简单介绍(一)

最近呢需要重构还有维护安卓项目,所以最近会从零开始梳理开发的一些知识点以及开发的内容 前面已经写了安装的教程,idea怎么安装,还有官方的开发工具Android Studio怎么安装 2024最新版Android studio安装入门教程(非常详细&…

SpringBoot整合ELK8.1.x实现日志中心教程

目录 背景 环境准备 环境安装 1.JDK安装 2.安装Elasticsearch 3.安装zookeeper 4.安装Kafka 5.安装logstash 6.安装file beat 解决方案场景 1.日志采集 1.1 应用日志配置 1.1.1 创建logback-spring.xml文件 1.1.2 创建LoggerFactory 1.1.3 trace日志的记录用法 …

K8S- Deployment 的滚动更新 Rolling Update

滚动更新 这里的更新指的不是更新deployment 本身的属性(label/ replicas)等, 而是更新POD 的container 的版本 更新方法通常有两种 是直接update deployment配置, 注意只有update了template中的内容(与container相关) 才会触发更新用kubectl set ima…

C++ | Leetcode C++题解之第7题整数反转

题目&#xff1a; 题解&#xff1a; class Solution { public:int reverse(int x) {int rev 0;while (x ! 0) {if (rev < INT_MIN / 10 || rev > INT_MAX / 10) {return 0;}int digit x % 10;x / 10;rev rev * 10 digit;}return rev;} };

鸿宇多用户商城 scan_list.php SQL注入漏洞复现

0x01 产品简介 鸿宇多用户商城是一款支持各行业的多商家入驻型电商平台系统,商家版APP,微信商城,小程序及各种主流营销模块应有尽有,是一个功能强大的电子商务平台,旨在为企业和个人提供全面的在线购物解决方案。 0x02 漏洞概述 鸿宇多用户商城 scan_list.php 文件 data[fa…

Python 网络请求:深入理解Requests库

目录 引言 一、Requests库简介 二、安装与基本使用 三、requests库的特性与优势 四、requests库在实际应用中的案例 1.get请求 2.post请求 3.超时重试 4.headers设置 5.session会话 6.携带cookie​​​​​​​ 7.携带代理​​​​​​​ 8.携带身份认证​​​​​…

FLink学习(三)-DataStream

一、DataStream 1&#xff0c;支持序列化的类型有 基本类型&#xff0c;即 String、Long、Integer、Boolean、Array复合类型&#xff1a;Tuples、POJOs 和 Scala case classes Tuples Flink 自带有 Tuple0 到 Tuple25 类型 Tuple2<String, Integer> person Tuple2.…

InternLM2-lesson2作业

书生浦语大模型趣味 Demo 视频连接&#xff1a;https://www.bilibili.com/video/BV1AH4y1H78d/?vd_source902e3124d4683c41b103f1d1322401fa 目录 书生浦语大模型趣味 Demo一、基础作业二、进阶作业 一、基础作业 第一次执行&#xff1a; 第二次执行&#xff1a; 第一次执…

最新408试卷分析+备考经验分享

408出题再糟糕&#xff0c;你是不是还是要考&#xff1f; 别管出题人出多刁钻的题&#xff0c;大家拿到的卷子都是一样的&#xff0c;要难就都难&#xff0c;要刁钻就一起g... 所以再潜心钻研出题规律或出题套路&#xff0c;不如多花些时间去多复习巩固几遍知识点&#xff01…

Leetcode_2两数相加

文章目录 前言一、两数相加1.1 问题描述1.2 解法一&#xff1a;分别将链表转为数字&#xff0c;然后相加1.3 代码实现1.4 解法二&#xff1a;分别将对应位置数字相加1.5 代码实现 二、使用步骤1.引入库2.读入数据 前言 链表是一种物理内存非连续存储&#xff0c;非顺序的线性数…

深入理解C/C++的内存管理

在C和C中&#xff0c;高效的内存管理是编写性能优化和资源高效利用程序的关键。本文将深入探讨C/C内存管理的各个方面&#xff0c;包括内存的分布、C语言和C中的动态内存管理方式&#xff0c;以及new和delete操作符的使用 C/C内存分布 C和C程序的内存可以分为以下几个区域&…

【Linux】-进程知识铺垫①计算机硬件的组织:冯诺依曼体系结构详细解读②关于操作系统对软硬件及用户的意义

目录 ​编辑 1.关于计算机的体系结构 1.1 冯诺依曼体系结构的诞生 2.冯诺依曼体系结构 2.1 cpu:运算器&#xff1a;更多的是让cpu具有特殊的数据计算功能&#xff1a; 2.2 控制器 2.3输入设备 2.4输出设备 3.计算机各个硬件设备之间的关系 4.内存与计算机效率 5.关于为什么总说…

AI智能校色解决方案,专业级画质提升

由于拍摄环境、设备性能以及编辑经验等多种因素的影响&#xff0c;视频画质往往难以达到理想状态。这时&#xff0c;一款高效、智能的校色解决方案就显得尤为重要。美摄科技凭借深厚的图像处理技术和AI算法研发实力&#xff0c;推出了全新的AI智能校色解决方案&#xff0c;助力…

Kubernetes(k8s)核心资源解析:Pod详解

Kubernetes核心资源解析&#xff1a;Pod详解 1、什么是Pod&#xff1f;2、Pod 的组成3、Pod 如何管理多个容器4、Pod 的网络5、Pod 的存储方式6、Pod 的工作方式6.1 自主式 Pod6.2 监控和管理 Pod6.3 Pod 的创建流程 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收…

力扣热题100_链表_2_两数相加

文章目录 题目链接解题思路解题代码 题目链接 2. 两数相加 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 …

网络安全 | 什么是云安全?

关注WX&#xff1a;CodingTechWork 云安全-介绍 云安全是为了解决企业安全所面临的外部和内部威胁&#xff0c;它是一组程序和技术的集合。企业在实施其数字化转型策略&#xff0c;并将各种云端工具和服务纳入企业基础架构中时&#xff0c;需要云安全保障业务顺利进行。 云计…

ComfyUI ClipSeg插件报错- resize_image出错应该怎么办

上一篇刚介绍了这个插件&#xff0c;结果emm..很快发现事情并不简单...结果又报错了。 后台报错信息&#xff1a; Unused or unrecognized kwargs: padding. !!! Exception during processing !!! Traceback (most recent call last): File "F:\ComfyUI-aki\execution.p…