H项目心路历程记(三)

现状

对于一个基于微服务的中台系统来说,需要以什么样的颗粒度来拆分系统。这一直是基于微服务架构的系统中绕不过去的问题。

已H项目来说,当初考量是这么划分微服务的,其中

  • 基础服务: 作用主要是负责用户,角色权限,登陆鉴权,以及一些基础主档数据的日常维护功能
  • 商品服务: 物料,销售商品,款式,平台商品等和实际商品相关的主档数据
  • 库存服务:负责管理各个库存层级库存(总库存,冻结库存,在途库存等),以及展示各种出入库单据,并向其他微服务提供出入库服务接口。
  • 订单服务: 负责接收各个公域平台的ToC订单,售后单(含发货前退款,退货,换货单等),并根据配置策略进行单据路由(分仓,分物流,Hold单,加送赠品,通知仓库发货,通知平台发货等)
  • 接口服务: 主要承揽中台服务和第三方平台间的交互处理(主要是第三方平台发起的主调通知以及回调)

上述五个服务为核心服务,是保证整个中台系统基础业务功能运行的重要组成部分。除此之外还有,

  • 采购服务:负责ToB业务线的处理
  • 财务服务:满足一些企业的财务对账以及开具电子发票的功能
  • 报表服务: 每天,每周定期从其他服务(主要是订单)
  • 会员服务:管理各类会员

等7,8个非核心服务,组成非核心业务。来构成一个完整的中台系统。

拆分策略以及缺点

当时主要考虑到整个中台系统是可以拆分出售,客户可以只选择购买自身需要的服务,而不需要的服务则无需购买服务,所以当初在拆分服务上在业务领域层面上需要更加低颗粒度;同时考虑到再业务高峰期(下午到晚上,就是各位疯狂刷淘宝,京东的时候)各个服务的负载是各不相同的,如果在高峰期对于特定服务需要进行精准扩容的话,拆分的颗粒度也需要更加细致,所以结果上导致整个系统满血运行起来要至少要十几个服务,外加一些没有云平台的中间件。整个Kubernetes环境中,至少要有十五六个Deployment,考虑到系统稳定性,一个Deployment也至少要有2到3个Pod,那整个系统至少就是50个Pod。

对于一个中大型的电商企业再服务器成本上可以负担这样一个能支持50个Pod的Kubernentes集群(考虑到需要一些中间件的云产品,当时估算最小的服务器成本都要到70K/年,一个中大型企业能面对双十一流量的服务器成本都要到300K/年以上)。所以整个系统对于中小型企业来说前期的投入负担相当大(尤其是在后疫情时代的2022年,2023年),以至于早起的好几个客户都是需要我们自己贴补服务器费用的。

复盘

现在重新复盘当初服务的拆分策略,个人觉得对于核心业务来说,相似领域的一些服务应该可以适当的聚合。比如上述的五大核心服务上中的商品服务,订单服务,库存服务三个核心服务应该可以合并在一起。首先这3个核心服务是H项目的最重要的竞争力,几乎不可能出现把这3个服务拆分出售的可能性。

而且在业务高峰期有订单服务有新订单处理时,势必会产生库存的变化,因此会调用到库存服务。三个服务之间的调用是相当频繁的,尤其是负责处理商品主档数据的商品服务,无论是通过HTTP或是其他方式的远程调用的话,都会有许多浪费的带宽。如果把三个服务整合,那么之间的调用基本上就是线程间的交互,无论是效率和可靠性上都会有更加显著的提升。

结尾

怎么拆分服务一直是个难题。虽然事后复盘的时候我的很多想法也不一定是正确,但是什么样的规模的客户需要使用什么样的拆分颗粒度,都是需要仔细考虑的。绝对不是一尘不变的。

H项目心路历程记 (二)

(书接上文)

前文已经吐槽了产品设计以及中间件上的相关问题。接下来讨论一下技术上的一些问题。

关于配置

对于一个Spring的应用来说,应用配置怎么存取其实并不能说是一个技术问题。更应该算是设计或是架构问题。哪些配置应该进入配置文件,通过微服务配置中心(例如Nacos, Apollo,Kubernetes ConfigMap等)来进行在线配置,哪些配置应该通过其他方式来配置,亦或者哪些配置项目看似是配置项其实本质上就是常量。实际上截止现在H项目的确在有些微服务上是完全没有厘清三者之间的区别,以及界限。以至于现在堆积在配置文件中的内容实在太多。多个运行环境需要统一维护的难度实在太大。

对于配置来说,理想的状态应该是下面这样的。

  1. 通过微服务配置中心来影响Spring的配置文件来实现动态配置的方案,通常会伴随Spring容器的重启(有些情况下甚至需要整个Docker容器),所以存在这里的配置项应该是随着运行环境改变才会需要改动的配置。例如:

    • 外部系统的链接信息(比如MySQL, Redis的连接信息)
    • 外部系统的认证凭证(数据库用户密码,第三方系统的密钥等)
    • 因为运行环境不同导致的不同变量 (例如消息队列的Topic,Group需要根据运行环境使用不同的名称)
  2. 通过数据库或是Redis存储的配置信息,这类配置主要是应该以业务级别的配置,可以通过页面进行修改,瞬间或是几分钟内生效,且不会造成微服务重启(无论是Spring或是Docker容器的重启)。对于这类配置有几个问题还是需要注意的。

    • 配置是否需要集中管理,每个微服务,都有自己独特的业务配置,这些业务配置是否需要集中到一个微服务统一管理,还是分散到各自的微服务进行单独管理。
    • 全局性统一管理的业务配置对外保留接口应该利用缓存有效的减少数据库访问的频次。甚至可以只使用内存而不是Redis来缓存这部分内容。如果使用内存的话则需要考虑失效时效和重新装载的机制。
  3. 无效配置,实际上很多配置说是可以修改,其实自从开始使用的那天开始就没有修改过。比如用来做分布式锁的键值。这部分需要梳理出来直接以常量的形式做在代码里。

数据清理以及归档

对于电商行业的零售业务来说每天产生的单据比如订单少做200,300条,多的是上千条。加上明细数据。几个月以后这些数据就会给MySQL的库表增加检索压力。整个H项目在这块是相当薄弱的。只依靠云平台的能力,对一些不需要留档的数据进行清理。远远没有能定期归档。

对于数据清理这块大致可以分为这几类

  • 定期可以无脑物理删除的数据,比如一些日志类数据。
  • 定期直接归档的数据,归档后即可清理的数据。
  • 业务必须处理完成后才能归档并清理的数据。例如订单数据等。

对于数据归档的时候,还需要考虑一些相关联单据数据一并归档。比如订单归档时,同时需要归档相关联的退换货单,发货单,以及操作日志等等。 归档格式基本上就是考虑单条数据以JSON格式存储,然后一个时间段的数据做成压缩包存储在OSS上,然后提供给客户下载。

而对于归档数据的检索功能,客户选择需要归档的数据,我们可以将归档的数据(每个压缩包内的所有数据)导入到一个主数据库以外的,临时文档数据库(例如MongoDB),然后针对这个临时的文档数据库进行检索。当然提供的检索项目也会远少于热数据的检索项目。这个临时文档数据库基本上保持一两天数据,定期就全部回收。

未完待续

H项目心路历程记 (一)

前言

2021年年中从前一家公司离职,经过朋友介绍到了现在公司参与了Halfling项目(以下简称H项目),并从零开始的设计开发到第一个客户交付的全部流程。

H项目是一个面向零售行业在电商时代的智能一体化中台产品。项目最早是从2021年中开始立项投入,期间经过数个版本迭代,以及2022年初的魔都封城等诸多困难。终于在2023年Q2季度完成了针对首个客户的上线。

作为从头开始全程参与项目的技术人员,经过2年多的产品研发期间有不少想吐槽和想说的。

业务设计

最初H项目是以打造一个面向零售行业业务中台为目的进行项目立项的,其中一个很重要的点就是希望除核心组件以外都可以独立售卖。所以在架构设计的使用当下最流行的微服务架构(基于Spring Boot + Kubernetes集群的解决方案), 再做业务拆分到服务的时候,进行了更细颗粒度的拆分。所以前前后后一共拆分了近20个微服务。

实际上到了2022年Q2前后,两个核心服务订单管理和库存服务实现以后发现,各个业务服务中间的耦合程度比想象中的还要精密,单个服务拆开单独售卖几乎不可能(应该说是完全不可能)。 然而由于服务拆分的过细,导致一个服务需要频繁访问其他服务获取大量数据。尤其类似订单关联数据比如商品的主档数据,会穿越好几个服务。而每个服务都会重复去获取主档数据,造成了大量的额外请求。 虽然最后在所有服务底部兜底使用同一个Redis作为缓存来减少额外的请求调用。

随着时间来到2023年产品组负责人离职后,整个H项目的方向有从行业的业务中台降级成中小企业的订单管理系统(Order Management System, OMS)。前期做的很多设计比如用户权限体系(当初需求是要精确到每个页面的每个按钮,如果下拉列表框有外调请求也需要做权限控制)等就显的很重,导致用户在使用以及配置都很不方便。而有些设计就显得稍显欠缺。 所以整个2022年Q4到2023年都是在对业务层上反复横跳,比如库存模型和商品模型的调整。这点是相当影响团队士气,同时也加剧了研发组和产品组之间的矛盾。

然后还有一个很重要的问题是多租户的态度上。早起项目是以服务中大体量客户为目的,所以设计之初并没有考虑多租户的问题。但实际项的进展过程中发现部分中大体量的客户也需要类似多租户的功能。这类客户需求与其说是需求多租户,不如说是希望有一个多租户级别的数据隔离,每个业务部门只能操作自己的业务部门的数据(数据包含商品,品牌,属性,平台店铺,订单,售后单据等),而总部依据可以查看一些汇总信息(例如销售对账单据等)。而随着2022年疫情状况严峻,很多中大客户对于内部平台升级和替换都变的更加谨慎和保守。所以当项目方向变成中小企业OMS时,多租户SaaS化的需求就更加迫切了。所以后期如何在原有系统上在扩展一套多租户SaaS化功能也是个课题。

中间件

H项目使用的中间件中比较特别的有分布式任务管理系统xxl-job,消息队列RocketMQ,这两个中间件都是第一次使用。关于这两个中间件也有不是可以谈谈的。

分布式任务管理系统 XXL-JOB

作为一个开源的分布式任务管理系统,xxl-job足够简单且相当容易上手使用,无论是基于Spring,还是原生Java做开发都是相当简单的。

而不足之处也很明显,最近1年版本迭代速度明显变慢,有些功能和BUG都有待处理的。比如想实动态创建定时任务这类功能就必须自己动手去对xxl-job-admin(调度中心)进行修改。

对于任务执行日志这块,现在xxl-job也是文件格式在执行器所在服务器上进行输出的,当执行器容器化或是运行在Kubernetes集群中的时候,当容器或是Pod重启后,就会丢失日志。这点在后期运维以及调试上还是有点麻烦的。

消息队列 RocketMQ

阿里出品的消息队列如果基于云产品来使用的话问题不大(阿里云卖的价钱也是有点黑的),但是在运维上还是有点问题的比如Topic,Group这些东西都只能人工去添加,阿里云并没有提供导入服务。全系统如果使用消息队列的场景多的话,维护这些Topic和Group还是很酸爽的。还有一点RocketMQ重复消息这一点是无法保证的。用阿里云的原话

绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。

实际运用上,在大数据量并发的情况下,我们实际上是遇到了消息重复消费的问题。这点还是需要通过代码干预(例如增加一个唯一MsgId,对MsgId做分布式锁来解决)。

还有一点就是在旧版本SDK的默认配置下消费者的负载是有点小问题,总是有一个节点会比其他节点更容易消费到大量消息。据阿里云自己说明在新版本SDK的默认负载配置也已经修复这个问题了。

(未完待续)

Largrange项目架构与设计回顾 (二)

Largrange项目架构与设计回顾 (一) 里面我讲了一下项目开始架构和技术选型的一些内容。这一章来聊聊业务设计上的这点事。

总体来说项目设计的时候,我们对业务模块的划分和拆解总体上来说都是遵循着高内聚,低耦合的原则来进行划分的。大部分和业务相关的服务,都还是能很好的进行功能划分的。但是有一些和具体业务关联性并不是很高的服务在界定与实现时出现了一些问题。

任务调度服务

由于平台方面会有一些控制命令下发给Android设备,而且下发的命令并不一定都是实时的,大部分都是指定一个时间来下发,所以在设计的时候就考虑到需要一个任务调度服务(以下略称cron),来处理这些下发指令以及未来可能会有的定时批处理任务的业务需求。技术选型的时候考虑到整个服务是构建在Kubernetes上的一个分布式的微服务架构,所以就没有选择Spring Scheduler,而是使用了Quarter来支撑整个cron

从技术选型上来说cron没有什么问题,但是在设计如何使用cron上还是有点问题的, 我们先来看看已推送服务为例,整个平台中cron的处理流程是怎样的。

  1. Platform 调用 cron的创建定时任务Job的接口(接口的参数为推送时使用的相关参数)
  2. cron 创建Quartz的Job和Trigger,并将Job和Trigger的Name(Quartz中Job和Trigger的唯一标识符)返回个Platform
  3. cron 在指定时间触发推送的Job,即调用Push服务的推送接口
  4. Push的推送接口调用第三方服务商的推送服务,并将第三方推送服务的调用结果返回给cron
  5. cron通过调用Platform预留的推送服务回调地址将第三方推送服务调用接口返还给Platform
  6. Platform 将第三方推送服务的调用结果留档保存,并继续业务处理

设计之初考虑到不想在cron中牵扯到具体的业务, 所以设计了一个Platform的回调接口来处理推送后的具体业务处理。虽然保证了cron尽量减少了和业务逻辑接触与数据库的访问。但是前前后后要访问集群内部的其他微服务2次,额外增加了网络开销,以及因为网络通信造成额外的通信失败风险。而且针对每种不同的定时任务,都需要额外开发一个QuartzJobBean,很难形成统一的QuartzJobBean来进行处理。今后如果还有相似项目还是需要重新考量一下如何设计一个更加完善的定时任务。

总结

暂时就想到了这些东西,今后想到啥还会继续在这里补存。

Largrange项目架构与设计回顾 (一)

项目背景

从去年年底开始一个老客户希望在他们的一个传统的机械设备(后面略称 E机关 )上外装一个Android设备。 Android设备和 E机关 之间通过串口或是RJ45接口进行数据交互,主要是Android设备获取 E机关 内部的数据,并不会通过接口来控制 E机关 。 Android设备则通过4G 来和平台交互,上报Android设备的状态数据,同时接受平台的控制。以此来实现让传统机械设备也能拥抱物联网的概念。

最后围绕着客户的需求分成了3个项目来并行推进

  1. Android设备的硬件设备的设计、选材、样机制作与量产规划
  2. 在Android设备上,进行与E机关以及平台进行交互的APP开发
  3. 用于Android设备交互的平台的架构、设计与开发

我们平台Team就负责 3.用于Android设备交互的平台 并命名Lagrange (拉格朗日) 。

设计&架构

整个平台这块不仅需要向Android设备的APP提供数据交互接口,还需要有一个供相关运营人员使用的前端Web应用,以及与云平台(主要是Aliyun) 交互的功能。所以考虑到多方面使用微服务的架构来实现整个平台端。
由于E机关的工作工况不能保证长期较的稳定的连接到4G网络,所以我们并不考虑使用Socket长连接的方式来做APP和平台之间的数据交互,要实现平台对Android设备进行反控的话,只能实现推送方式来把控制命令下发给Android设备,所以还需要有一个第三方推送服务商交互的服务。同时控制推送也有实时和非实时以及定期推送的需求,所以可能还需要一个任务调度的服务。

根据对上述业务进行梳理,我们将项目分成几个服务

  • 提供Android设备的交互接口的 App Service
  • 提供运营人员使用前端Web应用 Platform Web Service
  • 前端Web应用使用到的一些接口 Platform Service
  • 负责第三方推送服务商交互的 Push Service
  • 提供云平台鉴权用的 Auth Service
  • 用来管理任务调度的 Cron Service

由于前一个项目实施的时候没有使用容器部署,每当访问量峰值的时候,我们这些码农兼运维就各种加班,所以这次项目决定直接将服务容器化,同时选择了Kubernetes来管理容器。由于客观原因线上的Kubernetes直接购买了Aliyun的托管版Kubernetes服务。 内部的开发测试环境则使用了 Rancher 2.0 来构建Kubernetes集群。

技术选型

a. 后端服务选型

由于不考虑长连接的原因,所以在后端服务在技术选型上基本就不考虑Netty了,直接上Spring大礼包。

  • Spring Boot 开发接口
  • Spring Data 配合 JPA 来进行数据的持久化
  • Spring Cloud Kubenetes 来做数据的Config的autoreload
  • Quartz 负责处理任务调度

服务之间调用都使用HTTP服务, 服务发现也有Kubernetes的DNS机制支持。服务网格则选择了比较成熟的Istio,主要还是Aliyun的Kubernetes可以集成Istio,部署和使用都相当方便。

b. 前端Web应用选型

因为前端Web应用主要是给运营人员使用,所以我们考虑使用Single Page Application来做个前后端分离的Web应用。框架这块由于团队成员基本上没有什么前端开发经验,基本都是后台写Java的码农,所以框架选择有点随性,直接就点名了vue.js。前端控件库则用的是阿里系的Antd。

c. 数据持久层

主数据库选择了mysql,缓存用的redis。这些都是团队比较熟悉的。由于一些特殊的业务需求和使用场景,我们还加了一个mongodb来做为一个副数据库,主要存放一些特殊业务使用的数据。

d. DevOps

用了相当传统的GitLab CE 加上Jenkins的组合,实现前后端代码的自动编译,推送到私有的镜像仓库(使用Aliyun的镜像仓库服务)

最后

以上基本是项目最早做设计时候的各种考量。暂时先写这么多,过两天再回顾一下当初设计上有哪些觉得不足的地方。

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×