从.net framework 到 .net core:车家号项目的升级过程及经验

  • 从.net framework 到 .net core:车家号项目的升级过程及经验已关闭评论
  • 98 次浏览
  • A+
所属分类:.NET技术
摘要

       车家号作为一个PGC平台,聚合了全网大多数汽车行业的专家及意见领袖,每天为用户提供大量的汽车类优质内容。用户日浏览量在几千万级,后端的接口也承载亿级的日访问量。

从.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.性能提升

从.net framework 到 .net core:车家号项目的升级过程及经验

从5.25日开始逐步灰度,tp99也从100ms逐步降到45ms左右,到发稿为止稳定保持于45ms以下。

2.无感知升级

接口,WEB,后台管理等从外观及行为上没有的变化,做到用户及调用方无感知。

3.弹性

由于core 可运行于Linux上,可以借助于之家云平台,在容器中弹性扩容。

 

四、主要过程

在业务需求不断变更的同时,在第一次全量完成代码同步后,严格从原有业务主干(.net framework + TFS)单向同步到新版本业务主干(.net core+ git),保证业务行为的一致性,并不断同步发布新版本进行灰度,两个版本的行为保持一致性,升级过程无缝切换。如下图所示:

 

从.net framework 到 .net core:车家号项目的升级过程及经验

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.自测工具

本次升级,测试人员投入大概两个半天,进行测试。剩下的都由开发人员进行自测。使用工具进行大规模的覆盖。

测试工具,对比工具等都根据实际业务进行自主开发。在未来也可以重复用于其它或日常工作中。

接口对比及批量对比测试:

从.net framework 到 .net core:车家号项目的升级过程及经验

 

自动生成用例及在JMeter中测试:

从.net framework 到 .net core:车家号项目的升级过程及经验

 

4.日志工具

.net core 在 Linux下是没有相应的访问日志,另一个途径也可以从Nginx获取访问日志,但它与我们现有日志分析及监控系统不兼容。

基于.net core 中间件,对访问进行拦截并记录日志。使用Udp 方式发送至日志收集系统。

这样对系统的访问数量及处理性能通过报表有直观的了解。

从.net framework 到 .net core:车家号项目的升级过程及经验

5.监控工具

.net core 由于是一个新的生态,没有类似于windows下的性能记数器,或Java的Jmx这种工具对性能进行监控。所以,我们需要自己来做一套监控解决方案。

(1)  做SDK,收集系统运行性能指标。

(2)  提交至Logstash

(3)  Logstash 写入 ES

(4) 通过Grafana进行图形化展示。

从.net framework 到 .net core:车家号项目的升级过程及经验

 

6.缓存注解

从Java中学习到Spring boot 的Cache注解,按其思路实现注解并应用于.net core , 一个注解,省去大片代码。

 

代码中使用注解示例效果:

从.net framework 到 .net core:车家号项目的升级过程及经验 

使用注解前后效果TP 99对比:

 

从.net framework 到 .net core:车家号项目的升级过程及经验

 

 

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

 

作者| 张保维