0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

探讨篇(三):代码复用的智慧 - 提升架构的效率与可维护性

京东云 来源:京东物流 冯志文 作者:京东物流 冯志文 2024-12-27 15:58 次阅读

作者:京东物流 冯志文

前两篇从服务粒度和服务内的分层架构角度探讨,本文继续从服务间代码复用角度探讨。

背景

在分布式架构中,代码复用是个难题。那么如何处理代码功能共享的问题呢?本文结合日常实践中的案例,介绍几种分布式架构中管理代码复用性的技术。包括代码复制、共享代码库(jar包)、共享服务、边车服务。对于每一种技术,列出优缺点、适合场景权衡。

本文的观点源自我在学习与实践过程中的深思熟虑,尚处于不断探索和验证的阶段。希望能“抛砖引玉”,激发更多的讨论与交流。让我们共同进步,在探讨与实证中寻求真知。

一、代码复制

共享的代码被复制到每一个服务中。这种技术在服务早期比较流行。虽然现在代码复制比较少见,但它还是解决跨多个分布式服务的代码复用的有效技术。但这种缺点很明显,因为如果在代码中发现错误或需要对代码进行重构改造,需要在包含该代码库的所有服务变更。

这种技术在一些场景比较有用,比如服务需要的高度静态的一次性代码。这种类型的代码非常适合复制,因为它是静态的并且不包含任何错误。比如很多通用的业务识别逻辑就用这种方式,在不同应用代码库个应用中编写。

案例1:根据sendpay标位判断XXX代码

public static boolean isXXX(String sendpay) {
    boolean flag = false;
    String sendpay_x = Character.toString(sendpay.charAt(x));
    String sendpay_y = Character.toString(sendpay.charAt(y));
    if (("1".equals(sendpay_y) || "2".equals(sendpay_y) || "0".equals(sendpay_y))
            && "1".equals(sendpay_x)) {
        flag = true;
    }
    return flag;
}

案例2:新功能上线DUCC开关

/**
 * XXX功能 控制开关
 */
private boolean enableLargeApplianceSendMsg = false;

public boolean isEnableLargeApplianceSendMsg() {
    return enableLargeApplianceSendMsg;
}

public void setEnableLargeApplianceSendMsg(boolean enableLargeApplianceSendMsg) {
    this.enableLargeApplianceSendMsg = enableLargeApplianceSendMsg;
    log.info("enableLargeApplianceSendMsg: {}", enableLargeApplianceSendMsg);
}

代码复制技术
优点 1、无代码共享
缺点 1、代码分散各应用中,变更起来比较复杂。 2、无法保证跨服务代码一致性,有时候容易遗漏某个地方未修改 3、缺乏跨服务版本控制能力
适合场景 1、关联应用较少,比如2-3个左右可以接受,如果牵扯应用较多(比如5+,具体如何确定“较多”的基准或者指标待定)则不适合,同时核心要思考是不是服务粒度拆分的太细了? 2、简单的静态代码(比如公共工具类,业务逻辑通用类) 3、变更频率较低

二、共享代码库

共享代码库是共享技术的常用技术之一。共享代码库是一个外部组件(比如JAVA的jar包),共享代码库比代码复用更高一层级。基于功能划分,提供较为独立的功能。比如目前采用了根据基础能力和业务模块抽象时效内核JAR包方式,详见下图中时效计算内核jar包。为什么要采用这种方式呢?后文会说明权衡点。

jar包引入看起来简单,在编译时被整合和共享。但其实也有利弊取舍和复杂性。其中最重要的是代码库的共享库粒度和版本控制。

1、依赖管理和变更控制

如果粗粒度共享库中任何类文件发生变更,都需要每个服务变更、测试、部署。这极大的增加了共享库更改的整体测试范围。粗粒度共享库的变更会影响多个服务,但会减少依赖关系。

将共享库分解为更小的基于功能的共享库(比如拆分为 ABCDE等),这样有利于变更控制和整体可维护性。但这会造成依赖管理的混乱。如下图,业务根据不同业务域拆分不同 jar包,导致jar包依赖复杂。

chaijie_default.png

共享库对少量的应用可能并不重要,但随着服务数量增加,变更管理和依赖管理相关的问题也会增加。

建议:避免大的粗粒度的共享库,尽可能争取更小的、功能分区的库,有利于变更控制而不是依赖管理。

2、版本控制策略

对于共享库来说,版本控制需要向后兼容(后退、考虑旧版本兼容),同时也需要高度敏捷性。比如a.jar包变更升级版本为1.1,只需要1个服务上线,其他N个服务不会有任何影响。这虽然看起来版本控制很简单,但同样存在权衡和隐藏的复杂性。比如下次N个应用也需要上线。并且依赖管理混乱(服务ABC依赖a.jar版本1.1,服务DEF依赖a.jar版本1.0),复杂性不仅体现在版本变更的通知,也存在旧版本弃用的情况。

共享代码库技术
优点 1、减少重复代码 2、不受网络影响,性能更加稳定。对性能要求较高的场景使用该方式会有一定优势; 3、节省服务器硬件成本,尤其服务器QPS高,需要部署大量服务器资源的场景下。 4、支持版本变更
缺点 1、可维护性较差,依赖了该组件的服务都需要跟着一起升级,随着时间的推移,梳理维护起来会很麻烦; 2、组件升级成本高且风险较大,成本包括了开发维护升级各个服务的成本、测试验证的成本及运维发布的成本,需升级维护的服务越多,成本越高,对应的风险也越大。 3、容易jar包冲突 4、版本沟通可能很困难 5、依赖可能难以管理、版本弃用可能很复杂 6、如jar包过大,维护困难,并且调用方引入过多项目无用代码
适合场景 1、无隐形依赖,更新频率低和更新影响小的代码,比如通用的判断订单、运单校验 2、服务器资源硬件成本控制要求较为严格,尽量降低成本。 3、内部一些公共功能处理场景,不涉及到数据库资源层面的连接和调用,适合组件化的方式; 4、对性能要求较高的应用

为什么时效内核需要采用jar包给下单前下单后各应用这种方式呢?结合上面的优缺点,主要权衡核心点如下

1.应用场景相关:XX是下单前商详结算等高并发场景,下单后订单生产节奏控制。

2.降本:预估降低了服务器硬件成本XXX核左右

3.性能:通过JAR包的依赖的方式来较少RPC调用,提升了接口性能TP99,尤其是用户在商详、结算提单页面

4.更新频率低:由于时效内核更新频率较低,一年1-2次左右的改动点

三、共享服务

共享服务技术通过将共享功能服务化来避免重复使用。对应上面改造,把时效内核jar包进行服务化时效内核应用,具体架构图如下:

共享服务是分布式中常见的共享服务的方法,但也需要权衡,比如变更风险、性能、可伸缩性、容错性。

1、变更风险

使用共享服务变更共享代码是一把双刃剑。 只需要共享服务部署上线,但共享服务的变更可能在运行时破坏其他服务。那必须牵扯版本控制、共享代码库是在编译的时候绑定版本控制,降低更改风险。但如何在共享服务中版本化变更呢?使用API版本控制。但使用API版本控制有个问题,很多服务协议不是restful api,而是rpc或者消息mq,这样会使得版本控制复杂。

共享服务虽然版本控制可以帮助降低这风险,但它的应用和管理更复杂。

2、性能

共享功能服务必须进行服务间调用,存在网络延迟开销而影响性能。

3、可伸缩性

共享服务一定要随着调用服务的规模进行伸缩

共享服务技术
优点 1、减少重复代码 2、高度解耦: 每个服务都是独立的,可以独立开发、部署和扩展,提高了系统的可维护性和可扩展性。 3、快速迭代: 服务可以根据需求快速更新和迭代,更容易适应业务变化。 4、资源隔离,互不影响,对调用方隐藏内部细节。
缺点 1、增加硬件成本 2、性能受到网络延迟影响 3、服务依赖导致 容错性、可用性、可伸缩性、吞吐量问题 4、版本控制可能困难 5、分布式固有问题:比如一致性、分布式事务处理等
适合场景 1、适合多语言的环境 2、共享功能频繁变化 3、不需要太多的服务器资源 4、对性能要求不高

四、边车和ServiceMesh服务网格

"边车服务"(Sidecar Pattern)这个术语来源于摩托车的边车(sidecar),这是一种附加在摩托车旁边的一轮车厢,可以搭载乘客或货物,但它不是摩托车本身的核心部分。

边车服务(Sidecar Pattern)在微服务架构中用于将一些与业务逻辑不直接相关的控制面(如注册发现、熔断限流、pfinder链路追踪监控、DUCC配置管理等)从应用程序中分离出来。这样,应用程序可以专注于业务逻辑,而边车服务则负责处理其他方面的问题。

chaijie_default.png

边车服务的关键特点包括:

复用性:由于边车服务可以被多个主应用共享,因此一些通用的功能(如服务发现、断路器、限流器等)可以在不同的服务之间重用,减少了代码的冗余

隔离性:边车为主应用提供了一个清晰的隔离层,使得主应用可以专注于业务逻辑,而不必关心其他非功能性的问题。 边车服务是主应用程序的附属,为主应用提供支持和增强功能。

易于维护:边车的引入使得对于共享功能的更新和维护变得更加简单,因为这些功能被集中到单独的服务中,不需要在每个应用中单独进行修改。

透明性:对于主应用程序来说,边车的存在应该是透明的,主应用不需要知道边车的具体实现细节。

独立性:边车服务可以独立于主应用程序更新和维护,无需修改主应用程序的代码。

通过使用边车模式,开发人员可以将关注点分离,使主应用程序更加简洁,只关注业务逻辑的实现,而将服务治理等通用性问题交给边车服务处理

如果每个服务都包含边车组件,那么它就形成了服务网格。每个服务右边的盒子都互相连接,形成一个“网格”

chaijie_default.png

服务架构是围绕各自领域组织的,但服务治理运维耦合需要横切这些领域

五、总结

技术最终是要服务于业务,每种技术选择没有绝对的好坏,各有优缺点,适合场景。具体应该用哪一种,需要根据成本、团队技能、系统的未来发展综合考虑,目前团队系统中上面几种情况都存在。正如软件架构定律:软件架构中的一切都是在权衡,架构背后的原因比方法更重要。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 分布式
    +关注

    关注

    1

    文章

    899

    浏览量

    74507
  • 代码
    +关注

    关注

    30

    文章

    4788

    浏览量

    68611
  • 架构
    +关注

    关注

    1

    文章

    514

    浏览量

    25470
  • 复用
    +关注

    关注

    0

    文章

    6

    浏览量

    11991
收藏 人收藏

    评论

    相关推荐

    最新可用隔离元件的性能提升如何帮助替代架构在不影响安全的前提下提升系统性能

    本文探讨了系统架构选择对电源和控制电路设计以及系统性能的影响。本文还将说明最新可用隔离元件的性能提升如何帮助替代架构在不影响安全的前提下
    的头像 发表于 10-13 06:12 7849次阅读
    最新可用隔离元件的性能<b class='flag-5'>提升</b>如何帮助替代<b class='flag-5'>架构</b>在不影响安全<b class='flag-5'>性</b>的前提下<b class='flag-5'>提升</b>系统性能

    如何应用设计模式的思想写出更优质的嵌入式软件应用程序代码

    而言,代码复用可移植性要求越来越高,以获得更短的项目周期 和更高的可维护性。下面是AIOT时代嵌入式设备的常见的软件框架。
    发表于 06-12 09:38 244次阅读
    如何应用设计模式的思想写出更优质的嵌入式软件应用程序<b class='flag-5'>代码</b>

    谈谈几种芯片设计增加代码复用的方法

    很多芯片在设计之初,就已经考虑如何增加代码复用,尽量减少工作量,降低错误概率。
    的头像 发表于 09-12 09:55 1346次阅读
    谈谈几种芯片设计增加<b class='flag-5'>代码</b><b class='flag-5'>复用</b><b class='flag-5'>性</b>的方法

    15 年代码经验,总结出提升 10 倍效率件事!

    【译者注】本文作者 Matt Watson 已经写了超过 15 年的代码,也由此总结出了提升 10 倍效率件事。Matt 表示,一个 10 倍
    发表于 10-14 17:35

    嵌入式开发程序的架构和命名规范

    总是因人而异。很多较大型的代码写到最后总是捂不住各种冒出来的bug,其实最大的问题是根基没有打牢。当然,主要是程序架构的问题,架构的清晰,程序的可读
    发表于 11-05 08:22

    嵌入式软件开发过程之程序代码分层

    在嵌入式软件开发过程中,在程序架构的搭建完成之后,为了提高项目代码的可读可维护性等,应对程序代码分层
    发表于 12-21 06:13

    学习架构-RAS概述

    健壮、可靠的计算机系统有个关键属性:可靠、可用可维护性(RAS)。 这些属性可以定义如下: 可靠可靠的系统始终按照其规范提供正确的
    发表于 08-08 07:53

    面向可维护性的软件体系结构设计

    面向可维护性的软件体系结构设计_胡文生
    发表于 01-08 15:15 1次下载

    代码的可重用代码开发和确保可维护性的关键

      所有嵌入式软件开发人员都应该了解库的工作方式及其提供的好处。代码的可重用是高效、高效的代码开发和确保可维护性的关键。
    的头像 发表于 06-30 14:58 1672次阅读
    <b class='flag-5'>代码</b>的可重用<b class='flag-5'>性</b>是<b class='flag-5'>代码</b>开发和确保<b class='flag-5'>可维护性</b>的关键

    科普一下Verilog代码命名规范

    命名规范包括模块命名规范和代码命名规范,代码命名需要有确定的含义,提高代码可读可维护性
    的头像 发表于 11-17 09:54 3866次阅读

    ​如何提高HPC SoC的可靠、可用可维护性级别

    在大型数据中心和超级计算机的领域,高性能计算 (HPC) 已经变得相当普遍,并且在某些情况下,在我们的日常生活中必不可少。正因为如此,可靠、可用可维护性(reliability
    的头像 发表于 02-15 11:37 697次阅读

    MVVM+RAC的基本概念和使用方式

    在iOS开发中,采用合适的架构模式能够提高代码可维护性和可测试
    的头像 发表于 06-06 14:55 1246次阅读

    如何写出易维护的嵌入式代码怎么写?

    面向对象的语言更接近人的思维方式,而且在很大程度上降低了代码的复杂,同时提高了代码的可读可维护性,传统的 C
    发表于 08-23 09:42 266次阅读
    如何写出易<b class='flag-5'>维护</b>的嵌入式<b class='flag-5'>代码</b>怎么写?

    javaWeb的MVC架构的原理

    可维护性、可扩展性和重用。在本文中,我们将详细介绍JavaWeb的MVC架构的原理以及每个组件的作用。 模型(Model) 模型是JavaWeb应用程序中处理数据和业务逻辑的部分
    的头像 发表于 12-03 11:48 924次阅读

    如何采用分区架构提升车辆的简易

    维护和制造。 在飞速发展的汽车制造领域,正在从过去满载 ECU 的汽车向未来以数据为中心的流线型汽车转变。分区架构的概念正在重新定义从车辆设计到道路行驶性能、维护和制造流程等各个方面。  汽车行业正处于变革,分区
    的头像 发表于 07-11 15:59 660次阅读