▄︻┻┳═一「static」Agenda:

▄︻┻┳═一static,你还敢用吗?

▄︻┻┳═一static,你还敢用吗?(二)

▄︻┻┳═一【轻松一刻!】一段难倒了两名老程序猿的简单代码


我用火狐的HttpRequester测试开发组里一个同学发布的Web API接口,遇到了一个奇怪的问题。 我测试边界情况时,第一次调用响应的结果是正常的,但当再次及以后的请求时,却返回了异常“System.ObjectDisposedException: 无法访问已释放的对象”。 每次重新发布后,都是第一次请求是正常的,之后的请求就出现这个异常。

System.ObjectDisposedException: 无法访问已释放的对象。
对象名:“System.Net.Http.StringContent”。
在 System.Web.Http.WebHost.HttpControllerHandler.EndProcessRequest(IAsyncResult result)
在 System.Web.Http.WebHost.HttpControllerHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
在 System.Web.HttpApplication.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar)

下面是第一次的响应正常的请求。

如下是raw transaction:

POST http://localhost:3102/api/Notification/SmS
Content-Type: application/json
{
"OrderNo": "string",的
"BusinessType": "string",
"SendNodeCode": "string"
}
-- response --
500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 70
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcU291cmNlUHJvamVjdFxub3RpZmljYXRpb25cdHJ1bmtcWU0uTm90aWZpY2F0aW9uXGFwaVxOb3RpZmljYXRpb25cU21T?=
X-Powered-By: ASP.NET
Date: Mon, 06 Jun 2016 11:23:09 GMT


{ "data" : null , "status" : "103" , "message" : "数据格式不附"}


因为这个API应用程序的日志记录Filter、异常处理Filter的实现和使用方式与另一个API项目很类似。而那个API站点没有出现这样的问题,所以,很奇怪为什么这里出现了这样的异常。

如下是排障过程:

通过调试程序,发现错误定位在如下代码的红色加粗的方法上。 这块代码是用来实现记录统一的接口请求/响应日志的。

public class CustomMessageHandler : DelegatingHandler
{
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (!request.RequestUri.ToString().ToLower().Contains("swagger"))
{
SysLogToFile.WriteNotificationMessage(string.Format("请求url:{0}\r\n请求参数:{1}\r\n",
request.RequestUri, request.Content.ReadAsStringAsync().Result));
}
return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(task =>
{
if (!request.RequestUri.ToString().ToLower().Contains("swagger"))
{
SysLogToFile.WriteNotificationMessage(string.Format("请求url:{0}\r\n请求参数:{1}\r\n响应结果:{2}",
request.RequestUri, request.Content.ReadAsStringAsync().Result, task.Result.Content.ReadAsStringAsync().Result));
}
return task.Result;
});
}
}

其中, task的Result是一个HttpResponseMessage对象,Content是它的一个System.Net.Http.HttpContent类型的属性,如下是老盖茨对其ReadAsStringAsync方法的说明

#region 程序集 System.Net.Http.dll, v2.0.0.0
// D:\SourceProject\notification\trunk\packages\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll
#endregion using System;
using System.IO;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks; namespace System.Net.Http
{
// 摘要:
// 表示 HTTP 实体正文和内容标头的基类。
public abstract class HttpContent : IDisposable
{
// 摘要:
// 初始化 System.Net.Http.HttpContent 类的新实例。
protected HttpContent();
//
// 摘要:
// 释放由 System.Net.Http.HttpContent 使用的非托管资源和托管资源。
public void Dispose();
//
// 摘要:
// 释放由 System.Net.Http.HttpContent 使用的非托管资源,并可根据需要释放托管资源。
//
// 参数:
// disposing:
// 如果为 true,则释放托管资源和非托管资源;如果为 false,则仅释放非托管资源。
protected virtual void Dispose(bool disposing);
//
// 摘要:
// 以异步操作将 HTTP 内容写入流。
//
// 返回结果:
// 返回 System.Threading.Tasks.Task<TResult>。 表示异步操作的任务对象。
public Task<string> ReadAsStringAsync();
}
}

通过查看这个CustomMessageHandler类中引用的日志工具类SysLogToFile, 发现写日志文件是通过一个Queue来异步实现的。 那怀疑,是不是在异步记日志时task.Result.Content已经被销毁了呢。 所以,对上面这段记日志的代码做个改动:

                    string log = string.Format("请求url:{0}\r\n请求参数:{1}\r\n响应结果:{2}",
request.RequestUri, request.Content.ReadAsStringAsync().Result, task.Result.Content.ReadAsStringAsync().Result);
SysLogToFile.WriteNotificationMessage(log);

自信满满地以为这样就没问题了。经再测试后,发现并非如此,问题依然存在。(ps:读书少不行呀,其实.net在调用日志工具类之前,已经把string.Format这段代码给执行并计算了,所以,事先声明个变量是于事无补的)

继续排障,接下来看Sms接口的实现代码。

 /// <summary>
/// 通知发短信
/// </summary>
[HttpPost]
public HttpResponseMessage SmS(InPutNotifiSmsModel inputModel)
{
if (inputModel == null)
{
9 return YMHttpResponseMessage.ServerDataValidateErrorMessage;
} string returnvalue = NotificationBusiness.NotificationSmS(inputModel.OrderNo, inputModel.BusinessType, inputModel.SendNodeCode); if (returnvalue == "")
{
return YMHttpResponseMessage.GetMessage(CustomeHttpMessageResponseStatusCode.SUCCESS, "发送成功");
}
else if (returnvalue == "-1")
{
return YMHttpResponseMessage.GetMessage(CustomeHttpMessageResponseStatusCode.INTERNALSERVERERROR, "此订单不存在");
}
else if (returnvalue == "-2")
{
return YMHttpResponseMessage.GetMessage(CustomeHttpMessageResponseStatusCode.INTERNALSERVERERROR, "不存在通知");
}
else if (returnvalue == "-3")
{
return YMHttpResponseMessage.GetMessage(CustomeHttpMessageResponseStatusCode.INTERNALSERVERERROR, "不存在预定人");
}
else
{
return YMHttpResponseMessage.GetMessage(CustomeHttpMessageResponseStatusCode.INTERNALSERVERERROR, "发送失败");
}
}

因为是非法的json串,所以inputModel的值是null。经过持续调试程序,小组里一个细心的同学发现,重新运行项目后第一次调试时,执行第9行;而在之后再调试时,是不走第9行的。

这时就可以判断,问题可能出在第9行了。

再次排查发现YMHttpResponseMessageServerDataValidateErrorMessage是静态的

public class YMHttpResponseMessage
{
/// <summary>
/// 服务资源未找到
/// </summary>
public static HttpResponseMessage ServiceNotFoundMessage; /// <summary>
/// 服务器异常
/// </summary>
public static HttpResponseMessage ServerErrorMessage; /// <summary>
/// 数据格式不附
/// </summary>
public static HttpResponseMessage ServerDataValidateErrorMessage; static YMHttpResponseMessage()
{
....

ServerDataValidateErrorMessage
= new HttpResponseMessage()
{
Content = new StringContent(
new ResponseModel()
{
data = Newtonsoft.Json.JsonConvert.SerializeObject(null),
status = CustomeHttpMessageResponseStatusCode.INTERNALSERVERERROR,
message = "数据格式不附"
}.ToString(),
Encoding.UTF8,
"application/json"
),
StatusCode = System.Net.HttpStatusCode.InternalServerError
};
}

再与那个没问题的API项目源码比较,发现其里面每次处理请求的返回值都是new一个HttpResponseMessage对象。

这样,就大概知道原因了, Http请求是无状态的,.net CLR每次处理完请求后,会利用其自带的垃圾回收机制,将托管的对象释放掉。 而static修饰的对象,其一直保存在内存中。虽然被释放掉了,但对象还存在。 所以,就抛出了题目中提到的异常:System.ObjectDisposedException: 无法访问已释放的对象

那么,看来,是static惹的祸了。赶紧改程序修复后,发现问题解决了。

我们小组那个同学不禁长吁,大家用static还是小心为好。

记得以前在电商公司做项目时,就遇到类似static导致系统问题的情况。当然,再次强调,读书少是要吃亏的, 需弄明白static这些用法才是王道。

最后,吐槽一下博客园。

我初用博客园是在2011年也就是我入职那家电商公司后,认识了很多牛逼的程序猿,大家都用博客园,我自然也跟着用。这样显得牛逼嘛。 当时写过一篇随笔,js啊,我被open撞了一下腰, 看到半小时内很多点击量,也有很多人跟着来讨论,甚是兴奋。后来的一些随笔,越来越少有那种热闹劲儿了。 当然,跟自己能力有很大关系。  如今自己也持续了这么多年每月写作,在这里,给自己点一个赞,只为这份坚持。

static,你还敢用吗?的更多相关文章

  1. boost的link 和 runtime-link,搭配shared 和 static

    转自:http://blog.csdn.net/yasi_xi/article/details/8660549 link:生成动态链接库/静态链接库.生成动态链接库需使用shared方式,生成静态链接 ...

  2. Static NAT with iptables on Linux

    本文的名字取的比较有意义,因为本文并不是真的要讨论如何在Linux上使用iptables实现static nat!之所以这么命名本文,是想引起别人的注意,因为中文资料,以及国内的搜索引擎,基本上没有人 ...

  3. java中关键字static和final

    面向对象的不足 凡是有利必有弊,强对象编程,使得语法简单统一,但也有其缺点,而且有很多.我们在接下来的课程里会一点点接触到.我们今天先看第一个. 有些变量和函数确实没必要定义在一个类里.强行规定这些函 ...

  4. 浅谈this和static

    一.this关键字 一个比较经典的使用: 输出的结果是:12 1.this关键字只能在方法的内部使用,表示对“调用方法的那个对象”的引用,this的用法和其他对象引用并无不同.注意一点:在方法的内部调 ...

  5. link 和 runtime-link,搭配shared 和 static(转)

    原文转自 http://blog.csdn.net/yasi_xi/article/details/8660549 参考: http://bbs.sjtu.edu.cn/bbscon,board,C, ...

  6. Java中static修饰类的问题

    Java中static修饰类的问题 众所周知,Java中static关键字可以修饰方法与变量: 修饰变量的时候,这个变量属于类变量,可以直接通过类名.变量名来引用. 修饰方法的时候可以直接通过类名.方 ...

  7. static关键字真能提高Bean的优先级吗?答:真能

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  8. static关键字有何魔法?竟让Spring Boot搞出那么多静态内部类

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  9. PHP 面向对象编程和设计模式 (2/5) - 静态变量、属性和方法及延迟绑定

    PHP高级程序设计 学习笔记 2014.06.10 Static(静态)关键字用来定义静态方法和属性,static 也可用于定义静态变量以及后期静态绑定. 1.静态变量 static variable ...

  10. JavaSE基础第四篇

    1.参数传递   2,方法的重载 方法的参数的个数.类型.顺序 跟修饰符.返回值无关   3.构造方法: return 表示当前方法执行结束,后面不能写任何语句   4工程导入 单个.java文件粘贴 ...

随机推荐

  1. Visual Studio 2010的MSDN帮助文档离线使用

    如果没有在安装vs过程中安装帮助,也可通过Visual Studio帮助菜单中的Manage Help Settings来对帮助进行设置或安装. 可以选择从磁盘安装内容,如果选择从磁盘安装可能会要求提 ...

  2. 如何一步一步用DDD设计一个电商网站(二)—— 项目架构

    阅读目录 前言 六边形架构 终于开始建项目了 DDD中的3个臭皮匠 CQRS(Command Query Responsibility Segregation) 结语 一.前言 上一篇我们讲了DDD的 ...

  3. clr 元数据

    clr相关编译器编译生成的托管模块由四部分组成:PE32或32+头.clr头.元数据.IL代码. 元数据和IL代码完全对应,保持一致(:>)性. 元数据有很多用途: VS的智能感知,自动补全: ...

  4. H5程序员如何利用cordova开发跨平台应用

    什么是Cordova? Cordova以前也叫PhoneGap,它提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头.麦克风等.Cordova还 ...

  5. 简记某WebGIS项目的优化之路

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 该项目为研究生时的老师牵头,个人已毕业数年,应老师要求协助其 ...

  6. canvas快速绘制圆形、三角形、矩形、多边形

    想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...

  7. ExecuteOrDelayUntilScriptLoaded 还是 SP.SOD.executeFunc?

    SharePoint 客户端 JS 开发时,要等待 SharePoint 对象都加载完毕再调用自己的方法(myFunction),可以有两种方式: ExecuteOrDelayUntilScriptL ...

  8. 最新的 cocoaPods 安装方法

    经过努力终于发现了最新的 解决cocoaPods安装的办法: taobao Gems 源已停止维护,现由 ruby-china 提供镜像服务 第一步:安装rvm, 不管需不需要升级ruby,rvm可以 ...

  9. Win10连接远程桌面时提示“您的凭据不工作”

    我遇到这个问题的时候查找网上都给出一堆高大上的解决办法, 然而我的错误实际上是用户名的问题, 很多人以为远程用户名就一定是锁屏状态下的登录名, 其实不是,跟自己设置有关,所以首先应该检查远程用户名是否 ...

  10. EntityFramework 6 + Mysql 生成POCOs

    问题 使用EDMX文件 EF Power Tools参数不正确的解决方法 对于"异常来自 HRESULT:0x80070057 (E_INVALIDARG)",有方法说" ...