【dotnet】反编译记录

名词

MD 某项目简称

nuget dotnet包管理工具,类似于java 中的maven、gradle

反编译工具

ILSpy:https://github.com/icsharpcode/ILSpy

命令行版本:dotnet tool install ilspycmd -g

代码整理

公共模块整理

整理步骤

  1. 执行脚本1,把所有引入的模块名称和版本号打印出来,并排序&统计出现次数,放到文档里:
  2. 挨个整理每个微服务,根据微服务名称,基本可以看出【哪些是微服务自身的模块】,【哪些不是】
    1. 执行【脚本2】,查看他依赖的3方模块都是什么版本,更新到上面的文档中【谁在使用】
    2. 如果当前模块依赖的子模块版本低,先尝试引用最高版本,如果build失败,则引用现在的版本,留在以后解决(或不解决?),命名方式如:MD.BasicService.3.0.0.94
    3. 微服务可能会依赖一些中间件或开放模块,比如:grpc、redis等,可以放到lib目录下,如果有版本冲突,则加上版本号区分,比如:redis.4.0.1.dll
  3. 遍历一下 commonlibcode 下的模块:
    1. 根据名字,他该是哪个微服务的,就给他挪回去,同时在 sln 文件中添加声明;如果微服务下已经有了,就删掉;比如:
    2. 如果不是微服务中的模块,先不管
  4. 整理过程中,发现一些子模块找不到父模块,像:base、workflow,还有一些中间件模块,比如:MD.Grpc, MD.Etcd, MD.Kafka 等,看看要放到哪

我们会添加的目录:

lib:存放中间件、开放库等,如:redis.dll, kafka.dll

commonlibcode: 暂时存放MD内部模块

脚本1:获取公共模块及版本号

C#代码里各个微服务里引用了很多公共模块,但是某些模块版本不一样,所以写个脚本整理一下:

# 将 模块名和版本号 输出到文件
find . -name "AssemblyInfo.cs" | xargs grep "AssemblyProduct\|AssemblyVersion" > find_product_and_version.0.txt
# 输出: 模块名 版本号
cat find_product_and_version.0.txt |awk -F "[\"]" '{print $2}' | xargs -n 2 > find_product_and_version.1.txt
# 排序 & 去重 & 统计出现次数
cat find_product_and_version.1.txt | sort | uniq -c > find_product_and_version.2.txt
# 空格换成 \t
cat find_product_and_version.2.txt | tr " " \\t > find_product_and_version.3.txt

脚本2:查询子模块的名称和版本号

find . -name "AssemblyInfo.cs" | xargs grep "AssemblyProduct\|AssemblyVersion" | awk -F "[\"]" '{print $2}' | xargs -n 2 | sort

GRPC服务、客户端代码

背景:部分以 grpcservice、xxxservice 是由 proto 文件生成的,官方自己有一个 grpc proto to nuget package 插件,可以从 proto 直接生成 NuGet 包,但是没法用,而且(java中有个 a-proto)项目中的 proto 不一定是最新的,所以也不一定能直接使用;
这里提供所有可能的办法:

直接引用 proto

缺点:proto 可能是过时的,缺少一些类和方法
> dotnet-grpc add-file xxxx.proto
csproj中会生成对应的proto引入声明,这个时候就能直接使用了;
对于多个proto集合,比如:AService.proto 引入了 common/ARequest.proto  ,最好先引入 common 下的 proto,再引入 AService.proto ;(目前测试是这样)

直接修改反编译后的 grpc 代码

常见问题:
包含[AsyncStateMachine]注解的地方,是反编译生成的,改成 async关键字,比如 public async Result funcA…{}
如果涉及到增加字段的需求,可能涉及到的【客户端】、【服务端】 都要重新部署

检查版本号

(第一次)反编译全部服务后,检查deps.json中的依赖是否可用,对照AssemblyInfo.cs中的版本号是否一致;

  • 如果 deps.json 和 AssemblyInfo.cs 中的版本一致,则以后升级都以deps.json中的版本为准,进行增量升级;
  • 如果 deps.json中的版本号不可用,则每次升级都要全部反编译,查看每个服务依赖的模块的AssemblyInfo.cs中的版本,如果有改动,则增量升级;

AssemblyInfo.cs + xxx.csproj 相当于 java maven 中的 pom.xml

  • AssemblyInfo.cs:声明了当前项目模块的版本、公司信息、开发信息等
  • xxx.csproj:声明了当前模块声明的依赖项,相当于 maven 的 <dependencies>,还可以指定模块的版本(<version>),但是MD反编译出来的并没有版本号,只有指定了版本信息,xxx.deps.json 里的版本号才是正确的

xxx.deps.json 是 dotnet build 之后的产物,相当于java maven中的 mvn dependency:effective

反编译步骤脚本

## 目标

反编译之后的代码,要求:

- `dotnet build` 不报错
- `dotnet run` 不报错
- 部署到线上能正常运行,且不报错

## 重要!!!!

每个 csproj 中,依赖的本项目中的模块,先不要反编译,直接引用 dll,等到所有代码都`SK化`之后再调整

## 反编译过程

分几个迭代过程来进行

### 目标

主要是反编译几个微服务(比如:wwwapi 的 MD.Web.*.dll 等)的核心代码,依赖的内部模块(比如:MD.Redis等)放到 step2

### 核心模块

比如:

- api
- MD.Web.Api.dll
- MD.Web.Ajax.dll
- order
- MD.OrderService
- MD.OrderService.Core
- MD.OrderService.GrpcService

可以看出,每个 dll 都是以 `MD.` + `微服务名称` + `模块功能` + `.dll` 命名的;

其他看不出来的,可以去容器里的启动命令,来判断核心模块是哪个;

### 步骤

1. 把每个微服务的libs拷贝到各自项目的 `libs` 下
2. 反编译
1. 命令如:`ilspycmd -p -o ./MD.Base/ ../libs/MD.Base*.dll -lv CSharp8_0`,`-lv` 是必须的
3. 修复csproj文件的依赖:
1. nuget能直接下载的包: <Reference> -> <PackageReference Include=”xxx” Version=”xxx”>,参照 deps.json 修改 csproj 中的依赖的版本
2. 内部包 或 nuget搜不到的,直接引用 dll : 修正libs目录,libs -> ../libs
4. 反复build,直到没有error
5. 运行
1. 如果报错: grpc …
1. 检查 target/xxx 下面是否有 runtimes 文件夹,如果没有,则把 grpc 的依赖方式改成`<PackageReference Include=”Grpc.Core” Version=”2.39.1″/>`,不要用 `<Reference>`
2. 检查csproj 是否配置了:<OutputPath>../../target/api</OutputPath>,目的是指定打包产出物的存放目录

### 反编译后的代码应该放到哪?

分几种情况:

- 直接看dll名称,就知道属于哪个微服务
- 属于公共模块,这些模块大部分会在 AssemblyInfo.cs 里有这样的标识:`[assembly: AssemblyMetadata("RepositoryUrl", "https://sourcecode.mingdao.net/mingdao/MDNuget/tree/master")]`,可以看出是属于`MDNuget`这个项目

### 如何处理这个模块的依赖

参考 `步骤3`

## Q&A

- 涉及到 依赖 `DapperExtensions` 编译失败,应该改为:`Dapper.Extensions.Core`

## 其他
- 如何打包上传到nuget私服
> nuget pack : 生成 xxx.nupkg
> nuget push {xxx.nupkg} 44e6ae7c-9000-33e1-9b84-be0ee319ad52 -src https://nexus.xxx.cn/repository/nuget-hosted/

增量升级

建议参考项目的官方文档、roadmap、release等等;

将所有依赖上传到nexus

如果官方模块升级了,代码反编译后,先调整好项目结构,然后用nuget上传到nexus

修改包的引用方式

将csproj中的依赖全部改成<PackageReference Include=”XXX” Version=”X.X.X”>,方便以后包的管理,dotnet build时,会自动从nexus中查找

注:dotnet引入依赖的语句有多种,比如 <Package …/> , <PackageReference …/> 等

Q&A

磁盘空间爆满

提示:无法为立即文档创建临时文件: 设备上没有空间

提示:No device space left

查找大文件,主要是dotnet进程异常退出会生成 core 文件,先删除:
find /var/lib/docker/ -name “core.” -type f -size +100M find /var/lib/docker/ -name “core.” -type f -size +100M | xargs rm
查看linux生成core文件的限制:ulimit -a
不生成core文件:ulimit -c 0,服务器和容器内最好都执行一下
清理docker:docker system prune

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注