[干货分享] .NET应用迁移到.NET Core(方法论)

把一个运行在某个操作系统和硬件结构上的软件,在另一个操作系统和硬件结构上重新编译(包括一些必要的修改),以便在新的平台上运行,这一过程叫做应用程序移植。有些情况下,把应用程序从一个平台移植到另一个平台非常简单直接,仅需要重新编译并进行一些验证测试即可。但是有些情况下,移植程序并不是那么容易。

本篇是在应用程序移植方面对当前项目管理的一个补充,关于如何使用正规化的需求管理过程、如何更好的与软件开发人员交流,以及如何进行项目管理,今天的项目经理们都已经非常熟悉了,但是,软件开发和软件移植毕竟并不完全相同,这也就是本章要讲述的内容。

本篇重点讲述软件移植的详细过程和技术风险,并列出一些实现高质量应用程序一致的习惯和方法。


软件程序商业过程

    在开始一个移植项目之前,很重要的一点就是要搞清楚在这个应用程序的生命周期中那些商业过程会受到影响。那些受到影响的商业过程必须进行修改,以适应移植后的应用程序,因为需要支持新的平台、新的测试环境、新的工具、新的文档,最重要的是,是需要支持新的客户并建立客户关系。

      在应用程序的生命周期中,可能会影响到商业过程的三个主要领域是开发和测试、客户支持,以及应用程序发布:

  • 开发和测试。在开发和测试部门,必须在以下方面对开发测试人员进行Linux/Windows技能测试:应用程序编程接口(API)的区别、开发工具、调试功能根据、性能工具,以及待移植的应用程序需要的第三方软件。

  • 客户支持。在客户支持部门,必须在以下方面对支持人员进行培训:Linux/Windows系统管理、移植后的应用程序需要的第三方软件、安装和管理方法、Linux/Windows环境下的包管理工具、调试工具和方法,以及所需要的其他内容。

  • 应用程序发布。在应用程序发布部门,必须在Linux/Windows整体特性和知识方面对销售人员和技术顾问进行培训。对软件发布渠道人员,必须对其进行培训,使其成为Linux/Windows软件程序的培训者。从客户的角度看,他们也希望获取Linux/Windows集成方面的知识,一并能够把Linux/Windows和他们已有的IT系统集成在一起。

.NET应用移植应用程序到.NETCore平台,也就意味着修改可能受到新移植的应用程序影响的商业组织过程。应该在真正的移植开始之前,认真考虑这三个主要的方面,并将他们包含在整个移植项目过程中。


移植过程

    参与移植项目的开发人员在移植任何项目时都可以遵循类似的步骤。这些步骤包括:调查、分析、移植及测试。过程中的每一步都是后一步的基础。每一步都做好,后续的步骤也就会很容易完成。


调查

   调查这一步主要是项目经理召集移植专家(在应用程序方面比较有经验,并且对开源平台、目标平台及应用程序使用的第三方产品比较了解的软件开发人员)和某一领域内的专家一起来确定待移植的应用程序所依赖的产品、开发和测试环境等。在调查阶段要明确的几个关键内容包括:产品/软件依赖关系、开发环境组件、编译环境组件和测试环境组件。

  • 产品/软件依赖关系。确定待移植的应用程序所依赖的产品,也就是要确定该应用程序使用那个版本的数据库、中间件以及第三方类库等。知道了所依赖的产品和版本,移植专家就可以估计在.NET Core平台上是否有这些产品和版本。

  • 开发环境组件。确定开发环境包括确定待移植的应用程序时用哪种编程语言编写的。使用较新的编程语言(例如C#)编写的应用程序比较容易移植,但是使用C/C++语言编写的应用程序需要花费较多的时间来分析和移植。

  • 编译环境组件。确定编译环境包括确定需要的编译工具是否在Linux/Windows上可用。对于在源平台上使用的平台相关的编译和链接标志必须进行调查,以确定在Linux/Windows上是否存在对应的标志。有些编译环境可能依赖于原平台,这可能要花费较多的功夫才能移植到Linux上。

  • 测试环境组件。确定移植后的应用程序所使用的测试环境,会引入一些测试人员应该关注的问题。通常情况下,移植工程师只对他们移植的部分做单元测试,然后就把程序交给测试组来做更完整的验证和系统测试。但是,谁是测试组呢?

多数情况下,“调查”这一步也会明确一些项目开始后可能遇到的风险。在调查阶段可以明确的风险如下:

  • 需要的数据库、中间件和依赖的第三方程序集在.NET Core上不可用。

  • 应用程序包括一些需要转换成Linux汇编指令的汇编例程。

  • 应用程序使用了源平台特有的API或编程模型。这也包括编写程序时对字母大小写和字节序的假设。

  • 应用程序时按照.NET的某个版本编写的,而该标准的实现又依赖于原平台上特有的编译器。

  • 测试环境需要复杂的客户端/服务器架构。

  • 开发环境需用第三方工具,而该工具需要移植到.NET Core上。

  • 应用程序的发布或安装需要源平台的特有的工具。

“调查”这一步需要关注每一个新信息,这些信息可以通过问一些问题得到,例如关于文档、打包、性能调整等的问题。


分析

    分析“这一步需要从两个角度来考虑:项目管理和移植。从项目管理的角度看,分析就是要评估在前一个步骤中确定的各种移植问题和风险,以及它们会对项目移植产生怎么样的影响。”分析“这一步要制定项目计划,包括确定项目的范围和目标、创建工作进度计划、获取资源,以及分配项目角色。

   确定项目的范围和目标也定义了项目经理和组员的职责范围和责任。项目范围指的是项目要完成的一系列工作。例如,像“应用程序ABC的模块A需要移植到平台B上,并在B上测试”,这样的简单陈述就是一个很好的定义项目范围的例子。

   项目范围定义以后,移植工作的具体任务也就是可以定义了,这就产生了一个工作详细分类的进度表。该进度表可以帮助确定哪些工作需要做,以及这些工作是顺序做还是可以并行来做。另外,该进度表还列出了所需要的资源。一个完整的进度表可以通过定义项目任务和所需的资源得到。

   从移植的角度看,“分析”这一步就是移植工程师详细地分析应用程序的结构。移植工程师要确定应用程序所使用的API和系统调用,并且评估应用程序使用的动态链接和装载、网络、线程等。分析得到这些信息反馈给项目经理,项目经理据此确定更为详细的任务,并制定出更为准确的计划。


移植

    “移植“这一步就是移植工程师开始执行分配给他们的具体工作。根据前一步得出的工作细目表,移植工程师可能只能串行的工作。这主要是因为待移植的程序可能是紧耦合的。也就是说,应用程序的一个模块高度依赖于其他模块,只有那些被依赖的模块移植完成后,这些模块才能开始移植。一个典型的例子就是编译环境的移植。如果原来的编译环境是设计成一次编译整个应用程序,那么必须在任何移植工作之前,把各模块所依赖的通用配置文件修改完毕。

    如果移植工作相互之间没有关联,则移植可以并行进行。松耦合模块的移植可以分开来让不同的工程师同时进行。一个典型的例子就是共享库的移植,这样的共享库相互之间没有影响,可以各自独立编译,并且只是供其它模块编译链接使用。确定哪些工作可以并行进行是很重要的,并且这一工作应该是在分析阶段完成。

     .NET Core上编译代码的工作包括确定并消除代码对体系结构的依赖,以及非标准的编程习惯,包括检查代码并使用可移植的数据结构或编码标准。有经验和质量意识较强的移植工程师会纠正编译错误的同时检查后者。

      移植工作也包括移植编译环境到Linux/Windows平台。该工作应该在调查阶段明确下来。有些编译环境是可移植的,但有些并不是。确认编译环境不会导致潜在的问题是很容易被忽略的工作,需要非常仔细的调查和分析。

      应用程序移植完成以后(也就是说,在.NET Core上编译完成了),移植工程师需要对移植的应用程序进行单元测试。单元测试可以很简单,例如可以简单的运行程序,看是否产生运行时错误,如果产生运行时错误,就需要在把应用程序交付给测试组以前修改完成这些错误。如何,测试组会对程序进行更完全的测试。


测试

      在测试过程中,指定的测试人员会对移植后的应用程序运行一些测试用例,这些测试用例都有不同的测试目的,从仅仅运行应用程序的简单测试到测试应用程序在.NET Core平台上是否足够健壮的压力测试。在目标平台上对应用程序进行的压力测试,可以发现那些除体系结构依赖和坏的编码习惯之外的问题。大部分应用程序,尤其是多线程程序,在不同平台上进行压力测试时,往往会表现出不同的行为,部分原因是因为不同的操作系统实现,尤其是不同的线程的实现。如果在测试过程中发现问题,移植工程师就应该去调试和解决这些问题。

     有些应用程序移植也包括移植一套测试工具来测试该应用程序。移植测试工具也是应该在调查和测试阶段确定的一个任务。多数情况下,测试人员往往需要接受一些对应用程序的培训,才能测试该应用程序。学习应用程序是一个与移植工作完全独立的任务,可以与移植任务并行进行。测试发现问题后,需要解决这些问题并重新编译应用程序;然后重新测试该问题,直到应用程序通过所有的测试用例。


支持

     移植工作完成后,开发阶段就算结束了,支持阶段也就随之开始。一些移植工程师会留下来帮助回答客户可能提出一些一直相关的问题。另外,开发人员也应该培训客户怎样在Linux/Windows平台上配置和运行应用程序。在移植结束后,支持阶段一般需要持续6090天。在这期间,针对新移植到.NET Core的应用程序,移植工程师对技术支持人员和销售人员进行培训,并回答他们提出的问题。在培训完成以后,移植工程师的工作就算完成了。

 

定义项目范围和目标

定义项目范围就是定义清楚项目的结束点和边界,以及项目经理和项目成员的责任。定义清晰的项目范围可以确保该项目的所有相关者(参与项目或者受项目影响的人员或组织,如项目组、架构师、测试人员、客户或赞助商、合作部门等)都明确该项目的目标。

清晰的项目目标或需求可以让人更好地理解项目范围。在移植过程的分析阶段,客户的目标和需求被收集上来,然后被细分成各个结构,并最终定义成项目的交付物。项目的目标和需求是定义项目范围的起点。所有的项目目标都确定以后,项目的范围也就变得更加明确了。

定义项目范围的一种方法是,列出项目要包含和不包含的目标。从客户收集需求列表是个很好的开始方法。需求收集上来后,项目经理和技术领导详细的检查需求列表。如果对某些需求有疑问,应该把它们列到不被包含的目标列表中,至少最初时应该这样做。然后客户再次检查该列表,并对有异议的地方进行纠正。最后,知道所有人员都认为该列表已经正确表述了项目应该包含的所有目标。

需要再次强调的是,该列表需要足够详细,以便能够列出那些要包含在项目中,那些不要。的确,对超出项目范围的需求列出详细说明也是很重要的。对定义项目范围不能提供足够信息的目标,随着项目发布日期的临近,会逐渐成为争论的焦点。例如各个部分移植工作怎样交付给移植小组---是多次迭代还是一次性交付?每次交付时作为一个阶段,还是有一些更小的工作包含在该阶段中?在一个完整的项目中有多少个迭代?该列表不仅定义了项目本身,也列出了该项目的所有相关干系人所期望的结果。

下面列出创建项目目标列表时需要遵循的一些基本原则:

1、 尽可能详细定义项目范围。

2、 确认项目的所有相关干系人都同意该项目范围。

3、 在“不包含/不在范围内”列表中列出未解决的内容,直到全部解决。

定义项目目标的一部分工作是列出项目的接收和完成标准。关于项目的完成标准,所有的项目干系人必须达成一致意见。项目完成可能是指在Linux/Windows平台上通过了百分之多少的系统测试,或者是通过了系统性能所设定的某个性能标准也就是说,项目目标是运行一组具体的测试用例或者性能分析。不管项目完成的标准是怎样定义的,如果可能的话,所有的项目干系人在移植开始之前都必须理解并且同意这些标准。任何在移植过程中对标准的修改早替代现有标准前都必须与所有相关干系人交流协商并得到批准。

很多移植项目超出预算或未能按时完成,主要是因为没有很好地管理移植过程中可能遇到的风险。风险是在估计进度和资源时要考虑的一个重要因素。在应用程序移植项目中,这些风险来自与移植相关的不同方面,主要包括下面几种:

  • 移植工程师的技能水平和移植经验。

  • 使用的编译器。

  • 使用的编程语言。

  • 第三方软件和中间件的可用性。

  • 编译环境和工具。

  • 平台依赖的结构。

  • 平台和硬件依赖的代码。

  • 需要搭建的测试环境。

  • 用户界面需求。

依赖于要移植的具体应用程序,以上各个方面的每一个都可能给移植项目带来不同的复杂度和风险。评估这些复杂度和风险的级别,可以帮助你确定它们是不是可管理的。


技能水平和移植经验


    应用程序移植和软件开发最明显的区别就是编程人员所掌握的技能。虽然软件开发人员往往是某一领域内比较专业的人员,但是要做软件移植的开发人员却需要更宽广的知识和技能。一个应用程序开发人员可以是Windows开发环境上的.NET专家,或者是Windows操作系统上SQL Server的数据库专业开发人员;但是,从事代码移植工作的工程师却通常需要时两个或者更多操作系统、编程语言、编译器、调试器、数据库、中间件或最新的基于网页的技术方面的专家。他们还需要知道怎样安装和配置第三方数据库或中间件。

      应用程序开发人员需要的往往是专才,而移植工程师则多是通才。应用程序开发人员可以为一个软件工作18个月,但是移植工程师在一个项目上往往只需要工作3~6个月,并且在上一个项目完成以后,即可投入下一个新的移植项目中。为一个移植项目找到完全适合的移植工程师有时候是很困难的,从老的技术移植到新的技术上时尤其如此。


编译器

   

    Windows平台上使用的编译器和编译器框架,与Linux平台上使用的有很大不同。如果在Windows平台和Linux平台上使用的是相同的编译器,移植工作将会变得容易一些,例如在两个平台上都是用的是GNU编译器,除了-g-c标志外,不同的编译器使用的编译器标志可能大不相同,尤其是应用程序使用C++语言的时候。

     另外一个需要考虑的事情是编译器的版本。要移植的代码可能是几年前使用老版本的编译器编译的,这些代码可能使用了与新的编译器不同的语法,这就使得在不同的编译器上移植程序比较困难,因为这些编译器可能支持也可能不支持老的标准,即使新的编译器支持老的标准,不同编译器对这些标准的支持方式也可能不太相同。

.NET Core项目的C# 编译器的实现非常完整,和.NET编译器实现遵循同样的ECMA标准的“Roslyn”新编译器,大大降低了移植.NET项目的难度。

第三方软件和中间件的可用性

    当待移植的应用程序使用了第三方软件或中间件时,移植的复杂度会随之增加,尤其是在目标平台上没有这些第三方产品时,移植的难度会更大。可能会出现在Linux 平台上没有可用的第三方产品。在过去的几年中,一些中间件厂商,例如IBMOracle等都把他们的中间件产品移植到了Linux上。他们花了一些功夫,使那些已经使用或者准备使用Linux作为企业平台的公司在Linux上可以使用它们的中间件产品。这也是为什么越来越多的公司愿意把应用程序从Windows移植到Linux上的部分原因。

 

编译环境和工具

    

  编译环境越简单,理解如何编译源代码所需的时间也就越少。需要多遍编译的编译环境往往都很复杂。在这些多遍编译中,使用不同的编译器标志来编译目标文件,以便解决模块间的互相依赖。第一遍编译一些模块,第二遍可能会编译更多的模块,但是第二次编译使用了前面的编译的结果,以此类推,直到整个编译过程结束。有时候,根据一些非标准的配置文件,编译脚本会调用其他脚本来自动生成一些文件。这些文件的大部分可以和待移植的文件一样看待,但是事实上,这些脚本工具和配置文件才是要移植到目标平台上的内容、这种类型的工具往往会在评估和分析时漏掉了,进而可能会破坏已经制定的进度计划。

    源代码控制是另一个必须考虑的。在Linux环境下,SubversionGit是应用最广泛的源代码控制工具,在一个移植项目中,如果有多个工程师同时来移植相同的模块,那么就需要进行源代码控制。


平台依赖的结构

     当从基于x86Windows平台移植到基于RISC/ARM结构的Linux平台上时,需要检查应用程序对字节序的依赖和字母大小写敏感,例如,为了计算或数据操作而是用了字节交换的应用程序。在没有正确修改字节交换逻辑的情况下,调试问题将变得非常麻烦,因为很难找到数据在哪里出了问题,这主要是因为问题的根源通常是在发生问题的代码之前很远的地方。在调查和分析阶段,一定要找出平台依赖的结构。


平台/硬件依赖的代码

   需要内核扩展和设备驱动程序支持的应用程序时最难移植的。所有平台的内核API都不遵循任何标准。因此,API调用、参数个数,甚至是怎样把这些扩展装载到内核,在各个平台上都是不同的。多数情况下,都需要在Linux上开发新的代码。不过有一件事可以肯定,内核代码通常是使用C语言或者平台相关的汇编语言编写的。

 

搭建测试环境

     移植工作完成以后,如果项目的范围还包括系统测试和验证,测试代码所需要的设备可能也会增加移植的复杂度。如果应用程序需要在复杂的集群或网络环境中测试,设置环境和调配资源也会增加项目的时间。

      测试也可能包括性能测试。通常,性能测试都需要运行最大配置,以便检测应用程序在满负载下的运行情况。

     移植应用程序测试工具的工作也需要包含在移植进度计划中。需要对包括软件测试和专用硬件配置的测试工具进行评估,以确定其需要多长时间进行移植和配置。

 

用户界面需求

    移植用户界面可能很简单,也可能很复杂。基于ASP.NET的用户界面比较容易移植,基于WPF/WinForm技术的用户界面移植移植也简单,.NET Core已经支持WPF/WinForm,但是局限于Windows平台,WinForm 通过Mono可以实现跨平台

 

下面的表总结了需要考虑的各个方面,各个方面都根据其使用的技术列出了难、中、易。

 

内容

容易

中等复杂度

高复杂度

编译器/编程语言

C#F#

使用的语言在.NET Core上支持有限

待移植代码的是C++/CLI

使用第三方产品或中间件

None

.NET Core上支持的和可用的

Linux上不支持的;

使用了C++编写的第三方工具

编译环境和工具

MsBuild 脚本

MsBuild 脚本和编译脚本组合在一起


平台/硬件依赖的代码

非平台/硬件依赖的代码

平台/硬件依赖的代码来自第三方产品,而且已经移植到了.NET Core

使用了内核扩展及设备驱动代码

测试环境及其搭建

独立的

客户端/服务器设置

网络、高可用行,集群;需要外部设备来测试,如打印机、光纤连接通道磁盘等

用户界面

ASP.NET MVC

ASP.NET WebForm

不可移植的用户界面,例如WPF

      经验表明,整个移植项目所需的实际工作量,在移植项目开始的前两三周,就可以评估出来。这段时间是待移植代码刚交付给移植项目组,而且移植工程师刚开始熟悉该软件程序,也可能是移植工程师刚开始移植该软件程序。他们开始分解应用程序组件,并搭建最初的编译环境。在最初的两三周内,移植工程师会完成编译环境的配置,并开始编译一些模块。

     过了最初的两三周后,最好重新检查原先评估的进度计划,并根据刚得到的实际经验决定是否对计划进行调整。

创建项目移植进度表

      创建移植进度表时要考虑所有可能的风险,包括技术和商业相关的问题。技术方面,需要考虑资源和硬件是否可用、第三方的支持,以及Windows/Linux方面的经验等;商业方面,需要考虑部门重组,位置调整(如改变办公地点),发布给客户的日期,以及商业目标的改变等,这些都会影响到整个移植进度。

     上面这些技术和商业方面的问题,形成了移植项目对外部的依赖关系,而这些依赖又不是移植项目可以控制的。因此,建议仔细考虑每一个外部问题,以减少项目的风险。      

创建移植项目的进度计划和做开发项目类似。

   在移植过程中每次进入下一个步骤时,项目组都可以根据实际的进度和资源对项目重新估计。本质上讲,每一个阶段的结束,不仅仅是移植过程中的一个里程碑,也应该是重新检查先前估计得一个检查点。 

从商业角度看移植过程

      移植过程不仅仅是移植软件程序,移植后的应用程序最终还需要必要的商业支持,以成为一个完整的,成功的商业项目。在移植工作进行的同时,项目的相关干系人还需要准备对该应用程序提供支持的部门。客户支持和软件发布等部门还需要介绍应用程序在Linux上如何运行的支持和培训。

      对客户支持和软件发布人员进行必要的Linux培训,应该在项目目标中列为高优先级。与任何新产品一样,用户和系统管理员每次都会对新的发布提出很多问题。因此,也需要回答Linux系统管理的问题。我们的经验表明,刚移植到新的平台上的应用程序会产生很多关于系统管理以及如何使用新操作系统的问题,在支持热线接到的电话中,有五分之三的问题都是此类。

      随着系统管理和安装问题的解决,关于应用程序的技术问题会逐步显现。支持部门需要访问测试机器来复现客户的问题。这些机器可能是开发或移植机器,依赖于应用程序和需要调试解决的实际问题。

     从项目的整体来看,对支持的培训需求和提高应用程序支持所需资源的可用性,会在项目的后期出现。随着移植项目技术方面的工作趋于完成,商业内容也就逐渐显现。