ToB企服应用市场:ToB评测及商务社交产业平台
标题:
从.net framework 到 .net core:车家号项目的升级过程及经验
[打印本页]
作者:
惊落一身雪
时间:
2023-2-21 13:15
标题:
从.net framework 到 .net core:车家号项目的升级过程及经验
一、背景
车家号作为一个PGC平台,聚合了全网大多数汽车行业的专家及意见领袖,每天为用户提供大量的汽车类优质内容。用户日浏览量在几千万级,后端的接口也承载亿级的日访问量。
车家号WEB、API、后台管理等系统采用 .net4.5进行开发。一直以来为用户及调用方提供了稳定的服务。由于其只能运行于Windows平台上,其扩展及迁移的能力受到了极大限制。需要将车家号业务转移到Linux平台,可以进行更为灵活的运维,并且具有容器化能力。
方案之一,用java重写,这个对于一个已经维护多年的有大量的业务逻辑在里面的系统来说,工作量是相当大的。只重写接口相对简单,但如果将PC 及后台管理进行重写工作量极大。或前后分离NodeJS 方式,这样也会给前端开发及测试带来巨大大的压力。还有更重要的一点,需求不断的提出来,还要不断有新特性加入进来,如果维护两套异构语言的系统,会给业务及系统的稳定性带来很大的风险。
另一个方案,项目从.net framework 升级为dotnet core,基本上语法方面不需要变化,业务代码不需要变化,新需求的加入,单向同步代码即可。同时,.net core 是跨平台的,另外,可以使用docker进行弹性扩容缩容。.net core 在性能方面从官方数据及后来的一些测试看的确有了明显的提升。
所以,基于这些主要原因,我们选择了方案二,.net core ,并且容器化。
二、人员投入
时间
天数
人员
人日
2月21日-2月27日
5
1人
5人日
4月1日 -5月13日
27
0.5人
13.5人日
5月14日-5月24日
9
1.5 人
13.5人日
5月27日-6月6日
9
2.5人
22.5人日
合计:
54.5人日
升级前期,因存在尝试、学习等因素,人员投入较少。后面调整重心,加强人力投入到升级工作中,快速推进升级上线。
公司范围内,我们是第一个将较大项目进行.net core 升级的团队。没有太多dotnet core的积累,无成功案例可借鉴,同时大量工作也用在了构建基础设施。未来类似规模项目进行升级,时间及人力投入方面会大大减少。
三、升级效果
1.性能提升
从5.25日开始逐步灰度,tp99也从100ms逐步降到45ms左右,到发稿为止稳定保持于45ms以下。
2.无感知升级
接口,WEB,后台管理等从外观及行为上没有的变化,做到用户及调用方无感知。
3.弹性
由于core 可运行于Linux上,可以借助于之家云平台,在容器中弹性扩容。
四、主要过程
在业务需求不断变更的同时,在第一次全量完成代码同步后,严格从原有业务主干(.net framework + TFS)单向同步到新版本业务主干(.net core+ git),保证业务行为的一致性,并不断同步发布新版本进行灰度,两个版本的行为保持一致性,升级过程无缝切换。如下图所示:
1.准备阶段
由于.net core兼容性及某些功能的缺失,需要使用重新编译或编写新组件进行兼容,这些作为基础组件,需要进行更多的单元测试及测试项目中的测试,保证其它正确性,稳定性及兼容性。
.net core 已经无法使用.net framework的组件,所有原有组件都需要重新编译适配。(见组件列表)
Asp.net core 中 有些功能已经不再支持,需要重新适配。见附兼容组件
2.编译通过阶段可运行阶段
自底向上,逐个项目进行升级。
卸载所有项目。
从基础的底层的类库进行升级,通常其依赖着准备阶段中提到的各种组件。
底层公共项目,通常都可以顺利编译通过。
对于实在无法编译通过的项目,根据其作作决定是否先注释掉,并用异常抛出的方式先行替代(throw new NotImplementedException()),防止未来的误调用或忘记实现。
根据原有项目间依赖并系,逐步上向。
先将接口项目编译通过,由于其不依赖UI等,比较容易通过,替换兼容,运行。
WEB 项目
Asp.net Core 与 asp.net 项目结构不同需要进行调整。
批量替换命名空间
编译,如果有不能通过的,看是否能快速解决或重要性,决定是否跳过。
经过上面的操作,项目基本可以运行,完成升级过程的第一步。
3.细化解决问题并同步代码
Core 版代码使用git 进行管理,从 TFS某个时间点进行快照,并Push到git库。作为基础,进行升级改造,对组件及代码级别的不兼容进行调整。后面新业务也是在这个版本的基础上进行增量同步。
后面的需求还在继续在TFS上提交,采用由TFS到Git单向同步的方式,TFS上的新需求由负责的开发人员同步至git 上。这种情况将持续到全量上线。
这个阶段经历了大致两个星期。
4.测试
开发自测:开发人员 自动用例生成工具,生成测试用例,使用JMeter 测试,大规模的覆盖,快速发现并修复问题。另外,使用自动化接口对比工具,完成全量接口对数据对比,完全节省了人工对于接口的重复测试,快速发现并定位问题。
测试人员测试:对于一些重要场景,如作者发现文章及与财务相关的功能,需要人工方式进行二次验证。
5.灰度上线
虽然经过了多次测试,但一些场景可能会无法覆盖,需要采用灰度方式。将新版加入负载,经过从万分之一,百分之一,十分之一,二分之一,最终全量。
在灰度过程中,对错误日志、访问日志、性能日志等多维度进行监测,保证系统负载在可控范围内,并且发现有异常及时修复或回调流量。
6.全量切换
系统上是先从PC开始的,等PC上线稳定以后,再上线接口,服务,对外接口等其它系统。
PC:在构成方面虽然比较复杂,但它前面有CDN 及SCS,万一出现问题,来得及进行补救。
接口:流量大,但结构相对简单,如果出现问题,影响面比较大,所以,在PC成功切换后,有了经验及信心,再进行切换。
后台管理:运营使用,有问题可能快速反馈,并能快速修复。
服务:核心业务已经上线,服务,也要跟着上线了。
Open接口:由于流量及用户都不多,并且大多数是Post接口,需要更充分的测试,选择最后上线。
6月6日 最终完成所有系统的全量的上线。
五、经验
1.备用方案,随时可回滚
在最初制订方案的时候,考虑到极有可能出现意外,导致新系统有不可预知的问题。需要具有随时切换回旧系统的能力,当然,这个也是灰度的基础。
保证可以切回,要保证:
(1)最重要的,代码要保持同步。
(2)同步发布,新旧两个环境都要保持最新,同步。
(3)在完成稳定迁移,旧的环境一直保持高可用。
在本次迁移过程中,由于Core所在集群故障,紧急切回旧系统,这样避免了一次非常严重的事故。
2.扩容要谨慎
升级过程中某天,系统的流量突然异常。系统开始变慢,但还能勉强顶住。
随后做出了扩容决定,从8个实例,直接扩容为30个。接下来,就是连锁反应,WEB,API,相继告急,性能极度下降。数据库不断报警。
对于我们从传统的单体应用进行迁移至Docker, 不要急于扩容,一定要看到你的系统是否有单点,这个单点是否可以避免。
3.自测工具
本次升级,测试人员投入大概两个半天,进行测试。剩下的都由开发人员进行自测。使用工具进行大规模的覆盖。
测试工具,对比工具等都根据实际业务进行自主开发。在未来也可以重复用于其它或日常工作中。
接口对比及批量对比测试:
自动生成用例及在JMeter中测试:
4.日志工具
.net core 在 Linux下是没有相应的访问日志,另一个途径也可以从Nginx获取访问日志,但它与我们现有日志分析及监控系统不兼容。
基于.net core 中间件,对访问进行拦截并记录日志。使用Udp 方式发送至日志收集系统。
这样对系统的访问数量及处理性能通过报表有直观的了解。
5.监控工具
.net core 由于是一个新的生态,没有类似于windows下的性能记数器,或Java的Jmx这种工具对性能进行监控。所以,我们需要自己来做一套监控解决方案。
(1) 做SDK,收集系统运行性能指标。
(2) 提交至Logstash
(3) Logstash 写入 ES
(4) 通过Grafana进行图形化展示。
6.缓存注解
从Java中学习到Spring boot 的Cache注解,按其思路实现注解并应用于.net core , 一个注解,省去大片代码。
代码中使用注解示例效果:
使用注解前后效果TP 99对比:
7.缓存的一致性
由于我们的系统极度依赖于缓存层,并且服务会更新缓存,新旧的系统如果可以使用同一套缓存,使用同一个Key,那可以减少很多必要的麻烦。
我们重新编译了ServiceStack.Redis 。保证了缓存的行为的一致性,这为我们节省了至少三分之一的工作量,使得新旧各系统中都是一致的缓存,一致数据。
8. Json 框架
在当前系统中用到了多个框架,Json.net , FastJson , MS Json , 在代码调试过程中,新旧系统使用返回数据不一致,也发生各种序列化异常,经过分析,原来一旧代码与新代码使用不同的Json序列化方式。
在系统迁移过程中,由于多处使用了这三个框架,只重构掉了一种MS Json,现在还有 Json.net , FastJson 两种在运行着。
后面系统稳定后,应该仅保留一种序列化框架。
9.Docker性能
当时转core一个原因就是想使用 docker 进行动态扩容、缩容的特性。本次升级过程中,由于所在机房资源不足,docker集群不稳定,迁移机房暂时不具备条件等原因。只能将部分业务,如后台,服务,Open接口等运行在docker上,其它随时压力较大的如WEB ,接口等运行在VM上,在迁移机房后,再将大流量产品进行迁移。
10. 先运行再解决Bug
过程中不要把问题放在一个点上,先保证系统可以整体运行,再去解决某个点的技术问题,为团队树立信心,让每个人都可以看得到它是可运行的。
六、最后
在本次升级过程中,让我们感受到升级.net core很小的学习曲线,快速入手,性能的提升也是一种惊喜。还有,大多数常用的开源组件对core提供了支持,改极少或不用改代码即可使用。
同时,升级过程中,积累了经验,产出了通用性功能及组件,对于其它系统的升级具有指导和借鉴意义。
还有,这个成功的案例,也给有升级想法的团队,树立信心,使他们可以更快迈出这一步。
升级过程发现了一些问题,促使我们对于架构进一步的思考,在后面通过对架构的优化使系统具有更好的扩展能力。
七、附录
1.组件
dotcoreActionMessageSdk 发送HttpMessageQuene 组件
dotNews.ServiceStack.Redis Redis连接组件
dotcoreAutohome.DataHelper Sqlserver连接组件
dotCoreCasClient SSO 组件
dotcorejobclient 分布式Job组件
dotcoreNews.Comm 通用类型组件
dotcoreNews.Common.Extends.Log ELK 日志组件
dotcoreNews.Common.Extends.Upload 图片上传组件
dotcoreNews.Common.UploadObject 文件上传组件
dotcoreWebdiyer.MvcPagerCore ajax分页组件
2.兼容手段
dotcoreHttpContextCurrentCore组件 asp.net core 已经没有HttpContext.Current, 本项目实现 System.Web.HttpContext.Current 功能,最大程度兼容旧代码,减少重写量
core 不再支持 WCF ,未来版本是否支持社区还在争论中,我们采用重写路由的方式进行兼容。
Html.Action 不再支持,重写这个方法,为了区别更别为Html.RenderAction,参数功能不变。
EF Repository 模式代码,基于 sqlSugarCore 改写。保持调用外观不变。
Microsoft.Practices.ServiceLocation 不再支持,使用 Castle.Core
using System.Web.Mvc,不再支持, 使用 usingMicrosoft.AspNetCore.Mvc;
Areas 的支持不再,使用 router
Ashx 一般处理程序不支持,改用 router兼容
[assembly: PreApplicationStartMethod] 不支持,改用 StartUp.cs中调用
HTTP handlers and modules 改为 middleware
3. 工具支持
自制应用程序指标监控
自制批量用例自动生成
自制指量接口比较工具
Jmeter
作者| 张保维
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4