65.9K
CodeProject 正在变化。 阅读更多。
Home

性能对比:ATOM 与 JSON

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2013年10月23日

CPOL

3分钟阅读

viewsIcon

16103

性能对比:ATOM 与 JSON。

背景

在我们的项目中,我们使用 SAP 作为后端数据源,并使用 NWGW(Netweaver Gateway)适配器从 .NET 客户端以 OData 格式获取数据。由于 NWGW 组件托管在本地,而我们的 .NET 客户端托管在 Azure 中,因此我们通过 Service Bus 中继从 Azure 消费这些数据。在通过 SB 中继从本地传输数据到 Azure 时,我们面临着单用户大数据量以及并发用户小数据量时的性能问题。因此,我进行了一些 POC,以通过 JSON 格式消费 OData 服务来提高性能。

我做了什么?

我创建了一个简单的 WCF 数据服务,没有底层数据源连接。在该服务的上下文初始化时,会生成一个文本消息列表并将其作为 OData 暴露。

这是简单的服务代码

[Serializable]
public class Message
{
    public int ID { get; set; }
    public string MessageText { get; set; }
}
public class MessageService
{
    List<Message> _messages = new List<Message>();
    public MessageService()
    {
        for (int i = 0; i < 100; i++)
        {
            Message msg = new Message
            {
                ID = i,
                MessageText = string.Format("My Message No. {0}", i)
            };
            _messages.Add(msg);
           
        }
    }
    public IQueryable<Message> Messages
    {
        get
        {
            return _messages.AsQueryable<Message>();
        }
    }
}
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class WcfDataService1 : DataService<MessageService>
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(DataServiceConfiguration config)
        {
            // TODO: set rules to indicate which entity sets 
            // and service operations are visible, updatable, etc.
            // Examples:
            config.SetEntitySetAccessRule("Messages", EntitySetRights.AllRead);
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
        }
    } 

暴露一个端点到 Azure SB,以便客户端可以通过 SB 端点消费该服务。在托管服务后,我可以通过浏览器使用简单的 OData 查询获取数据。

我也可以以 JSON 格式获取数据。

之后,我创建了一个控制台客户端应用程序,并从那里消费该服务。

示例客户端代码
class Program
{
    static void Main(string[] args)
    {
        List<Thread> lst = new List<Thread>();

        for (int i = 0; i < 100; i++)
        {
            Thread person = new Thread(new ThreadStart(MyClass.JsonInvokation));
            person.Name = string.Format("person{0}", i);
            lst.Add(person);
            Console.WriteLine("before start of {0}", person.Name);
            person.Start();
            //Console.WriteLine("{0} started", person.Name);
        }
        Console.ReadKey();
        foreach (var item in lst)
        {
            item.Abort();
        }
    }
}

public class MyClass
{ 
    public static void JsonInvokation()
    {
        string personName = Thread.CurrentThread.Name;
        Stopwatch watch = new Stopwatch();
        watch.Start();
        try
        {
            SimpleService.MessageService svcJson = 
            new SimpleService.MessageService(new Uri
            ("https://abc.servicebus.windows.net/SimpleService /WcfDataService1"));
            svcJson.SendingRequest += svc_SendingRequest;
            svcJson.Format.UseJson();
            var jdata = svcJson.Messages.ToList();
 
            watch.Stop();
            Console.WriteLine("Person: {0} - JsonTime First Call time: {1}", 
              personName, watch.ElapsedMilliseconds);

            for (int i = 1; i <= 10; i++)
            {
                watch.Reset(); watch.Start();
                jdata = svcJson.Messages.ToList();
                watch.Stop();
                Console.WriteLine("Person: {0} - Json Call {1} time: 
                {2}", personName, 1 + i, watch.ElapsedMilliseconds);
            }

            Console.WriteLine(jdata.Count);
        }
        catch (Exception ex)
        {
            Console.WriteLine(personName + ": " + ex.Message);
        }
        Thread.Sleep(100);
    }

    public static void AtomInvokation()
    {
        string personName = Thread.CurrentThread.Name;
 
        try
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            SimpleService.MessageService svc = 
            new SimpleService.MessageService(new Uri
            ("https://abc.servicebus.windows.net/SimpleService/WcfDataService1"));
            svc.SendingRequest += svc_SendingRequest;
            var data = svc.Messages.ToList();

            watch.Stop();
            Console.WriteLine("Person: {0} - XmlTime First Call time: {1}", 
              personName, watch.ElapsedMilliseconds);

            for (int i = 1; i <= 10; i++)
            {
                watch.Reset(); watch.Start();
                data = svc.Messages.ToList();
                watch.Stop();
                Console.WriteLine("Person: {0} - Xml Call {1} time: 
                {2}", personName, 1 + i, watch.ElapsedMilliseconds);
            }

            Console.WriteLine(data.Count);
        }
        catch (Exception ex)
        {
            Console.WriteLine(personName + ": " + ex.Message);
        }
        Thread.Sleep(100);
    }
} 

之后我测试了什么

我测试了两种不同的场景

场景一:单用户,小数据量和大数据量

定期测量 XML 格式和 JSON 格式的数据传输时间。您可能会注意到,第一次调用我单独打印在每个屏幕截图中,因为它需要额外的时间来连接到 SB 端点。在第一次调用中,正在进行密钥认证。

小数据集(数组大小 10):以 XML 格式消费。

以 JSON 格式消费

对于小数据集,Json 和 XML 的响应时间在服务总线中继上几乎相同。

消费大数据量(数组大小 100)

这里的 XML 消息大小约为 51 KB。现在我将以 JSON 格式消费相同的数据列表(数组大小 100)。

从上述测试场景来看,很明显 JSON 响应时间比 XML 响应时间快得多,原因在于消息大小。在这个测试中,当我以 XML 格式获取 100 条记录的列表时,消息大小为 51.2 KB,但 JSON 消息大小为 4.4 KB。

场景二:100 个并发用户,大数据量(数组大小 100)

在这个并发用户负载测试中,我没有进行任何服务节流或最大并发连接配置。

在上面的屏幕截图中,您会发现我在 XML 响应中遇到的一些超时错误。这是由于中继上的高响应时间造成的。但是,当我以 JSON 响应执行相同的测试时,我发现响应时间非常稳定且比 XML 响应更快,并且没有出现任何超时。

如何轻松使用 UseJson()

如果您使用的是 WCF 数据服务 5.3 及更高版本以及 VS2012 更新 3,那么要从客户端消费 JSON 结构,我必须使用 <ServiceInstance>.Format.UseJson() 实例化代理/上下文。在这里,您不需要通过编写任何自定义代码来单独加载 Edmx 结构。.NET CodeGen 会在您添加服务引用时生成该代码。

但是,如果您的环境中没有生成该代码,那么您需要编写几行代码来加载 edmx 并将其用作 <service instance>.Format.UseJson(LoadEdmx(<service name>));

加载 Edmx 的示例代码
public static IEdmModel LoadEdmx(string srvName)
{
    string executionPath = Directory.GetCurrentDirectory();
    DirectoryInfo di = new DirectoryInfo(executionPath).Parent;
    var parent1 = di.Parent;
    var srv = parent1.GetDirectories("Service References\\" + 
    srvName)[0].GetFiles("service.edmx")[0].FullName;

    XmlDocument doc = new XmlDocument();
    doc.Load(srv);
    var xmlreader = XmlReader.Create(new StringReader(doc.DocumentElement.OuterXml));
    
    IEdmModel edmModel = EdmxReader.Parse(xmlreader);
    return edmModel;
}

编码愉快…

© . All rights reserved.