Natasha API-预热(三)

发布时间 2023-04-20 16:31:30作者: 摧残一生
NatashaInitializer.Preheating 预热方法

首次使用Natasha时都需要进行初始化操作,其中该方法还可以加入一个回调函数作为参数,该回调函数主要作用为减少程序集引用文件的加载,可以有效的控制内存涨幅

  • 通过判断Preheating方法中回调函数的第二个参数,可以有选择性的加载哪一类

    // 可以通过声明哪一些包不需要引用,例如Drawing,Xml等
    // 该变量的数值应该为某一部分的包,如果写System,所有包含System的都无法引用,例如System;System.Linq等
    List<String> noLoadDlls = new List<String>() { 
        "Drawing","Xml"
    };
    // 第二个参数为系统添加的dll名称
    // 当返回值为true时为排除该引用
    // 当返回值为false时为添加该引用
    NatashaManagement.Preheating((asName, name) => {
        if (!String.IsNullOrEmpty(name)) {
            // 判断noLoadDlls中是否存在符合参数二的数据
            // 如果有则返回noLoadDlls中的数据,否则返回空字符串
            // 将判断的字符串都变为小写或大写,这样就不会出现大小写判断的问题了
            var bReturn = noLoadDlls.Where(dll => name.ToLower().Contains(dll.ToLower())).ToList().FirstOrDefault("");
            return !String.IsNullOrEmpty(bReturn);
        }
        return false;
    });
    
  • Preheating方法中回调函数的第一个参数为AssemblyName,主要判断程序集版本号,程序集名称等

    AssemblyName中的Name就是回调函数中的第二个参数,单独拿出来的目的感觉主要还是对比名称

    // 排除 dapper 主版本号为 12 的程序集引用文件
    NatashaInitializer.Preheating((asmName, name) => {
        if (asmName.Name != null)
        {
            if (asmName.Name.Contains("Dapper") && asmName.Version!.Major > 12)
            {
                return true;
            }
        }
        return false;
    });
    
  • Preheating主要干了什么

    • 如果有回调函数,则赋值给DefaultUsing.SetDefaultUsingFilter和NatashaDomain.SetDefaultAssemblyFilter,让回调函数一直有效

    • 获取所有系统引用,回调函数,判断是否有手动排除引用的

      • 使用DependencyContext.Default.CompileLibraries 获取CLR中的所有库

      • 其中AssemblyName.GetAssemblyName的作用为将文件转换为程序集

        // DependencyContext.Default.CompileLibraries获取CLR中的所有库
        IEnumerable<string>? paths = DependencyContext
                 .Default
             	 // cl.ResolveReferencePaths 获得的应为绝对路径
                 .CompileLibraries.SelectMany(cl => cl.ResolveReferencePaths().Where(asmPath =>
                 {
                     //将文件转换为程序集
                    var asmName = AssemblyName.GetAssemblyName(asmPath);
                     // Preheating的回调函数
                    return !excludeReferencesFunc(asmName, asmName.Name);
                 }));
        
    • 将排除后的都添加到Natasha中

      // 1. 通过Path.GetFileNameWithoutExtension获得文件名
      // 2. 把获得的文件名与文件的绝对地址关联起来
      var resolver = new PathAssemblyResolver(paths);
      // 检查目的而加载的 Type 对象的封闭范围
      // 参见:https://learn.microsoft.com/zh-cn/dotnet/api/system.reflection.metadataloadcontext?source=recommendations&view=dotnet-plat-ext-7.0
      using (var mlc = new MetadataLoadContext(resolver))
      {
          // 并发
      	var result = Parallel.ForEach(paths, (path) =>
      	{
              Assembly assembly = mlc.LoadFromAssemblyPath(path);
              // 添加资源
              NatashaReferenceDomain.DefaultDomain.References.AddReference(assembly.GetName(), path);
              DefaultUsing.AddUsingWithoutCheck(assembly);
              // 将该类放到缓存中
              NatashaDomain.AddAssemblyToDefaultCache(assembly);
          });
          // 如果加载项一直没完成,则一直等待中
          while (!result.IsCompleted)
          {
          	Thread.Sleep(100);
          }
      }
      
    • 创建了一个Supperess为CS8019的配置实例,然后测试了一下,没问题就结束了

    • 有问题,那Preheating初始化会失败

    • 结束

  • 移除了不需要的包,如果想额外的添加类或dll

    // 第一个方法必不可少
    NatashaInitializer.Preheating();
    // 1.增加全局的 Using 引用
    NatashaManagement.AddGlobalUsing("System.IO");
    // 2.向全局引用中增加类型对应的元数据
    // 如果需要Natasha 自动覆盖全部引用,请引入 'DotNetCore.Compile.Environment' 包.
    NatashaManagement.AddGlobalReference(typeof(int));
    // 3.直接追加程序集到全局引用中
    //  path为dll文件的绝对路径
    Assembly assembly = mlc.LoadFromAssemblyPath(path);
    NatashaReferenceDomain.DefaultDomain.References.AddReference(assembly);