不积跬步,无以至千里;不积小流,无以成江海。

Dean's blog

  • Join Us on Facebook!
  • Follow Us on Twitter!
  • LinkedIn
  • Subcribe to Our RSS Feed

经验教训 - 细节决定成败

这两天在与NC系统做对接,其中需要将客商数据通过接口推送过去,接口是普通的HTTP接口只需要将XML数据POST过去即可。我们已要可以生成系统需要的XML,但是在POST数据到接口的时候,总是出现问题。拿对方验证没有问题的XML,通过我们的代码POST过去,问题也依旧。

于是开始了漫长的排查过程。

为了验证接口是否调用是否有问题,接着都在使用对方提供的XML进行验证,相关的代码如下:

string root = AppDomain.CurrentDomain.BaseDirectory;
string path = Path.Combine(root, "Bid/VendorBaseInfo/templet/客商数据推送模板.xml");

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(path);

MemoryStream ms = new MemoryStream();
xmlDoc.Save(ms);

var buffers = ms.GetBuffer();

var request = (HttpWebRequest)WebRequest.Create(strPostUrl);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = buffers.Length;
request.Timeout = 10 * 60 * 1000;//增加超时设置,限定为10分钟 -- by diming 2014.4.19 10:02 

var streamRequest = request.GetRequestStream();
streamRequest.Write(buffers, 0, buffers.Length);//传递XML文档内容
streamRequest.Close();

//获取NC应用平台返回的处理结果
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

string response = streamResponse.ReadToEnd();

 

整个过程好像都没有问题,但也一样调用失败。发现通过XmlDocument加载后得到的XML,发现与原文件存在不一致的地方:

string path = "......";

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(path);

MemoryStream ms = new MemoryStream();
xmlDoc.Save(ms);

var xml = Encoding.UTF8.GetString(ms.GetBuffer())

这样,获取到的内容和原文件不同:

<!--原内容-->
<pk_supplier></pk_supplier>

<!--读取到的内容-->
<pk_supplier>
 </pk_supplier>

虽然对方说没影响,但是也很好奇为什么有不一样,将文件读取的方法改成了:

var buffers = File.ReadAllBytes(".....");

var request = (HttpWebRequest)WebRequest.Create(strPostUrl);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = buffers.Length;
request.Timeout = 10 * 60 * 1000;//增加超时设置,限定为10分钟 -- by diming 2014.4.19 10:02 

var streamRequest = request.GetRequestStream();
streamRequest.Write(buffers, 0, buffers.Length);//传递XML文档内容
streamRequest.Close();

//获取NC应用平台返回的处理结果
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

string response = streamResponse.ReadToEnd();

这样调整后,接口调用居然正常了。

对比两份代码,只有buffers的来源不同,对比发现使用MemoryStream返回的数组更大。找到MemoryStream.GetBuffers()的描述:

        "返回从其创建此流的无符号字节数组"

MemoryStream在分配内存的策略如下:

private bool EnsureCapacity(int value) {
    // Check for overflow
    if (value < 0)
        throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
    if (value > _capacity) {
        int newCapacity = value;
        if (newCapacity < 256)
            newCapacity = 256;
        if (newCapacity < _capacity * 2)
            newCapacity = _capacity * 2;
        Capacity = newCapacity;
        return true;
    }
    return false;
}

可以看到的是,最小256长度,后续分配都是以 * 2的方式增加。而MemoryStream.GetBuffers()是直接把分配到的内存返回,这样返回的内容会比实际内容要多。MemoryStream.Length返回的是实际内容长度。

将上面的代码调整为:

string root = AppDomain.CurrentDomain.BaseDirectory;
string path = Path.Combine(root, "Bid/VendorBaseInfo/templet/客商数据推送模板.xml");

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(path);

MemoryStream ms = new MemoryStream();
xmlDoc.Save(ms);

var buffers = ms.GetBuffer();

var request = (HttpWebRequest)WebRequest.Create(strPostUrl);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = ms.Length;
request.Timeout = 10 * 60 * 1000;//增加超时设置,限定为10分钟 -- by diming 2014.4.19 10:02 

var streamRequest = request.GetRequestStream();
streamRequest.Write(buffers, 0, Convert.ToInt32(ms.Length));//传递XML文档内容
streamRequest.Close();

//获取NC应用平台返回的处理结果
var response = (HttpWebResponse)request.GetResponse();
var streamResponse = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

string response = streamResponse.ReadToEnd();

这样即可以正常调用。

回顾排查问题的整个过程,没认真理解MemoryStream的内存管理策略才导致的问题。

真是细节,决定是不是累成狗。

不允许评论
粤ICP备17049187号-1