这两天在与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的内存管理策略才导致的问题。
真是细节,决定是不是累成狗。