优化动态调用WebSerivce

起因

因为在项目调用WebApi,但在WebApi调用了一个WebSerivce,看到动态调用WebSerivce,因为是另外一个组开发的,只是感觉这样动态调用WebSerivce性能不太好.并进行了代码调整.
调整优化的思路:

  1. 减少每次都通过WebClient获取Wsdl
  2. 减少每次都根据Wsdl,生成请求代理类
  3. 减少程序集加载的次数

项目中的代码(待优化版本)

带优化的版本,存在以下问题:
  1. 每次调用都会通过WebClient请求以下WebSerivce的wsdl描述文件(没有必要)
  2. 每次调用动态生成dll,如果请求数量比较大时,会造成CPU很高
  3. 每次都会加载新生成dll(程序集),然后反射.
private static object CallWebService(string url, string methodname, object[] args)
{
    string nameSpace = "qiufeng.webservice";
    try
    {
        //获取WSDL
        WebClient wc = new WebClient();
        Stream stream = wc.OpenRead(url + "?WSDL");
        ServiceDescription sd = ServiceDescription.Read(stream);
        string classname = sd.Services[0].Name;
        ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
        sdi.AddServiceDescription(sd, "", "");
        sdi.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;//标准和异步
        CodeNamespace cn = new CodeNamespace(nameSpace);
        //生成客户端代理类代码
        CodeCompileUnit ccu = new CodeCompileUnit();
        ccu.Namespaces.Add(cn);
        sdi.Import(cn, ccu);
        CSharpCodeProvider csc = new CSharpCodeProvider();
        CompilerParameters cplist = new CompilerParameters
        {
            GenerateExecutable = false,
            GenerateInMemory = true,
            OutputAssembly = string.Format("{0}/{1}.dll", Directory.GetCurrentDirectory(), "WebService1")
        };
        cplist.ReferencedAssemblies.Add("System.dll");
        cplist.ReferencedAssemblies.Add("System.XML.dll");
        cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
        cplist.ReferencedAssemblies.Add("System.Data.dll");
        //编译代理类
        CompilerResults cr = csc.CompileAssemblyFromDom(cplist, ccu);
        if (true == cr.Errors.HasErrors)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            foreach (CompilerError ce in cr.Errors)
            {
                sb.Append(ce.ToString());
                sb.Append(System.Environment.NewLine);
            }
            throw new Exception(sb.ToString());
        }

        //生成代理实例,并调用方法
        Assembly assembly = cr.CompiledAssembly;
        Type t = assembly.GetType(nameSpace + "." + classname, true, true);
        object obj = Activator.CreateInstance(t);
        MethodInfo mi = t.GetMethod(methodname);
        object objs = mi.Invoke(obj, args);
        return objs;
    }
    catch (Exception ex)
    {
        return ex;
    }
}
string url = "http://localhost:50309/WebService1.asmx";

object o1 = CallWebService(url, "GetName", new object[] { "Hello" });
Console.WriteLine(o1);

优化第一版

/// <summary>
/// 优化第一版
/// </summary>
/// <param name="url"></param>
/// <param name="methodname"></param>
/// <param name="args"></param>
/// <returns></returns>
private static object CallWebService_Optimize2(string url, string methodname, object[] args)
{
    const string nameSpace = "qiufeng.webservice";
    object objs = null;
    try
    {

        //1. 判断是否加过Service的程序集
        if (serviceAssm == null)
        {
            //2. 判断文件当前目录是否生成WebSerivce的dll
            string filePath = string.Format("{0}/{1}.dll", Directory.GetCurrentDirectory(), nameSpace);
            if (File.Exists(filePath))
            {
                serviceAssm = Assembly.LoadFrom(filePath);
                fullName = ConfigurationManager.AppSettings[nameSpace];
            }
            else
            {
                //3. 没有生成Service的dll文件情况

                //获取WSDL
                WebClient wc = new WebClient();
                Stream stream = wc.OpenRead(url + "?WSDL");
                ServiceDescription sd = ServiceDescription.Read(stream);
                string classname = sd.Services[0].Name;
                string classFullName = $"{nameSpace}.{classname}";
                ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
                sdi.AddServiceDescription(sd, "", "");
                sdi.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;//标准和异步
                CodeNamespace cn = new CodeNamespace(nameSpace);
                //生成客户端代理类代码
                CodeCompileUnit ccu = new CodeCompileUnit();
                ccu.Namespaces.Add(cn);
                sdi.Import(cn, ccu);
                CSharpCodeProvider csc = new CSharpCodeProvider();
                //设定编译参数
                CompilerParameters cplist = new CompilerParameters
                {
                    GenerateExecutable = false,
                    GenerateInMemory = true,
                    OutputAssembly = filePath
                };
                cplist.ReferencedAssemblies.Add("System.dll");
                cplist.ReferencedAssemblies.Add("System.XML.dll");
                cplist.ReferencedAssemblies.Add("System.Web.Services.dll");
                cplist.ReferencedAssemblies.Add("System.Data.dll");
                //编译代理类
                CompilerResults cr = csc.CompileAssemblyFromDom(cplist, ccu);
                if (true == cr.Errors.HasErrors)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (CompilerError ce in cr.Errors)
                    {
                        sb.Append(ce.ToString());
                        sb.Append(Environment.NewLine);
                    }
                    throw new Exception(sb.ToString());
                }
                var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
                config.AppSettings.Settings[nameSpace].Value = classFullName;
                config.AppSettings.SectionInformation.ForceSave = true;
                config.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection("appSettings");
                fullName = classFullName;                serviceAssm1 = Assembly.LoadFrom(filePath);
            }
        }
        Type t = serviceAssm.GetType(fullName, true, true);
        object obj = Activator.CreateInstance(t);
        MethodInfo mi = t.GetMethod(methodname);
        objs = mi.Invoke(obj, args);
        return objs;
    }
    catch (Exception ex)
    {
        return ex;
    }
}

对动态调用WebSerivce优化前后进行对比

发现调整后,性能还是很明显的.调整前在BenchmarkDotNet.没有显示时间.

其他的优化思路

每次调用调用WebSerivce的时候都会对生成的请求代理类,进行反射创建.可以改为委托创建,并缓存委托.
private static T GenerateNewObjDelegate<T>(Type type) where T : class
{
    // Create a new, parameterless (specified by Type.EmptyTypes) dynamic method.
    var dynamicMethod = new DynamicMethod("Ctor_" + type.FullName, type, Type.EmptyTypes, true);
    var ilGenerator = dynamicMethod.GetILGenerator();

    // Look up the constructor info for the type we want to create
    var ctorInfo = type.GetConstructor(Type.EmptyTypes);
    if (ctorInfo != null)
    {
        ilGenerator.Emit(OpCodes.Newobj, ctorInfo);
        ilGenerator.Emit(OpCodes.Ret);

        object del = dynamicMethod.CreateDelegate(typeof(T));
        return (T)del;
    }
    return null;
}
秋风 2021-04-05