研发协同平台持续交付之代理服务实践

源宝导读:插件系统大大提高了系统的扩展性,有利于模块化开发。系统发布后,当我们需要对系统进行扩充,可以再不编译的情况下更新系统的插件即可。基于热拔插的软件系统提高了持续交付能力,在添加新特性的同时保持核心结构稳定。本文将介绍研发协同平台代理服务在插件化设计方面的技术实践。

一、背景

    插件系统大大提高了系统的扩展性,有利于模块化开发。系统发布后,当我们需要对系统进行扩充,可以再不编译的情况下更新系统的插件即可。基于热拔插的软件系统提高了持续交付能力,在添加新特性的同时保持核心结构稳定。

    ERP是To B产品, To B产品的持续交付和To C产品的持续交付是有很大不同的。To B产品需要交付给成千上万家客户,每家客户的产品和环境都不一样,要保证ERP产品能持续的、稳定的交付给成千上万家客户,在客户端需要一个代理服务来协同完成。由此,ERP代理服务应运而生。

二、设计架构

    ERP 代理服务在客户端负责产品的持续交付承担在重要的角色,ERP 产品每次的最终交付到客户,它需要在客户端完成很多重要的工作:更新包到客户机 IIS、收集客户机更新日志、服务器运行状态、同时由于客户机网络环境安全限制时也承担着信息的中转服务,为了日后公司产品的多样化、环境的多变性、产品的迅速更新,我们需要随时做出的相应响应,并能迅速的完成支撑,因此 ERP 代理服务在保证自身稳定的同时,需要快速的更新自己的产品所承担的功能职责。

    ERP代理服务的核心需求如下:

  • 自更新。

  • 自恢复。

  • 灰度更新。

  • 业务逻辑可升级。

  • 业务逻辑可灵活扩展。

  • 较好的容错机制,在网络不稳定或其他其他因素的影响下,也能稳定持续运行。

    基于以上需求,我们新设计了 ERP 代理服务的架构方案,我们将原有 ERP 代理服务改造为热拔插的插件支撑方案。

三、框架设计

    代理服务通过支持三种类型的插件热拔插,实现多种多样的需求:

  1. 类库插件:实现服务端下发命令执行业务处理。

  2. Web 插件:可灵活的提供 API 或者视图在页面中呈现。

  3. Console 插件:通过外部启动 exe 程序的方式,守护进程或者其他特殊需求。

    插件功能高内聚,与框架低耦合,开发人员根据规范,开发完成并进行单元测试通过后,打包并安装到宿主中,即可使用。

3.1、插件加载模式

    通过向更新服务获取用户可用的插件集合,动态新增、升级、卸载插件,通过在内存中加载 DLL 的方式热拔插插件,能有效的更新客户端服务器上的代理服务功能,静默升级插件后续将让我们的服务有很高的拓展性。

3.2、目前规划的插件

    针对现有的业务场景我们规划了多个插件,并用于实现不同的功能模块:

  1. 更新包插件:用于更新ERP产品到客户服务器的站点中。

  2. 更新服务API插件:用于提供产品注册等接口给ERP站点调用,将相应信息上报至服务端。

  3. 守护插件:守护代理服务运行状况,负责重启、更新代理服务。

    其中部分插件为热加载方式,我们可快速的进行插件迭代开发,并发布上线(同时支持灰度发布),能快速的应对产品需求。

四、实现方案

    我们从现有的 ERP 代理服务,以及 ERP 的交付特性,来考虑我们系统的可拓展性、可维护性,同时在一定程度上引入更新的技术栈来。我们从三个部分实现我们的插件系统:

  1. 主程序开发。

  2. 公共接口的基础插件类库开发。

  3. 各类插件开发。

    以上,我们需要实现各部分之间的接口规范。

4.1、技术调研

    我们调研了最新的 ASP.NET CORE 3.1(https://docs.microsoft.com/zh-cn/aspnet/core/?view=aspnetcore-3.1) 技术栈,它能很好的支持我们的热拔插的系统设计方案,微软也给出了简单的示例,以下是提供一些关键技术文档进行参考:

  1. ASP.NET CORE 3.1 热拔插插件的实现方案。

    (https://docs.microsoft.com/zh-cn/dotnet/standard/assembly/unloadability )

  2. ASP.NET CORE 支持独立部署方式避免系统需要安装.NETCORE SDK。

    (https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/?view=aspnetcore-3.1)

  3. 引入 NLog 日志组件。

    (https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3)

    在以上技术栈的基础上,我们对基础知识加以深入研究,引入项目的过程中不断思考优化项目结构。

4.2、实现解析

    我们改变原有ERP代理服务的插件方案,采用 AssemblyLoadContext 来实现热拔插插件方案,同时重新调整主程序框架根据支持多种的插件的的加载,将公共服务接口提升到基础插件类库中以便继承插件可以通过依赖注入的方式使用我们的公共接口。

4.2.1、封装插件加载器SDK

功能特性

  • 支持将dll以文件流的方式加载(可卸载)。

  • 支持将占用dll文件的方式加载(可卸载)。

  • 支持插件文件变更卸载插件并自动重新加载。

设计目录:

核心代码:

// 程序集加载
protected override Assembly Load(AssemblyName assemblyName)
{var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);if (assemblyPath == null){var localFile = Path.Combine(Path.GetDirectoryName(MainAssemblyToLoadPath), assemblyName.Name + ".dll");if (File.Exists(localFile)){assemblyPath = localFile;}else{return null;}}// 内存方式加载if (IsLoadInMemory){using var file = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read);return LoadFromStream(file);}else{return LoadFromAssemblyPath(assemblyPath);}
}
/// <summary>/// 卸载插件/// </summary>[MethodImpl(MethodImplOptions.NoInlining)]public bool Unload(){    if (!_context.IsCollectible)    {        Console.WriteLine($"AssemblyLoadContext 不是可回收的");        return false;    }    if (!_context.IsLoadInMemory)    {        var mainAssemblyToLoadPath = _context.MainAssemblyToLoadPath;        // 判断资源是否存在引用        var weakRef = new WeakReference(_context, trackResurrection: true);        _context.Unload();        _context = null;        // 尝试至多20次垃圾回收,将资源引用释放        for (int i = 0; weakRef.IsAlive && (i < 20); i++)        {            GC.Collect();            GC.WaitForPendingFinalizers();            // 睡眠500毫秒,确保GC回收完毕,资源引用释放            Thread.Sleep(500);        }        if (weakRef.IsAlive)        {            Console.WriteLine($"程序集({mainAssemblyToLoadPath})回收失败,请确认程序集未被使用");        }        return !weakRef.IsAlive;    }    else    {        _context.Unload();        _context = null;        return true;    }}

4.2.2、 主程序功能模块解析

功能特性及结构分层:

  • 全局异常过滤。

  • 计划任务:命令定时执行任务、定时上报任务、定时更新插件任务。

  • 插件加载器:类库插件加载器、Web插件加载器、控制台(exe)类插件加载器。

  • 中间件:请求记录日志中间件。

  • 应用程序全局配置。

  • 服务层:命令服务、插件缓存、插件服务、上报服务。

目录结构:

应用设计结构:

4.2.2.1、 启动说明

    ERP代理服务启动前会注册所需服务:

  • 注册配置中心服务。

  • 注册日志服务。

  • 注册全局异常处理。

  • 注册更新服务。

  • 注册Web插件View视图引擎目录。

  • 注册各类型插件加载器(类库插件、Web插件、控制台类插件)。

  • 注册插件缓存服务、命令服务、上报服务、插件管理服务。

  • 注册上报调度任务、命令执行调度任务、更新及加载插件调度任务。

    并根据配置中心配置的服务启动地址启动,改地址用于ERP产品调用代理服务上报产品信息等接口调用。

4.2.2.2、API接口服务说明

    目前由更新服务API插件提供给ERP产品将相应信息提交至更新服务,以便后续我们能很好的利用信息完成客户的产品交付:

  • 注册产品信息。

  • 获取服务器状态信息。

  • 获取更新包更新状态。

  • 获取更新页面地址。

  • 获取更新服务状态。

    同时由于该插件可以后期升级并热加载,我们可以根据需求快速迭代插件,为ERP产品提供更多的所需服务。

4.2.2.3、调度任务说明

    应用启动后,通过异步方式启动调度任务,在调度任务中完成各自的职责。

  • 命令任务:通常用于执行发布通知更新包任务或即时更新插件。

  • 上报任务:用于上报客户相关信息、插件运行状态等信息。

  • 更新插件任务:用于异步加载插件,不影响主程序运行,并定期同步客户端插件与服务端插件版本信息。

    通过将核心任务使用调度任务异步运行的方式,避免影响主程序。

五、写在最后

    目前我们的项目已经进入尾声,我们已基本在运用这套方案实现了原有ERP代理服务的功能,同时我们更加友好的支持灰度插件发布上线。未来我们还会加入日志主动或被动上报、客户服务器监控,ERP站点运行监控等功能插件来帮助我们产品发展的越来越好。

    最后,我们在实践中也遇到了一些问题:

  1. 当使用 XmlSerializer 操作相关资源时,由于改对象目前还不支持可卸载,暂不能用于可卸载插件中 ——— 详情请查阅 Issue

    (https://github.com/dotnet/runtime/issues/1388)。

  2. 当插件依赖了 System.Data.SqlClient 类库时,会导致报异常:

    SqlClient is not supported on this platform. ———  详情请查阅 Issue。

    (https://github.com/dotnet/SqlClient/issues/115)

    此时我们可改用 Microsoft.Data.SqlClient 包。

    (https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/)

    我们在努力克服困难的同时也在实践中成长进步。

------ END ------

作者简介

    曾同学: 研发工程师,负责研发协同平台产品的研发工作。

也许您还想看

    研发协同平台持续集成实践

    研发协同平台持续集成2.0架构演进

    研发协同平台微服务监控的技术实践

    ERP开放平台定制化远程高效协作秘笈

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

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

相关文章

vscode python 自动补全_利用CodeBERT,这个VS Code扩展可以自动生成Python文档字符串...

机器之心报道编辑&#xff1a;魔王该扩展利用可处理编程语言和自然语言的预训练模型 CodeBERT&#xff0c;实现快速生成 Python 文档字符串的功能。Visual Studio Code(简称 VS Code)是一个由微软开发&#xff0c;同时支持 Windows、 Linux 和 macOS 等操作系统的免费代码编辑器…

[蓝桥杯2016初赛]报纸页数-生活常识+思维

题目描述 X星球日报和我们地球的城市早报是一样的&#xff0c;都是一些单独的纸张叠在一起而已。每张纸印有4版。 比如&#xff0c;某张报纸包含的4页是&#xff1a;5,6,11,12&#xff0c;可以确定它应该是最上边的第2张报纸。 我们在太空中捡到了一张X星球的报纸&#xff0c;…

great English sentence:

great English sentence: Therefore,everyone needs to find their extra ——their unique value contribution that make them stand out in whatever is their field of emploment. 因此&#xff0c;每个人必须去找到独特点&#xff0c;这个独特点就是他们唯一的价值创造的…

2020 年 5 大 DevOps 趋势

由于高效率和较快的部署能力&#xff0c;DevOps 正在一步步地塑造着软件世界&#xff0c;并进入主流领域。而随着 DevOps 逐步成熟&#xff0c;它也将继续改变全球的 IT 和数字管理。为了促进各方更加了解 DevOps&#xff0c;并为即将到来的一切做好准备&#xff0c;一位名为 G…

datagridview实时更新数据_旭诺云盒|智能办公新趋势进出口数据自动提取,通关状态实时更新...

春节期间&#xff0c;很多公司同事都被滞留在老家无法返回公司上班&#xff0c;为了保证公司业务正常运转&#xff0c;同事之间依靠邮件、微信、QQ等工具进行文件和数据的传递&#xff0c;增加了很多数据整理时间。且电子口岸、单一窗口这些进出口企业频繁使用的平台&#xff0…

排名前15位的Kubernetes监控和安全工具

Kubernetes推动了竞争力的提升。如今&#xff0c;作为一项成熟的技术&#xff0c;全球各地的企业都在迅速采用基于微服务的&#xff0c;容器驱动的方法来交付软件。Kubernetes是行业标准。行业领导者正在帮助它如雨后春笋般发展&#xff0c;基于Kubernetes核心开发综合应用程序…

word List 31

word List 31 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

什么是python语言的动态类型机制_理解Python的Dynamic typing

Python的Dynamic typing有些类似于C语言的指针&#xff0c;在C中&#xff0c;一个变量可以指向任何地址空间&#xff0c;在Python中&#xff0c;一个变量也可以指向任何type的数据对象。变量的指向可以在程序运行过程中变化&#xff0c;这就是我理解的Dynamic typing。 Python是…

Asp.Net Core IdentityServer4 管理面板集成

前言IdentityServer4&#xff08;以下简称 Id4&#xff09; 是 Asp.Net Core 中一个非常流行的 OpenId Connect 和 OAuth 2.0 框架&#xff0c;可以轻松集成到 Asp.Net Core 应用中&#xff0c;并且与 Asp.Net Core Identity 也可以轻松集成。博客园也有大佬发布了很多关于 Id4…

数据结构---递归实现十进制装换为任意进制

数据结构—递归实现十进制装换为任意进制 代码&#xff1a; #include <stdio.h> #include <stdlib.h> #include"stack.h" stack Stack; void toAnyDigit(int n, int d) {//十进制转化为d进制if (n 0) {//递归结束的条件return;}int temp n % d;push(…

c++ string 删除字符_字符串操作的全面总结(附完整代码)

字符串操作看似简单&#xff0c;其实非常重要&#xff0c;不注意的话&#xff0c;经常出现代码运行结果和自己想要的不一致&#xff0c;甚至崩溃。本文总结了一些构建string对象方法、修改string对象的方法、string类型的操作函数、string类型的查找、string对象的比较。1 构建…

给Hangfire的webjob增加callback和动态判断返回结果功能设计

背景介绍通常业务中需要用到定时执行功能&#xff0c;我用hangfire搭建了一个调度服务&#xff0c;这个调度服务是独立于业务逻辑的&#xff0c;具体可以参考文章&#xff1a;https://github.com/yuzd/Hangfire.HttpJob/wiki也就是说只要我有了这个调度服务后&#xff0c;只要提…

C++实现双向链表

#include <iostream> using namespace std; typedef int ElemType;typedef struct DuLNode {//节点的创建ElemType data;struct DuLNode *prior, *next; } DuLNode, *DuLinkList;bool InitList(DuLinkList &L) {//初始化L new DuLNode;if (L NULL) {cout << …

word List32

word List32 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

spring aop实例讲解_Spring框架核心知识点

文章内容输出来源&#xff1a;拉勾教育Java高薪训练营前言&#xff1a;由于工作需要提升自身技术能力&#xff0c;在各方比较下&#xff0c;报名了拉勾教育的java高薪训练营&#xff0c;目前已经学了半个月啦&#xff0c;来说说自身学习的感受吧&#xff1a;课程内容有广度更有…

word List 33

word List 33 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

深入理解ASP.NET Core依赖注入

概述ASP.NET Core可以说是处处皆注入&#xff0c;本文从基础角度理解一下原生DI容器&#xff0c;及介绍下怎么使用并且如何替换官方提供的默认依赖注入容器。什么是依赖注入百度百科中对于依赖注入的定义&#xff1a;控制反转&#xff08;Inversion of Control&#xff0c;缩写…

word List 34

word List 34 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

redis api 中文文档_我的Redis学习资料库

最近经常有人问我&#xff0c;我这边学习Redis都有哪些资料&#xff0c;能不能发他们一下。作为一个习惯”慢慢啃”技术学习的人&#xff0c;我确实积累了大量资料&#xff0c;这里可以介绍给大家。注明&#xff1a;本文提供的书籍、视频、源码、文档等&#xff0c;都是非盈利性…

前后端分离架构一直没机会实战?1周完成Vue+Core WebApi移动商城实战(含源码)!...

疫情让企业受到重创&#xff01;就业形势更加严峻&#xff01;前后端分离架构成了当下最高频的招聘需求还没实战过前后端分离&#xff1f;花3分钟阅读本文&#xff0c;带你全面了解前后端分离&#xff0c;轻松面试拿高薪&#xff01;Web发展至今技术非常成熟&#xff0c;主流有…