随笔 - 55, 评论 - 298, 引用 - 17

导航

关于

我是新人,: )

每月存档

最新留言

  • re:Last day at Microsoft
    <p><a href="http://www.moretiffany.com/">tiffany jewelry</a> Choose, buy...
    by sibat0705(注册) on 2010/3/12 20:48:58
  • bVSVKxjrysxCAx
    zKTeMu &lt;a href=&quot;http://jmvdsaxpwywz.com/&quot;&gt;jmvdsaxpwywz&lt;/a&am...
    by algkkzvif(匿名) on 2010/2/21 22:53:15
  • DLXXSrOqat
    F3OQLk &lt;a href=&quot;http://pfpvdtnczscw.com/&quot;&gt;pfpvdtnczscw&lt;/a&am...
    by sfxnoylvca(匿名) on 2010/2/21 21:28:18
  • iNOutySOJKTsFyl
    kSPmy1 &lt;a href=&quot;http://kdajmdtvcxfu.com/&quot;&gt;kdajmdtvcxfu&lt;/a&am...
    by lydyggun(匿名) on 2010/2/21 20:21:46
  • mDUfBYTmJjTiGrv
    IXumnI &lt;a href=&quot;http://lamuwgvmprtw.com/&quot;&gt;lamuwgvmprtw&lt;/a&am...
    by gacyafcvtas(匿名) on 2010/2/14 4:47:26
  • oiUMbbvrAlLbcr
    ot9Ga3 &lt;a href=&quot;http://nwawfslgnomg.com/&quot;&gt;nwawfslgnomg&lt;/a&am...
    by cvalpp(匿名) on 2010/2/14 4:46:45
  • re:Last day at Microsoft
    写的真好,看了觉得很受到启发,谢谢,
    by Zheying Zheng(匿名) on 2010/2/4 10:35:20
  • re:REST API的身份验证(Authentication)
    <p>顶</p>
    by kekesoft(注册) on 2010/1/27 20:27:43
  • re:Last day at Microsoft
    祝你好运啊
    by Eric v(匿名) on 2010/1/24 1:53:34
  • re:Last day at Microsoft
    鄙人正在打算内部调动,跨过欢德福卡海峡去西雅图呢, 以后互通有无,常联系.
    by Charlie 木匠(匿名) on 2010/1/11 4:23:23
  • re:Last day at Microsoft
    Good luck, buddy.
    by Huimiao Liu(匿名) on 2010/1/10 20:18:24
  • re:Last day at Microsoft
    &lt;p&gt;Oops,you are right. 改正了。&lt;/p&gt;
    by demonfox(匿名) on 2010/1/10 18:22:13
  • re:Last day at Microsoft
    2006年8月14日起至2009年1月8日止 是2010年吧
    by q(匿名) on 2010/1/10 14:59:53
  • re:Last day at Microsoft
    &quot;永远选择你最感兴趣的项目而不是升职空间等所谓的职业发展前景&quot; -- 说的很好!Good luck!
    by CoderZh(匿名) on 2010/1/8 20:49:26
  • re:Chrome OS和Android的背后
    Google当然不是在传统的操作系统上去跟微软计算,手机操作系统,Windows CE是公认的烂,Google在这里竞争没有什么不可以。Chrome OS更多的是Google云计算战略的一部分,人家根...
    by 啊(匿名) on 2009/12/22 13:14:12

广告

More on connection pooling in a web application

Thread pooling for web connections中我曾经提到Shared Source CLI,这次再借用另一个场合宣传一下这个"Open Source .NET Framework"吧。

Topic还是web connection pooling:可以很自然地想到我们可以设计这么一个class:

class WebConnector

{

    WebRequest CreateRequest(string targetUrl)

    {

        return WebRequest.Create(targetUrl);

    }

    ...

}

这个class现在什么都没有做,因为我们的希望是.NET提供的WebRequest可以提供一定的pooling mechanism,如果真是那样,那生活真是很美好,我们什么都不要做就可以下班了。而其实这也不是什么太wild的dream,毕竟是.NET,连个pooling都不提供,也不要混了。

在深入研究一下WebRequest class之前,我们还需要考虑一个问题:web application很常见的会用到其他的web service,也就是用SOAP像其他web service请求服务。在Visual Studio中如何生成web service的proxy class等等我就不用重复重复了吧(wsdl.exe等等的)?关键在于:这个由wsdl自动生成proxy class用的是什么connection mechanism呢?虽然基本可以想到应该也是somewhere, somehow用的WebRequest,但还是让我们double check一下。找任意一份wsdl生成的proxy class的代码看看:

public partial class WSProxcyClass : System.Web.Services.Protocols.SoapHttpClientProtocol

嗯,看来基本功能都在SoapHttpClientProtocol这个类里面。

再研究一下MSDN里SoapHttpClientProtocol的API,aha,有这么一个member function:

protected override WebRequest GetWebRequest(Uri uri)

看来所有SoapHttpClientProtocol里面用到的WebRequest都是通过这个函数来取得的。注意一下前缀的override关键字,很好很好,这说明如果我们对WebRequest现有的pooling mechanism不满意的话(或者WebRequest根本没有提供pooling机制),我们可以从SoapHttpClientProtocol派生一个类,然后改写掉GetWebRequest来实现我们满意的pooling机制。其他的接口都还是用SoapHttpClientProtocol,而WSProxyClass只要继承新的派生类就可以了。

 

嗯,很好,至少我们有了backup plan,虽然麻烦了一点点,但也算不错,感谢SoapHttpClientProtocol良好的界面。

现在该进一步了,我们来研究一下WebRequest到底有没有提供pooling吧。

(题外话:可以看到.NET源代码的朋友可以研究一下SoapHttpClientProtocol的实现,这里是一个class hierarchy:

WebClientProtocol

-> HttpWebClientProtocol

    -> SoapHttpClientProtocol :
你会发现,GetWebRequest其实一直延伸到最基类的WebClientProtocol里,有不少messy stuff to deal with,所以真的要改写GetWebRequest并非那么容易。不过,anyway, that's a side note to this article, we are heading for better stuff, baby.

这下该用到,搜一下webrequest.cs,嗯嗯,time to read the source code:

private static WebRequest Create(Uri requestUri, bool useUriBase) {

    string LookupUri;
    WebRequestPrefixElement Current = null;
    bool Found = false;

    if (!useUriBase) {
        LookupUri = requestUri.AbsoluteUri;
    }
    else { 
        // schemes are registered as <schemeName>":", so add the separator
        // to the string returned from the Uri object

        LookupUri = requestUri.Scheme + ':';
    }

    int LookupLength = LookupUri.Length;

    // Copy the prefix list so that if it is updated it will
    // not affect us on this thread.

    ArrayList prefixList = PrefixList;

    // Look for the longest matching prefix.

    // Walk down the list of prefixes. The prefixes are kept longest
    // first. When we find a prefix that is shorter or the same size
    // as this Uri, we'll do a compare to see if they match. If they
    // do we'll break out of the loop and call the creator.

    for (int i = 0; i < prefixList.Count; i++) {
        Current = (WebRequestPrefixElement)prefixList[i]; 


        // See if this prefix is short enough.
        if (LookupLength >= Current.Prefix.Length) { 
            // It is. See if these match.
            if (String.Compare(Current.Prefix, 0, LookupUri, 0, Current.Prefix.Length, StringComparison.OrdinalIgnoreCase ) == 0) {

                // These match. Remember that we found it and break
                // out.
                Found = true;
                break;
            }
        }
    }

    WebRequest webRequest = null;

    if (Found) {

        // We found a match, so just call the creator and return what it
        // does.

        webRequest = Current.Creator.Create(requestUri);
        return webRequest;
    } 


    // Otherwise no match, throw an exception.
    throw new NotSupportedException(SR.GetString(SR.net_unknown_prefix));
}

 

WebRequest.Create就是最后的生成一个个实际的request object的地方。如果有pooling mechanism,在这里也该看出些端倪来了。

嗯,这段代码实在太有意思了。看懂了么?WebRequest有一个url prefixlist这样的列表,对于每个注册过的url prefix,都有一个对应的Creator来create相应的webrequest。可以想象的,一定还有一个" * "这样的prefix来匹配所有没有注册的prefix。

而匹配prefix的过程更是有趣,如果你仔细研究并理解我用红色highlight的comment的话,一定可以看出为什么了。不过这个不是我的focus,我就不分析了。

重要的是,我们知道有Creator这么一个东西,而且用户可以注册不同的url用不同的Creator,比如对于连接到www.microsoft.com的webrequest有一种要求,对于连接到www.amazon.com的webrequest有另一种要求,这些要求都可以customize。

^_^ 很好很好,我们已经很接近了。现在只有最后两个问题了,怎么customize webrequest? 以及,可以customize到什么程度?

嗯,第二个问题很好,我猜,答案是。没准,嗯,没准可以customize的程度很高?高到可以让我们指定pooling的功能!!

现在所有的线索都串起来了,我们知道有那么一个Creator,特别是那个匹配" * " (即所有)url的Creator,它应该可以是customize的,而且很可能允许我们指定pooling的一些参数。

那好,现在只有最后一个问题了:怎么customize?

难不成要我们自己派生几个Creator的子类,然后改写一下Create函数??

太复杂了吧。藏的这么深,谁知道这一把怎么玩的。。。

嗯,找找线索。

而线索也只有一条了:Creator的type!

Current = (WebRequestPrefixElement)prefixList[i];

webRequest = Current.Creator.Create(requestUri);

Bingo! WebRequestPrefixElement!

Hmm, element, element, element... 想到了什么?web.config里的configuration element么!

web.config -- we are almost done, baby.

让我们查一下msdn里system.net configuration element的文档(因为webrequest类在system.net里),有4个child element:

<authenticationModules> Specifies the modules used to authenticate Internet requests.
<connectionManagement> Specifies the maximum number of connections to Internet hosts.
<defaultProxy> Specifies the proxy server used for HTTP requests to the Internet.
<webRequestModules> Specifies the modules used to request information from Internet hosts.

找到了,从connectionManagement里你可以看到如何设置maxConnection #,然后最低下有指向ServicePoint和ServicePointManager的连接,那里,有如何设置pooling的介绍。

 

Well, quite an adventure, isn't it?  I mean, it really doesn't have to take that much if you know system.net.connectionManagement element from the every beginning. I just found this process of following the cues, digging around, pondering about things, etc is quite some fun and definitely worth the time, : )

At least, for me, it is.

Hope you enjoyed it.

========================================================

升级后没有了code highlighter真不方便啊,code snippet在我的主页上看好好的,在博客堂的主页上看就很乱。

posted on 2006-11-29 12:10:00 by demonfox  评论(2) 阅读(6363)

Bug in IE7?

下载了一个网页,存在本地硬盘上,文件名为:How to send a client certificate by using the HttpWebRequest and HttpWebResponse classes in Microsoft Visual C# _NET.mht

后来想打开看一下,IE7却出现这样的错误:

找了一下原因,原来是“C#”中的“#”造成了问题。应该是IE7在处理mht文件的时候把这个#和html中的anchor混起来了。

在IE6也测试了一下,似乎对这个问题免疫。

Hmm, maybe I should submit a bug to the IE team.

Update: This problem is fixed in Vista RTM. It's a problem on XP or earlier version of Vista (such as Beta 2). There may be a fix for this issue on XP platform in the future, but I am not sure.

posted on 2006-11-15 07:47:00 by demonfox  评论(10) 阅读(5685)

A Great Free eBook

Building Secure ASP.NET Application

PDF格式,contains pretty much very thing you need to know about securing your ASP.NET web app.

 

I will also recommend actually reading the web-based version since it may be more up-to-date:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp

posted on 2006-11-11 06:17:00 by demonfox  评论(3) 阅读(5349)

Thread pooling for web connections

 

很不好意思这么长时间都没有文章了,实在是因为觉得最近接触的很多内容不会有很多人感兴趣,举个例子: mutual SSL authentication with ASP.NET,会有人感兴趣么?

现在写的内容也觉得是marginally appealing,不管了,好歹也写一点。

[Main Reference: http://msdn.microsoft.com/msdnmag/issues/04/12/NETMatters/]

那么这次要说的是在client app中使用HttpWebRequest向server发送服务请求。在.NET 1.x中,如果你并发的HttpWebRequest数量太多的话,你会发现.NET开始抛这样一个exception: There were not enough free threads in the ThreadPool object to complete the operation. 这是什么原因呢?

答案总是在源代码里找。如果你看一下Shared Source CLI里的实现(顺便说一句,很推荐大家去下载一封SSCLI,.NET不是开源的,但SSCLI中的很多实现都忠实地体现了.NET中很多设计的概念和方法),会发现,其实在1.x的.NET framework中,HttpWebRequest其实是Asynchronous的:

public override WebResponse GetResponse()

{
  ....
  IAsyncResult asyncResult = BeginGetResponse(null, null);
  ....
  return EndGetResponse(asyncResult);
}

 

而BeginGetResponse其实是往Thread pool里queue一个work item (用过thread pool的朋友应该很熟悉QueueUserWorkItem这个API)。而这为什么会引起异常呢?

死锁 (deadlock)。

我们假设Thread Pool里只有一个thread,当你第一次调用GetResponse的时候,用掉了Thread pool里唯一的那个thread,GetResponse调用BeginGetResponse queue了一个work item来发送请求,然后再调用EndGetResponse来等待结果返回。不过很不幸,BeginGetResponse永远被执行了,因为唯一的thread已经被GetResponse用掉,而它(EndGetResponse))正在等待BeginGetResponse的返回,deadlock。

那么在现实情况中,thread pool显然不可能只有一个thread。然而,这种情况还是会发生的,比如thread pool有10个thread,当你连续queue 10个work item进去的时候,一样发生死锁。

在1.x中,.NET的权宜之计就是抛异常。在BeginGetResponse的最后,就在queue user work item之前,程序会检查System.Net.Connection.IsThreadPoolLow,如果是true,就跑出InvalidOpeartionException异常。

但如果你看一下上面SSCLI里这部分的实现,你会发现GetResponse里根本没有用到BeginGetResponse/EndGetResponse (呵呵,其实当时我也是一开始就开始看.NET的source code,结果发现根本没有什么BeginGetResponse/EndGetResponse,于是怀疑原文作者是不是在乱扯,后来看到文章的后面才明白),因为在.NET 2.0中,GetResponse已经是真正的使用synchronous call了,于是死锁的问题也不存在了。

那么在1.x的.NET中我们怎么人为干预以防止死锁的发生呢。

最显然的答案自然是使用semaphore(关于semaphore的概念就不讲了吧,大学operating system课程的这个是必修内容)。

但更可怜的是,.NET 1.x居然连semaphore也没有...... (汗,我看到这里才意识到很久以前读Jeff Prosise的《Programming Microsoft .NET Core Reference》的时候居然没有发现Multithreading一章里确实没有提到semaphore一个字)。

那么只能自己wrap一下win32的API了:

 

public sealed class Semaphore : WaitHandle
{
    public Semaphore() : this(1, 1) {}

    public Semaphore(int initialCount, int maximumCount)
    {
        if (initialCount < 0 || initialCount > maximumCount)
            throw new ArgumentOutOfRangeException("initialCount");
        if (maximumCount < 1)
            throw new ArgumentOutOfRangeException("maximumCount");
        IntPtr h = CreateSemaphore(
            IntPtr.Zero, initialCount, maximumCount, null);
        if (h == WaitHandle.InvalidHandle || h == IntPtr.Zero)
            throw new Win32Exception();
        Handle = h;
    }

    public void ReleaseOne()
    {
        int previousCount;
        if (!ReleaseSemaphore(Handle, 1, out previousCount))
            throw new Win32Exception();
    }

    [DllImport("kernel32.dll", SetLastError=true)]
    private static extern IntPtr CreateSemaphore(
        IntPtr lpSemaphoreAttributes, int lInitialCount,
        int lMaximumCount, string lpName);

    [DllImport("kernel32.dll", SetLastError=true)]
    private static extern bool ReleaseSemaphore(
        IntPtr hSemaphore, int lReleaseCount, out int lpPreviousCount);
}

 

怎样用Semaphore来控制并发线程数的代码就不贴了吧,经典的producer/consumer问题,或者你参考一下原文里的code snippet也行。

===============================================================

我的一些comments:

web connection pooling对于任何一个大型的web app都几乎是必须的,那么在2.0中,.NET是如何控制同步并发线程数的呢?对于web connection,还有没有其他可以配置的变量?

答案是有的,有兴趣的朋友可以看一下:ServicePoint, ServicePointManager, 和 system.net configuration element. 也许我下次再关于这个内容写点介绍.

 

在一个要求high security-concern, high flexibility的web应用中,以下的几条是我一直牢记的:

1. Sacrifice performance for design simplicity.

2. Sacrifice functionality for performace.

3. If a functionality is really desired and a must-have, at least provide a fallback so that user can get their result in a not intuitive but fast fashion.

复杂的design容易出错,更容易有安全漏洞,当你自以为自己的security mechanism坚不可摧的时候,无数hacker已经开始蠢蠢欲动。什么是最好的security? 我看如果能只用到public key cryptography来加密所有敏感数据才是最安全的(ok, I am over-simplifying here, but I hope you get my point)。

关于Performance,every extra second your service takes, you lose 10% more users. 我情愿先有一个简陋但可用且快速的系统,然后出alpha, beta测试,而不是上来就设计一堆caching mechanism, xslt translation, throttling,blahblah, 看上去很美,其实慢到无法忍受。后来再要修改臃肿的系统,代价就很大。

posted on 2006-11-10 19:52:00 by demonfox  评论(7) 阅读(6576)

Powered by: Joycode.MVC引擎 0.5.2.0