[Linq]LINQ2Douban Demo Code(豆瓣Linq实例)

mikel阅读(838)

转载:http://www.cnblogs.com/chwkai/archive/2009/12/31/1636873.html

LINQToolKit是基于.Net的Web API调用框架。通过定义的XML Mapping,可以实现XML和单个实体对象和关系之间的映射,并可以在运行时对需要访问的属性进行延迟加载。LINQToolKit封装了LINQ style的查询和数据处理方式,基于LINQToolKit你可以用from/where/select来直接进行查询,这些关键字的语义可以通过定义 的QueryRule来生成相应的查询uri(用于invoke web api),并且可在上面进行任何Enumerable和Querable允许的的数据操作。

LINQToolKit可以通过扩展支持任何规范的web api,自带了Google Data API的支持

LINQ2Douban是基于LINQToolKit的douban网的api的全功能调用框架。通过LINQ2Douban,你可以用LINQ-Style的方式对douban的数据进行CRUD操作。

声明:LINQ2Douban不是官方框架,K.K只是douban的忠实用户

相关信息:

douban api讨论组:http://www.douban.com/group/dbapi/

google data protocol :http://code.google.com/apis/gdata/docs/2.0/reference.html

你可以通过google code获取LINQToolKit和LINQ2Douban的所有代码和Example

http://code.google.com/p/linqtodouban/

demo code源码下载:http://linqtodouban.googlecode.com/files/LINQ2Douban%20Demo.rar

所有疑问和错误请和K.K邮件联系chwkai@gmail.com

关于LINQToolKit稍后会有文档介绍

 

在运行Demo Code之前,请先修改context.config文件中的access token,如何获取access token请访问douban api组

1 <request type="LINQToolKit.Douban.DoubanRequest, LINQToolKit.Douban">
2     <!--
3     <apiKey></apiKey>
4     <apiKeySecret></apiKeySecret>
5     <accessToken></accessToken>
6     <accessTokenSecret></accessTokenSecret>
7     -->
8   </request>

Demo Code:

001 /// <summary>
002     /// Demos for LINQToolKit.Douban
003     /// </summary>
004     /// <remarks>
005     /// This framework is under the GNU licence and Creative Commons 3.0.
006     /// You can also get a copy of the source code from google code
007     /// <see cref="http://linqtodouban.googlecode.com/svn/trunk/"/>.
008     /// For more information and further support, please contace with kevin (chwkai@gmail.com)
009     /// </remarks>
010     class Program
011     {
012         static void Main(string[] args)
013         {
014             var context = new DoubanContext();
015             // 用于输出查询的url
016             context.Log = Console.Out;
017             context.Me.Title.Dump();
018  
019             // 回复广播(需要授权)
020             // context.CommentMiniBlog("miniBlogId", "content");
021             // 参与指定活动(需要授权)
022             //context.ParticipateEvent("eventId");
023             // 对活动感兴趣(需要授权)
024             // context.FollowEvent("eventId");
025             // 退出活动(需要授权)
026             // context.QuitEvent("eventId");
027             // 删除活动(需要授权)
028             // context.DeleteEvent("eventId", "reason");
029             // 回复推荐
030             // context.CommentOnReco("recommendId", "content");
031             // 删除推荐的回复
032             // context.DeleteCommentOnReco("recommendId", commentId);
033             // 批量设置豆油已读
034             // context.SetMailsRead(string[] mails);
035             // 批量删除豆油
036             // context.DeleteMails(string[] mails);
037             // 验证access token是否可用
038             // context.IsTokenAvailabe(token);
039             // 注销token
040             // context.CancelToken(token);
041         }
042  
043         /// <summary>
044         /// 获取用户豆油(需要授权,只能看Me的)
045         /// </summary>
046         /// <param name="context"></param>
047         private static void GetUserMails(DoubanContext context)
048         {
049             // 未读邮件
050             // context.Me.UnReadMails;
051             // 已发邮件
052             // context.Me.OutboxMails;
053             foreach (var item in context.Me.Mails)
054             {
055                 item.Title.Dump();
056             }
057         }
058  
059         /// <summary>
060         /// 获取用户所有推荐
061         /// </summary>
062         /// <param name="context"></param>
063         private static void GetUserRecos(DoubanContext context)
064         {
065             foreach (var item in context.Me.Recommends)
066             {
067                 item.Title.Dump();
068  
069                 //获取推荐回复
070                 foreach (var cc in item.Comments)
071                 {
072                     cc.Title.Dump();
073                 }
074             }
075         }
076  
077         /// <summary>
078         /// 获取指定城市id的活动
079         /// </summary>
080         /// <param name="context"></param>
081         private static void GetLocationEvents(DoubanContext context)
082         {
083             var events =
084                 (from e in context.Events
085                  where e.Location.ID == "beijing"
086                  select e).Take(10);
087  
088             foreach (var item in events)
089             {
090                 item.Title.Dump();
091             }
092         }
093  
094         /// <summary>
095         /// 获取指定用户所有活动
096         /// </summary>
097         /// <param name="context"></param>
098         private static void GetUserEvents(DoubanContext context)
099         {
100             // 用户参与的活动
101             //context.Me.ParticipateEvents
102             // 用户感兴趣的活动
103             //context.Me.WishEvents
104             // 用户发起的活动
105             //context.Me.InitiateEvents
106             foreach (var item in context.Me.Events)
107             {
108                 item.Title.Dump();
109             }
110         }
111  
112         /// <summary>
113         /// 获取用户所有日记(延迟加载)
114         /// </summary>
115         /// <param name="context"></param>
116         private static void GetUserNotes(DoubanContext context)
117         {
118             foreach (var item in context.Me.Notes)
119             {
120                 item.Title.Dump();
121             }
122         }
123  
124         /// <summary>
125         /// 获取指定用户广播
126         /// </summary>
127         /// <param name="context"></param>
128         private static void GetUserMiniBlogs(DoubanContext context)
129         {
130             var user = context.Peoples.GetByID("ahbei");
131  
132             foreach (var item in user.MiniBlogs)
133             {
134                 item.Title.Dump();
135  
136                 // 获取广播回复
137                 foreach (var c in item.Comments)
138                 {
139                     c.Title.Dump();
140                 }
141             }
142         }
143  
144         /// <summary>
145         /// 获取友邻广播
146         /// </summary>
147         /// <param name="context"></param>
148         private static void GetContactMiniBlogs(DoubanContext context)
149         {
150             foreach (var item in context.Me.ContactMiniBlogs)
151             {
152                 item.Title.Dump();
153             }
154         }
155  
156         /// <summary>
157         /// 获取指定用户收藏
158         /// </summary>
159         /// <remarks>
160         /// 此处以Me为例,也可以换成查询得来的People。当访问这些属性时,会自动加载数据
161         /// </remarks>
162         /// <param name="context"></param>
163         private static void GetPeopleCollections(DoubanContext context)
164         {
165             // 获取音乐收藏
166             // context.Me.Musics
167             // 获取电影收藏
168             // context.Me.Movies
169             // 获取书籍搜藏
170             foreach (var item in context.Me.Books)
171             {
172                 item.Title.Dump();
173             }
174         }
175  
176         /// <summary>
177         /// 删除指定id的评论
178         /// </summary>
179         /// <param name="context"></param>
180         private static void DeleteReview(DoubanContext context)
181         {
182             // 代码中的id更换为有效id
183             var review = context.Reviews.GetByID("2902263");
184             context.Reviews.Delete(review.ID);
185         }
186  
187         /// <summary>
188         /// 更新指定的id的评论
189         /// </summary>
190         /// <param name="context"></param>
191         private static void UpdateReview(DoubanContext context)
192         {
193             // 代码中的id更换为有效id
194             var review = context.Reviews.GetByID("2902263");
195             review.Content = "Udpat content test for linq2douban";
196             context.Reviews.Update(review.ID, review);
197         }
198  
199         /// <summary>
200         /// 发表评论
201         /// </summary>
202         /// <remarks>
203         /// 发表短的Content内容,douban api会报失败
204         /// </remarks>
205         /// <param name="context"></param>
206         private static void AddReview(DoubanContext context)
207         {
208             var review = new Review
209             {
210                 // 如果Subject是从API读回来的,Source属性会自动赋值
211                 Subject = new Movie { Source = "http://api.douban.com/movie/subject/1424406" },
212                 Rating = new Rating { Value = 4 },
213                 Content = "this is the test for linq2doubanddddddddddddddddddddddddddddddddddddddddd",
214                 Title = "this is the test for linq2douban"
215             };
216  
217             context.Reviews.Insert(review);
218         }
219  
220         /// <summary>
221         /// 获取指定书籍/电影/音乐的评论
222         /// </summary>
223         /// <param name="context"></param>
224         private static void GetSubjectReviews(DoubanContext context)
225         {
226             var movie = context.Movies.GetByID("1424406");
227             movie.Reviews.Count().Dump();
228         }
229  
230         /// <summary>
231         /// 获取用户所有评论
232         /// </summary>
233         /// <remarks>
234         /// 此例用me做示例,获取指定的用户的评论,访问People.Reviews属性即可,框架会延迟加载
235         /// </remarks>
236         /// <param name="context"></param>
237         private static void GetReviews(DoubanContext context)
238         {
239             foreach (var item in context.Me.Reviews)
240             {
241                 item.Title.Dump();
242             }
243         }
244  
245         /// <summary>
246         /// 示例复合查询(关键字,startindex,maxtresult)
247         /// </summary>
248         /// <remarks>
249         /// douban的startindex, maxresult的含义与常识理解不通,
250         /// douban的maxresult表示取回数据的最大下标,所以Take(20).Skip(10)返回下标为10-20共11条数据
251         /// </remarks>
252         /// <param name="context"></param>
253         private static void GetBooks(DoubanContext context)
254         {
255             var query = context.Books.Has("新东方").Take(20).Skip(10);
256  
257             foreach (var item in query)
258             {
259                 item.Title.Dump();
260             }
261         }
262  
263         /// <summary>
264         /// 用多个条件查询书籍(电影、音乐等,查询同此例)
265         /// </summary>
266         /// <remarks>
267         /// LINQToolKit会根据queries.config里定义的query rules转换成相应的url进行查询,
268         /// 你可尝试用不同关键字匹配,如果有问题请发chwkai@gmail.com
269         /// </remarks>
270         /// <param name="context"></param>
271         private static void GetBooksByQuery(DoubanContext context)
272         {
273             var query =
274                 from b in context.Books
275                 where b.LongIsbn == "9787543639133" || b.ID == "2023013"
276                 select b;
277  
278             foreach (var item in query)
279             {
280                 item.Title.Dump();
281             }
282         }
283  
284         /// <summary>
285         /// 获取指定ID的书籍
286         /// </summary>
287         /// <remarks>
288         /// 获取指定ID的douban信息与此例相同
289         /// </remarks>
290         /// <param name="context"></param>
291         private static void GetBookByID(DoubanContext context)
292         {
293             var query =
294                 from b in context.Books
295                 where b.ID == "2023013"
296                 select b;
297             query.Single().Dump();
298         }
299  
300         /// <summary>
301         /// 获取用户关注的人(延迟加载)
302         /// </summary>
303         /// <param name="context"></param>
304         private static void GetContacts(DoubanContext context)
305         {
306             // context.Me.Contacts 获取当前用户关注的人
307  
308             var query =
309                 from p in context.Peoples
310                 where p.ID == "chwkai"
311                 select p;
312  
313             // People.Contacts 当需要访问时会延迟加载
314             query.Single().Contacts.Count().Dump();
315         }
316  
317         /// <summary>
318         /// 获取用户朋友(延迟加载)
319         /// </summary>
320         /// <param name="context"></param>
321         private static void GetFriends(DoubanContext context)
322         {
323             // context.Me.Friends 获取当前用户的朋友
324  
325             var query =
326                 from p in context.Peoples
327                 where p.ID == "chwkai"
328                 select p;
329  
330             // People.Friends 当需要访问时会延迟加载
331             query.Single().Friends.Count().Dump();
332         }
333  
334         /// <summary>
335         /// 关键字搜索用户
336         /// </summary>
337         /// <remarks>
338         /// 不是ID的相等比较条件都会转换为关键字查询
339         /// </remarks>
340         /// <param name="context"></param>
341         private static void GetUserByKey2(DoubanContext context)
342         {
343             var query =
344                 from p in context.Peoples
345                 where p.Title == "net"
346                 select p;
347             query.Count().Dump();
348         }
349  
350         /// <summary>
351         /// 关键字搜索用户
352         /// </summary>
353         /// <param name="context"></param>
354         private static void GetUserByKey(DoubanContext context)
355         {
356             var query = context.Peoples.Has("net");
357             query.Count().Dump();
358         }
359  
360         /// <summary>
361         /// 获取当前用户(需要授权)
362         /// </summary>
363         /// <param name="context"></param>
364         private static void GetCurrentUser(DoubanContext context)
365         {
366             context.Me.Content.Dump();
367         }
368  
369         /// <summary>
370         /// 获取单个用户信息
371         /// </summary>
372         /// <param name="context"></param>
373         private static void GetOneUserLinqStyle(DoubanContext context)
374         {
375             var user =
376                 from u in context.Peoples
377                 where u.ID == "chwkai"
378                 select u;
379             user.Single().Content.Dump();
380         }
381  
382         /// <summary>
383         /// 获取单个用户信息
384         /// </summary>
385         /// <param name="context"></param>
386         private static void GetOneUser(DoubanContext context)
387         {
388             var user = context.Peoples.GetByID("chwkai");
389             user.Content.Dump();
390         }

[MVC] asp.net mvc中“Action”的创建

mikel阅读(1144)

转载:http://www.cnblogs.com/niuchenglei/archive/2009/12/29/1635482.html

在上一篇《ControllerActionInvoker——Action的导火索》中,我介绍了ControllerAcionInvoker类,那么接下来就到了Action的创建了,继续我们的ASP.NET mvc源代码之旅。

内容概览Top

本篇主要探讨“Action”的创建过程,为什么要加引号呢?因为我们创建的不是真正的Action,方法是没法创建的,它是指 ActionDescriptor对象,是对Action方法描述的一个对象,在mvc中,方法的调用是利用反射来实现的。下面我们就具体讨论一下这个过 程。

为什么要创建Action?Top

在一个请求到达时,必然最终会由一个Action去执行,那么这个Action是怎么执行的呢?答案是利用反射得到 Action的描述,然后再调用Action的。为什么要这么大费周折呢?因为在Action上还有好多Filter,我们要在执行的时候考虑到AOP的 影响,并把二者无缝的结合起来。所以在执行Action上,我们要得到一个ActionDescriptor对象,这个对象用以描述Action方法的一 些特性。

ControllerDescriptor与ActionDescriptorTop

ControllerDescriptor是描述Controller的类,ActionDescriptor是描述Action的类,而Action是 Controller的方法,那么在ControllerDescriptor和ActionControllerDescriptor两者之间就必然存 在着某种关联,下面我们看看到底他们是一种什么关系:

在ControllerActionInvoker类中,我们发现了两个类直接的一次协作,代码是这样的:

ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);

我们看到,ActionDescriptor是调用ControllerDescriptor的FindAction方法得到的。我们猜想它们可能是一对 多关系,一个ControllerDescriptor对应多个ActionDescriptor,下面就一步一步来验证我们的猜想,首先我们先从 ReflectedControllerDescriptor类入手,因为这个类是ControllerDescriptor类的惟一继承者。

ReflectedControllerDescriptor类有几个比较重要的字段:

private ActionDescriptor[] _canonicalActionsCache;
private readonly Type _controllerType;
private readonly ActionMethodSelector _selector;

从上面我们看到,ReflectedControllerDescriptor类有一个 ActionDescriptor[]类型的字段,这就证明了我们的猜想是正确的。还有一个ActionMethodSelector类型的字段,这个类 我们暂且不去管它。接着,我发现ReflectedControllerDescriptor类的构造函数接受一个Type类型的参数,这个Type就是 Controller的类型,然后new ActionMethodSelector(Type)一个ActionMethodSelector类型的对象,把它赋值给_selector字段。然 后,我们回到FindAction方法上,ControllerDescriptor就是通过这个方法得到ActionDescriptor对象的。该方 法代码如下:

public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) {
    if (controllerContext == null) {
        throw new ArgumentNullException("controllerContext");
    }
    if (String.IsNullOrEmpty(actionName)) {
        throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
    }
    MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName);
    if (matched == null) {
        return null;
    }
    return new ReflectedActionDescriptor(matched, actionName, this);
}

从上面我们看到,首先利用ActionMethodSelector获得MethodInfo对象,然后把它作为参数new ReflectedActionDescriptor对象并返回。

总结一下,ControllerDescriptor通过ActionMethodSelector得到ActionDescriptor对 象,ReflectedActionDescriptor对象的构造操作只需要一个MethodInfo对象。具体的 ActionMethodSelector类的机制下面介绍。

ActionMethodSelector是什么?Top

从上一节中我们了解到,ActonMethodSelector是一个工具,是被ControllerDescriptor利用来获取 ActionDescriptor对象的工具。那么我们就有必要来了解一下这个工具了。从上一节中,我们得知FindActionMethod是一个突破 口,但是这次我们要先从构造函数入手,因为这个类在构造函数里面完成了一些初始化的操作,而这些初始化的操作是非常重要的。下面是它的构造函数:

//构造函数
public ActionMethodSelector(Type controllerType) {
    ControllerType = controllerType;
    PopulateLookupTables();
}
//构造函数调用的方法,用来获取属于某个Controller的所有Action方法的MethodInfo对象
private void PopulateLookupTables() {
    MethodInfo[] allMethods = ControllerType.GetMethods(
                BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
    MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod);
    AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute);
    NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(
                method => method.Name, StringComparer.OrdinalIgnoreCase);
}
//判断MethodInfo是否为合法的Action
private static bool IsValidActionMethod(MethodInfo methodInfo) {
    return !(methodInfo.IsSpecialName ||
            methodInfo.GetBaseDefinition().DeclaringType.IsAssignableFrom(typeof(Controller)));
}
//判断MethodInfo是否为被ActionNameSelectorAttribute所修饰
private static bool IsMethodDecoratedWithAliasingAttribute(MethodInfo methodInfo) {
    return methodInfo.IsDefined(typeof(ActionNameSelectorAttribute), true /* inherit */);
}

在上面的代码中,初始化的操作做了一些工作,这些工作是获取一个Controller的合法的所有Action,并存放在ActionMethodSelector的两个字段中,下面是这两个字段的定义:

public MethodInfo[] AliasedMethods
public ILookup<string, MethodInfo> NonAliasedMethods

这两个属性是存放Action方法对应MethodInfo的,AliasedMethods存放那些使用 ActionNameSelectorAttribute属性标注的Action,也就是我们告诉mvc这是一个Action。 NonAliasedMethods用来存放我们没有明确指出这是一个Action,但的确它是一个Action的Action方法。

ActionMethodSelector的初始化操作已经完成了,下面我们从FindActionMethod方法入手,继续探究是如何获取一个Action的MethodInfo的。下面是该方法的代码:

public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) {
    List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName);
    methodsMatchingName.AddRange(NonAliasedMethods[actionName]);
    List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName);
    switch (finalMethods.Count) {
        //匹配到0个
        case 0:
            return null;
        //匹配到1个
        case 1:
            return finalMethods[0];
        //匹配到多个,抛出异常
        default:
            throw CreateAmbiguousMatchException(finalMethods, actionName);
    }
}

在上面的一段代码中,还涉及到了GetMatchAliasedMethods、RunSelectionFilters方法,这两个方法的代码就不再做详细分析了,相信大家看看mvc的源代码就很容易明白了。步骤是这样的:

  • 该类在初始化操作中已经把“明确标注为Action”和“没有明确标注为Action”的所有Action填充到了自己的两个字段中。即我们通常使用的[ActionName("name")]特性。
  • 建立一个最终查询的列表,并把“明确标注为Action”的所有Action都加入其中。
  • 从“没有明确标注为Action”(即不使用ActionName进行标注)的Action列表中,即该类的NonAliasedMethods属性中找到名为action参数的一个MethodInfo,并把它加入到最终查询列表。
  • 从最终查询列表中查找名为action参数的MethodInfo,如果匹配0个表示没有该Action,匹配1个就正确,匹配多个就抛出异常。

好了,到此我好了,到此我们便获取到了一个Action的MethodInfo,即反射信息。下一步就是利用这个MethodInfo构建一个描述Action的ReflectedActionDescriptor对象了,这点我们在上一节已经说过了,的确很简单。

获取全部合法的ActionTop

在ControllerControllerDescriptor类中,我们发现有一个 GetCanonicalActions的抽象方法。我们转到ReflectedActionDescriptor类,看看这个方法的实现。它的返回值是 ActionDescriptor[]类型的,它返回一个Controller所有的合法的Action。下面我们具体分析一下,它与 FindActionMethod有什么不同。下面是该方法的代码:

public override ActionDescriptor[] GetCanonicalActions() {
    ActionDescriptor[] actions = LazilyFetchCanonicalActionsCollection();
    // need to clone array so that user modifications aren't accidentally stored
    return (ActionDescriptor[])actions.Clone();
}
private ActionDescriptor[] LazilyFetchCanonicalActionsCollection() {
    return DescriptorUtil.LazilyFetchOrCreateDescriptors<MethodInfo, ActionDescriptor>(
        /* cacheLocation */
        ref _canonicalActionsCache ,
        /* initializer */
        GetAllActionMethodsFromSelector ,
        /* converter */
        methodInfo => ReflectedActionDescriptor.TryCreateDescriptor(methodInfo,
                                                                    methodInfo.Name,
                                                                    this));
}

在上面的代码中,我们看到GetCanonicalActions方法需要一个DescriptorUtil类来辅助得到 ActionDescriptor[]类型列表。这个类的LazilyFetchOrCreateDescriptors方法需要三个参数,他们分别是 /*cacheLocation*/就是ControllerDescriptor存放ActionDescriptor的列表, /*initializer*/获取所有Action的一个委托,/*converter*/一个把MethodInfo对象包装成 ActionDescriptor对象的委托。第一个参数值为ControllerDescriptor类的一个字段,第二个参数值为一个方法的委托,下 面为这个委托:

private MethodInfo[] GetAllActionMethodsFromSelector() {
    List<MethodInfo> allValidMethods = new List<MethodInfo>();
    allValidMethods.AddRange(_selector.AliasedMethods);
    allValidMethods.AddRange(_selector.NonAliasedMethods.SelectMany(g => g));
    return allValidMethods.ToArray();
}

我们看到这个方法就是要把Controller的所有的Action都返回。第三个参数是一个lambda形式的表 达式,它直接调用ReflectedActionDescriptor的方法TryCreateDescriptor来包装MethodInfo对象成一 个ActionDescriptor对象。接下来我们看看DescriptorUtil类是如何设计的,这是一个静态类,只有一个静态的泛型方法:

public static TDescriptor[] LazilyFetchOrCreateDescriptors<TReflection, TDescriptor>(
                                            ref TDescriptor[] cacheLocation,
                                            Func<TReflection[]> initializer,
                                            Func<TReflection, TDescriptor> converter) {
    // did we already calculate this once?
    TDescriptor[] existingCache = Interlocked.CompareExchange(ref cacheLocation, null, null);
    if (existingCache != null) {
        return existingCache;
    }
    TReflection[] memberInfos = initializer();
    TDescriptor[] descriptors = memberInfos.Select(converter).Where(
                            descriptor => descriptor != null).ToArray();
    TDescriptor[] updatedCache = Interlocked.CompareExchange(ref cacheLocation,descriptors,null);
    return updatedCache ?? descriptors;
}

这段代码看起来很吓人,因为它不但使用了泛型方法,还使用了linq查询,已经lambda表达式。其实并没有那么 复杂,他要先判断传入的ControllerDescriptor的ActionDescriptor列表是否为空,是则把原来列表中相同的部分删掉一 个,然后返回(为什么会有相同的两个元素在节点中呢?请看下面的小注)。如果为空,则获取所有合法Action,然后把两个相同中的一个删除,然后返回 (怎么又出现两个元素相同的现象呢?请看下面的小注)。看,并不是那么的难理解,泛型只是对类型抽象了一下而已,而lambda表达式只不过是传递一个方 法,不要畏惧这些小把戏,有了这些小把戏编程才更有趣。

为什么会出现有两个相同元素相同的情况?还记得ActionMethodSelector的两个属性 吗,AliasedMethods和NonAliasedMethods,我们使用[ActionName("actionname")]标注的 Action是“非常合法”的Action,这些Action将被划入AliasedMethods行列,我们没有使用这一特性标注的Action是“合 法”的Action,这些Action将被划入NonAliasedMethods行列。如果我们有如下代码的两个Action时会出现什么情况呢?很明 显,在获取Controller的全部Action时这两个就都获取到了,就会有两个Action相同。

public ActionResult Index(){……}
[ActionName("Index")]
public ActionResult Default(){……}

总结Top

在这一篇中,我们围绕ActionDescriptor讨论了一些,讨论了ActionDescriptor的创 建,与ControllerDescriptor的关系,以及怎么去获取相应的Action等,其实这个过程十分的简单,概括一句话就是 ControllerDescriptor利用ActionMethod获取到ActionDescriptor,而且这个过程在mvc中也很微小,但是 的确它是一个不可或缺的部分,而且是相当重要的一部分。谢谢大家的阅读,也许你对mvc源代码不是太感兴趣或对这个过程不太清楚,但我还是要告诉你,学习 mvc源代码有很多用处,只有坚持一下,一切就都会明白的。希望朋友们多批评指正,相互学习。

作者:Creason New(Creason's Blog)
出处: http://www.cnblogs.com/niuchenglei
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[MVC]超级简单:一步一步教你创建一小型的asp.net mvc 应用程序

mikel阅读(864)

z转载:http://www.cnblogs.com/zhuqil/archive/2009/12/27/1633353.html

   这本教程中将帮助你创建一个小型的ASP.NET mvc示例。

    在本教程中,我们将创建自己的 Model , View 和Controller ,让他们同心协力运行起来。在这里,使用Visual Studio创建一个新的ASP.NET MVC应用程序。本教程的目标是使用mvc原理在web页面上输出的信息。简单地理解一下我们要打算做的,然后我们将演示每个步骤来建立我们的网站。对于 我们的目标,我们需要一个model 来保存信息。该model 将是一个具具有必要属性的类。然后,我们将创建一个View,在一个表格形式中显示信息。最后,我们需要一个Controller来接受的网页请求,并对 此作出反应。那就让我们开始吧。

创建Model

在Models文件夹右击并通过Add -> Class,创建一个新类,见图:

将类命名为 PersonalInformation.cs 。在Model中添加如下的属性:

创建这些属性使用C#3.0规范,我们不需要为他们创建私有字段。这就完成了我们的Model。现在让我们来创建我们的View,我们将使用它在浏览器中显示信息。

创建View:

    在views文件夹右击,创建一个新的文件夹。命名为PersonalInformation。右击PersonalInformation 文件夹,然后选择Add -> View ,看下图:

    将View命名为Index,选择“Create a strongly-typed view”,然后再选择class ,它是作用于这个View的Model。在View data class下拉框中,你可能看不到任何视图类。编译应用程序,然后添加一个View。 你就会看到在下拉框中看到PersonalInformation类。然后为你的View选择一个母板页。一旦这些步骤完成后,单击Add 和创建的View 。请参考下图。

创建Controller:

    现在我们已经完成了Model 和View。让我们来创建我们的Controller。右击Controllers 然后选择Add -> Controller

    将Controller命名为PersonalInformation。请确认Controller 的名字和我们创建的在Views文件夹下面的名字相同。

    你将在Controller类中看到一个名字为Index的默认的方法。

    这种方法具有返回类型ActionResult,它将返回一个视图。在未来教程中,我们将深入探讨的返回类型和其的细节。现在让我们做一些编码,传递Model给View,以便它可以在网页上显示信息。在下面的代码输入到您的Controller 类中。

    此代码创建了一个Model和填充数据给它,现在剩下的最后一步,是使用的视图显示的信息。对于我们创建的开放的View,我们输入下面代码:

    就是这样。我们完成了我们的Model,View和Cintroller。现在让我们编译和运行我们的应用程序。下面是最后的输出,我们将在浏览器中看到。请注意仔细观察浏览器地址栏中的URL。

[C#]使用NVelocity自动生成Favorite收藏夹的导航页面

mikel阅读(1161)

转载:http://www.cnblogs.com/wuhuacong/archive/2009/12/28/1634447.html

你是否碰到你的收藏夹凌乱不堪,或者很多收藏网页地址虽然分类,可是使用起来却感觉不太好,有没有想过把你的收藏夹内容自动生成一个网页的导航页面生成类似Hao123(http://www.hao123.com/),或者番茄花园导航(http://www.tomatolei.com/)一样的网页,用来迅速导航到自己的网址呢?

即使使用场景有争议,不过我们也来关注一下这种自动生成页面的技术吧,说不定可以使用在合适的环境下,如下面我测试了两个类别的收藏夹 Favorite目录下,其中“搜索引擎”和“新闻网络”两个目录是我的测试收藏夹分类,Icon和Images是我构造生成主页所需要的一些内容目录。 我们根据Index.htm来生成index.htm来作为导航的主页面。

由于我们不想改变收藏夹的IE快捷方式的内容,因此收藏夹里面的内容还是以IE快捷方式来保存,如下图所示。

其中IE快捷方式的内容如下所示,文件时采用INI格式进行保存的。

[DEFAULT]
BASEURL=http://g.cn
[InternetShortcut]
URL=http://g.cn

其中的Default中的BaseURL是域名地址,InternetShortcut中的URL是具体的网页地址,基本上这些内容就可以构造成一个IE快捷方式了。

整体运行的效果如下图所示。其中内容可以动态生成,由于采用了NVelocity引擎来生成内容,速度非常快,和你直接浏览一个html页面的速度相差无几。

 

下面我们来具体介绍下如何实现这个页面的自动生成吧。

单击Home主页的时候,我们就自动生成一个页面,生成代码如下所示。

代码

        /// <summary>
        
/// 导航到主页面
        
/// </summary>
        public void GoHome()
        {
            
string tempFile = "Favorite/index.txt";
            
try
            {
                HelperClassAdapter helper 
= new HelperClassAdapter(tempFile);
                helper.Execute();
                
//MessageUtil.ShowTips("生成成功");

                
bool found = false;
                
foreach (IDockContent content in this.dockPanel.Documents)
                {
                    MainIE form 
= content as MainIE;
                    
if (form != null && form.extendedWebBrowser1.DocumentTitle == "收藏夹导航")
                    {
                        found 
= true;
                        form.Activate();
                        
break;
                    }
                }
                
if (!found)
                {
                    
this._currentUrl = "my:home";
                    AddMainIE();
                }
            }
            
catch (Exception ex)
            {
                LogHelper.Error(ex);
                MessageUtil.ShowTips(ex.Message);
            }
        }

 

这里其实主要的就是生成代码的类HelperClassAdapter,这个类通过获得模版文件,通过NeVelocity引擎把指定格式的内容附加到模版文件中,然后输出文件即可。

代码

    public class HelperClassAdapter : AbstractAdapter
    {
        
public HelperClassAdapter(string templateFile)
            : 
base(templateFile)
        {
        }
        
public override void Execute()
        {
            
string startPath = Path.Combine(Application.StartupPath, "Favorite");
            
if (!Directory.Exists(startPath))
            {
                Directory.CreateDirectory(startPath);
            }                        
            
//遍历目录和文件,根据文件模版输出到文件中

            
#region 根据目录放置的字典
            Dictionary
<string, List<FavoriteInfo>> fileDict = new Dictionary<string, List<FavoriteInfo>>();
            
string[] dirArray = Directory.GetDirectories(startPath);
            
foreach (string dir in dirArray)
            {
                
string dirName = new DirectoryInfo(dir).Name;
                
if (dirName.ToLower() == "icon" || dirName.ToLower() == "images"//系统图标跳过
                    continue;
                
string[] fileArray = Directory.GetFiles(dir);
                List
<FavoriteInfo> fileList = new List<FavoriteInfo>();
                FavoriteInfo info 
= null;
                
foreach (string file in fileArray)
                {
                    
if (!string.IsNullOrEmpty(file))
                    {
                        info 
= new FavoriteInfo();
                        info.Category 
= dirName;
                        info.Title 
= new FileInfo(file).Name.Replace(".url""");
                        INIFileUtil iniUtil 
= new INIFileUtil(file);
                        info.Url 
= iniUtil.IniReadValue("InternetShortcut""URL");
                        fileList.Add(info);
                    }
                }
                
if (!fileDict.ContainsKey(dir))
                {
                    fileDict.Add(dir, fileList);
                }
            } 
            
#endregion
            
//设置输出文件名称和页面变量,然后输出文件
            this.FileNameOfOutput = Path.Combine(Application.StartupPath, "Favorite/index.htm");
            context.Put(
"FileDict", fileDict); 
            OutputFile();
        }
    }

 

 

其中模版文件index.txt文件很简单,部分关键代码如下所示,其中主要关注有$开始的运算符和变量,这些代码是实现对象到页面内容的转换的。 其中#foreach($FavoriteList in ${FileDict.Values})是遍历里面的对象,进行循环处理的,语法也很简单。

代码

<DIV id=content>
  
<!–/ sidebar–><!–/ board–>
<div class=box id=coolsites>
      
<h2 align="center" class="style2">收藏夹导航<class="" id=cslayout title=居中对齐/左对齐>居中对齐/左对齐</a></h2>
      
<div id=list>
#foreach($FavoriteList in ${FileDict.Values})
        
<dl>
#set($count = 0)
#foreach($FavoriteInfo in $FavoriteList)
#if($count==0)
          
<dt><href="#">[ ${FavoriteInfo.Category.ToString()} ]</a>
#end
#set($count = $count + 1)
          
<dd><href="${FavoriteInfo.Url.ToString()}">${FavoriteInfo.Title.ToString()}</a>
#end
        
</dd></dl>
#end
      
</div>
    
</div>
  
</div>

 

 另外FavoriteInfo是一些基本属性的实体类,代码很简单:

代码

    public class FavoriteInfo
    {
        
private string m_category;//目录分类名称
        private string m_title;//网页标题
        private string m_url;//网页链接

        
/// <summary>
        
/// 目录分类名称
        
/// </summary>
        public string Category
        {
            
get { return m_category; }
            
set { m_category = value; }
        }
        
/// <summary>
        
/// 网页标题
        
/// </summary>
        public string Title
        {
            
get { return m_title; }
            
set { m_title = value; }
        }
        
/// <summary>
        
/// 网页链接
        
/// </summary>
        public string Url
        {
            
get { return m_url; }
            
set { m_url = value; }
        }
    }

 

 HelperClassAdapter的基类如下所示,主要是定义一些公共的属性变量及操作,方便被继承成各种类型的子类。

代码

    public abstract class AbstractAdapter : IAdapter
    {
        
#region 保护成员变量
        
protected VelocityContext context;
        
protected Template template;
        
protected string templateFile;
        
protected const string NVELOCITY_PROPERTY = "nvelocity.properties";
        
#endregion
        
#region 成员变量及公共属性
        
private string fileExtension = ".htm"//输出的文件后缀名,如".cs"。
        private string directoryOfOutput = "Articles"//输出文件的文件夹名称
        private string fileNameOfOutput; //输出的文件名称

        
/// <summary>
        
/// 输出文件的文件夹名称, 如"Entity","Common"等
        
/// </summary>
        public string DirectoryOfOutput
        {
            
set { directoryOfOutput = value; }
            
get { return directoryOfOutput; }
        }
        
/// <summary>
        
/// 输出的文件名称. 如果想输出的文件为 "A.cs", 那么文件名为"A".
        
/// 默认的文件名称为模板文件的名称,没有后缀名.
        
/// </summary>
        public string FileNameOfOutput
        {
            
set { fileNameOfOutput = value; }
            
get { return fileNameOfOutput; }
        }
        
/// <summary>
        
/// 输出的文件后缀名,如".cs"。
        
/// </summary>
        public string FileExtension
        {
            
get { return fileExtension; }
            
set { fileExtension = value; }
        }
        
#endregion
        
/// <summary>
        
/// 根据参数初始化基类的Adapter
        
/// </summary>
        
/// <param name="templateFile">模板文件名称</param>
        public AbstractAdapter(string templateFile)
        {
            
#region 判断输入的参数是否有效
            
if (templateFile == null)
            {
                
const string error = "Invalid parameter of templateFile.";
                LogHelper.Error(error);
                
throw new ArgumentException(error, "templateFile");
            }
            
#endregion
            
this.templateFile = templateFile;
          
            InitTemplateEngine(); 
//初始化模板引擎

            fileNameOfOutput 
= GetFileNameFromTemplate(templateFile); // 默认情况下, 输出的文件名称为模板名称           
            directoryOfOutput = "Articles"// 默认情况下,放到Common目录下面
        }
        
/// <summary>
        
/// 创建输出到文件中的方法
        
/// </summary>
        public virtual void Execute()
        {
        }
        
/// <summary>
        
///根据模板创建输出的文件
        
/// </summary>
        public virtual void OutputFile()
        {
            
if (template != null)
            {
                
string filePath = Path.GetDirectoryName(this.fileNameOfOutput);
                DirectoryInfo dir 
= new DirectoryInfo(filePath);
                
if (!dir.Exists)
                {
                    dir.Create();
                }
                LogHelper.Debug(
string.Format("Class file output path:{0}"this.fileNameOfOutput));
                
using (StreamWriter writer = new StreamWriter(this.fileNameOfOutput, false))
                {
                    template.Merge(context, writer);
                }
            }
        }
        
/// <summary>
        
/// 根据模板创建代码输出到字符串流中
        
/// </summary>
        
/// <returns>根据模板生成的代码</returns>
        public virtual string OutputString()
        {
            
string code = string.Empty;
            
if (template != null)
            {
                
using (StringWriter writer = new StringWriter())
                {
                    template.Merge(context, writer);
                    code 
=  writer.GetStringBuilder().ToString();
                }
            }
            
return code;
        }
        
/// <summary>
        
/// 初始化NVelocity模板引擎并加载程序的配置信息e
        
/// </summary>
        protected void InitTemplateEngine()
        {
            Velocity.Init(NVELOCITY_PROPERTY);
            
//Velocity.Init();
            
// Make a context object and populate with the data.  This is where the Velocity engine 
            
// gets the data to resolve the references (ex. $foreignList) in the template
            context = new VelocityContext();
            
try
            {
                template 
= Velocity.GetTemplate(templateFile);
            }
            
catch (ResourceNotFoundException re)
            {
                
string error = string.Format("Cannot find template " + templateFile);
                LogHelper.Error(error);
                
throw new Exception(error, re);
            }
            
catch (ParseErrorException pee)
            {
                
string error = string.Format("Syntax error in template " + templateFile + ":" + pee.StackTrace);
                LogHelper.Error(error);
                
throw new Exception(error, pee);
            }
        }
        
#region 辅助方法
        
/// <summary>
        
/// 从模板文件名称获取输出文件名的方法
        
/// </summary>
        
/// <param name="templateFileName">带后缀名的模板文件名</param>
        
/// <returns>输出的文件名(无后缀名)</returns>
        private string GetFileNameFromTemplate(string templateFileName)
        {
            
int sindex1 = templateFileName.LastIndexOf('/');
            
int sindex2 = templateFileName.LastIndexOf('\\');
            
int sindex = (sindex1 > sindex2) ? sindex1 : sindex2;
            
int eindex = templateFileName.IndexOf('.');
            
if (sindex < eindex)
            {
                
return templateFileName.Substring(sindex + 1, eindex  sindex  1);
            }
            
else
            {
                
return templateFileName.Substring(sindex);
            }
        }
        
#endregion
    }

 

    public interface IAdapter
    {
        
/// <summary>
        
/// 创建输出到文件中的方法
        
/// </summary>
        void Execute();
    }

 

 另外,读取INI文件,需要的辅助类代码INIFileUtil如下所示。

代码

    public class INIFileUtil
    {
        
public string path;
        
public INIFileUtil(string INIPath)
        {
            path 
= INIPath;
        }
        [DllImport(
"kernel32")]
        
private static extern long WritePrivateProfileString(string section,string key,string val,string filePath);
        [DllImport(
"kernel32")]
        
private static extern int GetPrivateProfileString(string section,string key,string def, StringBuilder retVal,int size,string filePath);
    
        [DllImport(
"kernel32")]
        
private static extern int GetPrivateProfileString(string section, string key, string defVal, Byte[] retVal, int size, string filePath);
        
/// <summary>
        
/// 写INI文件
        
/// </summary>
        
/// <param name="Section"></param>
        
/// <param name="Key"></param>
        
/// <param name="Value"></param>
        public void IniWriteValue(string Section,string Key,string Value)
        {
            WritePrivateProfileString(Section,Key,Value,
this.path);
        }
        
/// <summary>
        
/// 读取INI文件
        
/// </summary>
        
/// <param name="Section"></param>
        
/// <param name="Key"></param>
        
/// <returns></returns>
        public string IniReadValue(string Section,string Key)
        {
            StringBuilder temp 
= new StringBuilder(255);
            
int i = GetPrivateProfileString(Section,Key,"",temp, 255this.path);
            
return temp.ToString();
        }
        
public byte[] IniReadValues(string section, string key)
        {
            
byte[] temp = new byte[255];
            
int i = GetPrivateProfileString(section, key, "", temp, 255this.path);
            
return temp;
        }
        
/// <summary>
        
/// 删除ini文件下所有段落
        
/// </summary>
        public void ClearAllSection()
        {
            IniWriteValue(
null,null,null);
        }
        
/// <summary>
        
/// 删除ini文件下personal段落下的所有键
        
/// </summary>
        
/// <param name="Section"></param>
        public void ClearSection(string Section)
        {
            IniWriteValue(Section,
null,null);
        }
    }

 

主要研究技术:代码生成工具、Visio二次开发
    
    转载请注明出处:
撰写人:伍华聪  http:
//www.iqidi.com 
 

[IIS]W3WP.EXE的解决方法

mikel阅读(1076)

转载:http://www.cnblogs.com/kuyuecs/archive/2009/02/25/1397921.html

防DDOS攻击11条:
1.确保所有服务器采用最新系统,并打上安全补丁。计算机紧急响应协调中心发现,几乎每个受到DDoS攻击的系统都没有及时打上补丁。
2.确保管理员对所有主机进行检查,而不仅针对关键主机。这是为了确保管理员知道每个主机系统在 运行什么?谁在使用主机?哪些人可以访问主机?不然,即使黑客侵犯了系统,也很难查明。
3.确保从服务器相应的目录或文件数据库中删除未使用的服务如FTP或NFS。Wu-Ftpd等守护程序存在一些已知的漏洞,黑客通过根攻击就能获得访问特权系统的权限,并能访问其他系统——甚至是受防火墙保护的系统。
4.确保运行在Unix上的所有服务都有TCP封装程序,限制对主机的访问权限。
5.禁止内部网通过Modem连接至PSTN系统。否则,黑客能通过电话线发现未受保护的主机,即刻就能访问极为机密的数据。
6.禁止使用网络访问程序如Telnet、Ftp、Rsh、Rlogin和Rcp,以基于PKI的访问程序如SSH取代。SSH不会在网上以明文格式传送 口令,而Telnet和Rlogin则正好相反,黑客能搜寻到这些口令,从而立即访问网络上的重要服务器。此外,在Unix上应该将.rhost 和 hosts.equiv文件删除,因为不用猜口令,这些文件就会提供登录访问!
7.限制在防火墙外与网络文件共享。这会使黑客有机会截获系统文件,并以特洛伊木马替换它,文件传输功能无异将陷入瘫痪。
8.确保手头有一张最新的网络拓扑图。这张图应该详细标明TCP/IP地址、主机、路由器及其他网络设备,还应该包括网络边界、非军事区(DMZ)及网络的内部保密部分。
9.在防火墙上运行端口映射程序或端口扫描程序。大多数事件是由于防火墙配置不当造成的,使DoS/DDoS攻击成功率很高,所以定要认真检查特权端口和非特权端口。
10.检查所有网络设备和主机/服务器系统的日志。只要日志出现漏洞或时间出现变更,几乎可以肯定:相关的主机安全受到了危胁。
11.利用DDoS设备提供商的设备
估计你是网管,还是选用硬件防火墙象思科的,或专业软
件防火墙如:傲盾,天盾等来减少DDOS。

 

w3wp.exe内存占用过高解决方法

在IIS6下,经常出现w3wp的内存占用不能及时释放,从而导致服务器响应速度很慢。

由于内存释放不及时严重影响到服务器的正常运营,建议采用以下配置,但请考虑自身服务器的使用情况。

可以做以下配置:

1、在IIS中对每个网站进行单独的应用程序池配置。即互相之间不影响。(进程池越多越占用内存,由于访问网站之后往往不会立即释放内存资源。)

2、设置应用程序池的回收时间,默认为1720小时,可以根据情况修改。同时,设置同时运行的w3wp进程数目为1。再设置当内存或者cpu占用超过多少,就自动回收内存 。

3、 设置固定时间加收进程,比如在凌晨1:00至9:00之前访问人数都比较少,影响很少小,可以设定每天在这个时间段内进行内存回收,以减少服务器因为内存 不足带来的影响。设定固定的时间在IIS6中没有限制次数,可以根据实际情况来设定时间比如:晚上12:00,8:00,12:30,6:30等时间段时 间有效的进程池资源回收。

一般来说,这样就可以解决了。但仍然会出现个别网站因为程序问题,不能正确释放。

那么,怎么样才能找到是哪一个网站的?
1、在任务管理器中增加显示pid字段。就可以看到占用内存或者cpu最高的进程pid

2、在命令提示符下运行iisapp -a。注意,第一次运行,会提示没有js支持,点击确定。然后再次运行就可以了。这样就可以看到pid对应的应用程序池

3、到iis中察看该应用程序池对应的网站,就ok了。

 

Windows Server 2003中的w3wp.exe进程大量占用cpu资源的各种问题解决方法2007-04-

24 14:43这几天服务器总是运行缓慢,远程登录后发现一个w3wp.exe的进程占用了100%

cpu

在Windows   Server   2003中对于每一个Web应用,IIS   6.0都用一个独立的w3wp.exe

的实例来运行它。w3wp.exe也称为工作进程(每一个主机头都会有一个)

直接在任务管理器中结束进程是不起作用的,结束后不久它会执行启动,要想结束它可

以在IIS中找到相应的应用程序池停止相应的应用程序池工作。

这些都不是解决办法,它的根本问题是你的那个网站程序有问题,在找到问题以前可以

先打开IIS找到应用程序池先用右键属性中设置“性能”把其中的CPU设成大于60%关闭应

用程序池,把关闭时间和开启时间设短一些比如10秒,这样当您的网站程序大量占用系

统资源时IIS自动快速回收进程并且快速启动进程,您的网站暂时还可以将就着工作。

要解决根本问题还要从程序查起,您可以在IIS中的应用程序池中右键创建多个应用程序

池,然后在每个主机头中的文件家选项的底部将应用程序池对应道刚才建好的应用程序

池,然后一个一个关闭在任务管理器中看看是哪个程序占用的资源较大。

下面是一些网友的相关贴子也许对大家有帮助

朋友的WEB服务器一直运行正常,但这几天CPU占用率一直将近100%,遂去看个究竟。
服务器采用Windows   2003,   网站使用ASP+Access数据库,   查看进程列表发现

w3wp.exe   占用了70%以上的CPU,
查看WEB日志,站点访问量不大,查看TCP连接也不多。用net   stop   w3svc停掉WEB服

务,CPU占用立即正常,net   start   w3svc启动WEB后不久现象又出来了。停止所有虚

拟站点,新建一个虚拟站点发现并没有问题,怀疑是站点本身的代码问题。
检查首页代码,大致是如下结构:

粗看一下并没有问题,但就是这段代码造成了w3wp.exe占用大量CPU,难道是死循环?似

乎没有理由。在循环体内加入计数,发现确实是死循环,说明RS.EOF一直为false,加入

如下代码:

if   RS.EOF   =   true   then   Response.Write   "EOF   is   true"
if   RS.EOF   =   false   then   Response.Write   "EOF   is   false"

发现输出竟然是EOF   is   true   EOF   is   false,   说明无法判断RS.EOF的值,

为何如此百思不得其解。检查数据库,发现库中并没有mytable表,   如果该表不存在,

RS.Open   "Select   *   FROM   mytable",   conn   就会出错,为何没有出错,很

有可能捕获的异常被忽略了。
检查包含文件conn.asp,   发现了异常处理代码:

On   Error   Resume   Next

原来问题在此。

On   Error   Resume   Next忽略了查询表时的失败以及后续的错误,造成进入死循环


那为何网站本来运行正常,现在却找不到mytable表了呢?仔细检查网站才发现“有‘客

’自远方来”,上传了后门工具、删除了多张数据表,害我忙活了一天。

更多的内容大家还可以到:

http://www.microsoft.com/china/technet/security/guidance/secmod93.mspx

查找更详细的安全设置

windows2003   iis6.0假死问题解决

这几天服务器总是运行缓慢,远程登录后发现一个w3wp.exe的进程占用了100%   cpu。

问题的原因最终找到两个:

1.采用的jet   数据库连接方式存在问题:http://support.microsoft.com/?id=838306

补丁下载:

chs:WindowsServer2003-KB838306-x86-chs.exe

enu:WindowsServer2003-KB838306-x86-enu.exe

2.将access数据库扩展名改为asp

下面是我的差错过程和解决方案:

搜索一下发现类似问题还真不少,那个w3wp的进程是iis6.0的应用程序池,网上的说法

有两种,一是因为asp或者ASP.NET代码中含有死循环引起的。但是服务器上这么多网站

,谁知道那个网站出了问题。二是由于上面的jet连接数据库方式的bug引起的,下载

838306的补丁,或者升级到sp1可以解决这个问题,但是打了这个补丁后,有些网站的问

题依然存在。

又去搜索,有人说将每个网站建立独立的应用程序池,应用程序池的安全性帐户设为本

地服务即可。方法如下:

首先新建应用程序池:

然后将网站的应用程序池指向刚才建立的应用程序池:

在建立完所有应用程序池后,统一修改应用程序池的属性:

将应用程序池安全帐户指定为本地服务:

设置完这些之后,问题依然存在,这样一个网站出现问题,不致影响其他网站,但是这

个网站仍然占用大量资源,导致其它网站响应缓慢。不过在任务管理器中出现了每个应

用程序池的进程,因此可以找到具体出问题的进程了。

下面是寻找出错网站的过程:要找到这个网站,必须把有问题的进程跟该网站的应用程

序池联系起来。首先设置任务管理器的查看方式,加入PID的显示:

然后再命令行运行iisapp   -a,可以看到PID跟应用程序池的对应关系:

再去iis中看该应用程序池对应的网站,有问题的网站就找到了,剩下的就是这个网站代

码中的问题了。

在某位网站管理员的纠缠不休下,我终于无法忍受,帮他找错误-   –   无数次配置iis

,网站程序也换了,该升级的也升级了,问题还是存在,黔驴技穷,把网站下载到本地

看看到底怎么回事。当我试图打开他的数据库的时候,问题出现了:

他的数据库是.asp的扩展名,要先修改为.mdb才能打开,但是当我点击要改名字的时候

,我的电脑没有响应了~!看来问题在这个数据库了。

用命令行rename之后,打开数据库,修复,似乎没有任何问题,但是再改为.asp时,又

出现了刚才的问题。哈~原来是.asp的扩展名在作怪。

但是我试着将其他的数据库改为.asp,没有问题。根本原因不得而知,望知情者告知。

最后,在iis中随便添加了一个isapi对应到mdb,造成mdb无法执行,防止下载,将所有

的.asp的数据库改回.mdb,问题解决
(转)Windows Server 2003中的w3wp.exe进程大量占用cpu资源的各种问题解决方法 1:使用 MicrosoftJet 数据库引擎 Web 应用程序可能停止响应负载,造成假死:
     原因:发生此问题是因为 Jet 数据库引擎中存在缺陷。 Microsoft Windows Server 2003 上只会出现此问题。 在 Windows Server 2003, COM+ 更改频率 CoFreeUnusedLibraries 函数被调用。 此更改导致 Jet 驱动程序以初始化频繁。 此行为可能导致死锁条件。
Web 服务器负载时最常出现此问题。
补丁下载:
chs:WindowsServer2003-KB838306-x86-chs.exe
enu:WindowsServer2003-KB838306-x86-enu.exe
在IIS6下,经常出现w3wp.exe的内存及CPU占用不能及时释放,从而导致服务器响应速度很慢。

解决内存占用过多,可以做以下配置:
1、在IIS中对每个网站进行单独的应用程序池配置。即互相之间不影响。
2、设置应用程序池的回收时间,默认为1720小时,可以根据情况修改。再设置当内存占用超过多少(如500M),就自动回收内存。

解决CPU占用过多:
1、在IIS中对每个网站进行单独的应用程序池配置。即互相之间不影响。
2、设置应用程序池的CPU监视,不超过25%(服务器为4CPU),每分钟刷新,超过限制时关闭。

根据w3wp取得是那个一个应用程序池:
1、在任务管理器中增加显示pid字段。就可以看到占用内存或者cpu最高的进程pid
2、在命令提示符下运行iisapp -a。注意,第一次运行,会提示没有js支持,点击确定。然后再次运行就可以了。这样就可以看到pid对应的应用程序池。(iisapp实际上是存放在 C:\windows\system32目录下的一个VBS脚本,全名为iisapp.vbs,如果你和我一样,也禁止了Vbs默认关联程序,那么就需要 手动到该目录,先择打开方式,然后选“Microsoft (r) Windows Based Script Host”来执行,就可以得到PID与应用程序池的对应关系。)
3、到iis中察看该应用程序池对应的网站,就ok了,做出上面的内存或CPU方面的限制,或检查程序有无死循环之类的问题。

 

QUOTE:

环境:win2003server+IIs+ASP+MSSQL

现象:每隔一段时间(不定,有时几分钟,有时半小时)出现一次网站打开非常缓慢,甚至有时会出现超时打不开站点,此时查看服务器端的进程,CPU占用率达到100%,其中w3wp占用70~80%,SQL占用20~30%。所有服务器端的操作也变得缓慢。

初 期解决方法:每次现象出现时,立即登录服务器直接结束w3wp进程或重启IIS服务,平均每天约十次操作,由于服务器存放于远程机房,所有操作都是远程控 制进行,有时会因此出现远程无法连接登录的情况,只能通过电话通知机房管理人员重启服务器解决,此过程导致用户抱怨不断。

经过网上查阅资料,发现此类现象多数由于网页代码不合理所致,以下情况会导致此类现象发生:
1、代码中多处使用application、seesion等服务器缓存,导致服务器资料过度占用;
2、代码有不合理语法,死循环等;
3、数据库损坏,尤其是ACCESS数据库;
4、装过多第三方软件或插件,与IIS或网页功能代码冲突。

第一阶段排查:根据查阅到的参考资料逐项分析
1、服务器上所有站点代码均为公司设计人员自行编写,可证实并无过多调用服务器缓存语法(排除)
2、代码是否存在不合理语法(不确定)
3、根据情况来看,IIS进程占用率升高时,SQL占用率同时升高,应为SQL数据库的站点,根据现象判断,库或表应该正常,估计是数据方面可能有误;(不确定)
4、服务器端除了基本的系统服务,防杀毒及网站运作必备服务之外,并无多余第三方软件,机率不大(排除)。

经过以上分析判断,将不确定项连起来得出的结论是:某个采用了SQL数据库的网站网页代码存在不合理语法,导致IIS和SQL进程CPU占用率过高。

第二阶段排查:
确定范围,接着继续把范围缩小。
由于服务器上采用SQL数据库的站点并不多,便于建立独立进程ID来观察,将所有采用SQL数据库的站点在IIS管理器中分别建立独立的应用程序池,然后 通过CMD界面输入:iisapp -a 命今查看并记录下各IIS池的进程ID号,通过多次现象重现时的观察,有个IIS进程ID是导致此次问题的罪魁祸首。

2003服务器用.net程序,w3wp老占cpu资源!怎么办?

 

w3wp.exe狂占内存的问题解决方案!  

问:

好的ASP.NET程序,放在一台服务器上,客户端连接使用一段时间后,在服务器上打开任务管理器一看,发现有很多w3wp.exe,占用内存很大,达到1g,请问为什么会这样?有什么办法可以避免这种情况呢?

答:这主要是你的ASP.NET 开发的程序有 内存泄漏;对于 非托管资源,一定要注意 释放。

-==================================
问:我的具体情况是这样的:
服务器配置 至强2.8G 内存512M SCSI硬盘 2块 (软镜像)
系统 windows 2003
现在挂了一个asp.net开发的网站 访问量不大 但是出现一个 问题就是
每当服务器运行2-3天后 访问网站就特别慢 重启动服务器后就 正常了
查看进程使用内存的情况 发现w3wp.exe 和sqlservr.exe 进程 占用内存
相当大 达到了170多M( 每个) 物理可用内存几乎用光
(服务器重启动时 占用的内存很小才40多M 每个)
以前网站挂在一个虚拟机上 数据库是分开挂的 从没出现这种情况
后来 原版移植到新服务器上就 出现这样的问题~~
还个一问题就是 我在SQL企业管理器中查看SQL进程 发现有很多是 。net 引起的进程是sleeping 但是却占用了内存~ 无法释放

搞了很久了 一直都没解决
求救~~请高手 指教~~ 万分感谢~~~~~

答:IIS服务管理器—-》应用程序池—-》添加你的应用,并设置最大内存,当程序达到最大内存后其会自动重启。

我的问题跟你一样,不过我的内存是2G的,访问量比较高,一般是差不多运行24小时后就得重启,内存没耗完,W3WP进程占到一百八九十兆,SQL占了二百多兆时,就得重启,不然整个站点就当在那边….55555555,搞了快半个月了还是不行,痛苦啊

w3wp.exe 就是你的ASP.NET应用宿主,如果你使用了大量的Session、Cache等资源,并且Session超市时间很长,那么内存占用量就比较大。应用 池是为增加性能而设的一个特性,但是也消耗很大的内存。另外关掉Windows Server 2003里的大多数Service(那个不用都可以关掉),也可以节省一部分内存

1.怀疑在程序中应用的CACHE,
2.CACHE中有大量的数据
3.频繁刷新CACHE
4.没有设计好CACHE的方式

你的问题我以前也遇见过,我以前是用的Session,后我全部改成cook之后就好多了,应该是你的Session或是你的CACHE有问题(CACHE不太懂,但多多少应该是有的)

跟踪下SQL的调用记录,在每次往CACHE或SESSION写入大量数据时记录一下时间,看是否太过频繁

1.在win2003里asp.net的进程就是w3wp.exe

2.512M 内存个人用是够用了,但是放在服务器上就有点不够用了,尤其是win2003 + asp.net +sql server 。尤其是sql server 他是很吃内存的,如果不控制的话,他会占光所有的物理内存(只剩下几十M 倒 100M 吧)。win2003 本身就要占用150M左右。也就剩不下什么了。

3.优化asp.net程序,就向楼上的说的那样,少用或不用session cache application之类的东西,再有就是是不是有翻页的地方,翻页处理不好也是会占很多内存的。

4.限制sql的内存。企业管理器——SQL的属性(一般是local)——“内存”标签
在这里看内存的设置,把最大值改成100M吧。

第四条是最快的方法,可以试一试。

我的一个自开发OA系统也存在这样的问题。
总结上面,大概原因是因为 session 和 cache 的不合理使用造成的。
我的应用程序中,确实用了很多的Session 和 Cache,
在 MSDN 中找到 了 “动态内存分配”这一篇,今天就试看看,是否有效。
希望有经验的朋友多给些信息,大家也好总结下出现类似错误的原因,谢谢!!

不知道你是什么网站。按理说是不会占用这么大的。如上你用了cache存放了超额的内容。当然。象session这种是不太可能占用这么大的了,或用了 application 类似的一些有超长时间或永久保持性的对象来保存大量数据。如利用单例保存数据这些都有可能造成使用大量的内存。

建义2003系统安装至少1G内存。

w3wp.exe是2003下的一个iis进程,至于楼主说的sql占用内存,那有可能是因为你的sql没有设置占用内存上限

 

-==================================
w3wp.exe进程狂占内存和CPU问题 谁能帮我??
   我的电脑:P4,512M内存。。当我用了aspx系统建站两个星期后,就出现了速度极慢的情况,在进程里看到w3wp.exe狂占内存和CPU.
刚开始几十人注册时速度都不会有影响,但注册会员有100名以上后,再有多人同时注册时就出现了速度极慢的情况,简直导致瘫痪。

IIS中设置不正确。在.NET中,不要在IIS里限制内存的使用,不然当内存不足的时候,IIS会不断的重新起动。每次起动有30秒的时候用来重新加 载.NET包,所以,网站在加载.NET 包时狂慢。这也是我当时用.NET系统时碰到过的问题。

应用程序池--属性--回收--内存回收:消耗太多内存时回收工作进程:1、最大虚拟内存。2、最大使用的内存。两个选项都没选择
这里不要打勾。另一处是性能里的请求队列,不要打勾。
自已调调看就有经验了,我也是自已摸索出来的方法。

A:关于W3WP.EXE的知识.

Q : W3WP.EXE,应用程序,应用程序池之间的关系

A : 一个应用程序池可以包含多个应用程序,一个应用程序池创建一个W3WP.EXE进程.那么我们就不能简单的说一个进程池对应一个W3WP.EXE进程了!其实是多个应用进程池对应一个W3WP.EXE进程的.

Q : 如何启动和关闭W3WP.EXE这个进程

A : W3WP.EXE这个进程将在你访问www应用程序的时候启动.有人就会这么问了:"我启动了一个Web应用程序,发现系统自动创建了一个 W3wp.exe进程。但我关闭这个Web应用程序后,发现这个刚创建的W3Wp.exe进程还在,请问如何关闭该进程。"这个进程不会在你关闭了这个程 序以后,就马上关闭的.那是因为Http是无连接的访问,当你关闭了web网页,不会返回相应的关闭信息,所以W3WP.EXE这个进程不会因为你关闭了 web应用程序尔关闭.

Q : 那么如何关闭这个进程呢?

A : 在应用程序池的配置中,"空闲超时"中设定合适的时间,系统默认的是20分钟.设定好指定的时间,那么在这个时间范围内没有在访问应用程序,那么系统会自 动的关闭W3WP.EXE这个进程的.而不需要我们人为的干预的.也可以在Windows任务管理器里面,结束这个进程

Q : 如何让W3WP.EXE进程长时间的运行.

A : W3WP.EXE这个进程的默认生命是1740分钟,但依然是在这种默认的自然规律下,W3WP.EXE将在自己失业20分钟后,被系统直接枪杀.这样就可以看出,要想让W3WP.EXE长时间的生存,那我们可以通过”空闲超时”不作处理来达到我们的目的.

Q : 在IIS6下,经常出现w3wp.exe的内存及CPU占用不能及时释放,从而导致服务器响应速度很慢。

A: 解决内存占用过多,可以做以下配置
1、在IIS中对每个网站进行单独的应用程序池配置。即互相之间不影响。
2、设置应用程序池的回收时间,默认为1720小时,可以根据情况修改。再设置当内存占用超过多少(如500M),就自动回收内存。

解决CPU占用过多:
1、在IIS中对每个网站进行单独的应用程序池配置。即互相之间不影响。
2、设置应用程序池的CPU监视,不超过25%(服务器为4CPU),每分钟刷新,超过限制时关闭。

根据w3wp取得是那个一个应用程序池:
1、在任务管理器中增加显示pid字段。就可以看到占用内存或者cpu最高的进程pid
2、在命令提示符下运行iisapp -a。注意,第一次运行,会提示没有js支持,点击确定。然后再次运行就可以了。这样就可以看到pid对应的应用程序池。(iisapp实际上是存放在 C:\windows\system32目录下的一个VBS脚本,全名为iisapp.vbs,如果禁止了Vbs默认关联程序,那么就需要手动到该目录, 先择打开方式,然后选“Microsoft (r) Windows Based Script Host”来执行,就可以得到PID与应用程序池的对应关系。)
3、到iis中察看该应用程序池对应的网站,就ok了,做出上面的内存或CPU方面的限制,或检查程序有无死循环之类的问题。

A2 : by 小步舞曲
查看占用cpu的w3wp进程里面里面有那个用户呼叫的token

Q : 另一种加载了某程序后发生w3wp.exe问题

A: 修改C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\CONFIG\machine.config,再配置 节点<processModel>中有一个属性"memoryLimit",这个属性的值是一个百分比,默认为"60"(注意,是60%不是 60M),即制定了ASP.NET进程能够使用所有物理内存的60%,当ASP.NET使用的内存量草果这个限额时,IIS会开始自动回收进程,即创建一 个新的进程去负责应付HTTP请求,而将旧进程所占用的内存回收.

[SQL]深入了解SQL Server数据库的线程与纤程

mikel阅读(918)

深入了解SQL Server数据库的线程与纤程

一、基本概念
(一)线程
SQL Server 使用操作系统的线程来执行并发任务。在不使用纤程的情况下,SQL Server将启动线程,并由OS将线程分配给cpu,线程管理由OS内核控制,当一个线程完成退出CPU,其他线程调度占用该CPU时,将发生一个上下 文切换,这个切换是在应用程序的用户模式和线程管理的内核模式之间的切换,所以需要付出一定的代价,应当尽量避免这种切换
(二)纤程
为了减少上下文切换,引入纤程的概念,可以在SQL Server中启用纤程,纤程是线程的子模块,纤程由运行在用户模式下的代码管理,所以切换纤程比切换进程的代价少的多,因为纤程模式不需要在将用户模式和内核模式中切换,而切换线程则需要这种转换。
(三)两者的区别
SQL Server管理纤程的调度,而OS管理线程的调度;线程模式里 SQL Server为每个并发用户创建配一个线程,而纤程模式中 SQL Server为每个CPU分配一个线程,并为每个并发用户创建配一个纤程,一个线程中可以有多个纤程纤程只在线程里切换,不用上下文切换
注意:SQL Server Personal Edition和 Desktop Edition不支持纤程模式;Win95,98不支持线程池
二、SQL Server对线程和纤程的管理
SQL Server既可以为用户连接维护一个线程池,也可以维护一个纤程池(如果设置了纤程模式,则系统只维护纤程池,不在是线程池) 在连接池中,线程和纤程都看作工作纤程,并可以由max worker threads 选项来设置SQL Server可分配的线程或纤程的最大数目,一般采用默认值255 。
当一个语句需要SQL Server执行时:
(1)如果线程池里用空闲的线程,SQL Server将为该语句分配一个线程
(2)如果线程池中没有可供使用的空闲线程并且没有达到最大工作线程数量,SQL Server将为该命令创建新的工作线程
(3)如果到了最大线程使用数量并且没有空闲的线程,则进程必须等待其他处理完成后释放一个线程
三、配置SQL Server线程纤程
(1)如何启用纤程模式:
企业管理器->服务器属性->处理器->使用windows nt 纤程 或者用 sp_configure 将lightweight pooling 设置为1
(2)max worker threads 选项
该选项配置SQL Server进程的可用工作线程数 默认值为 255
改变方法:
企业管理器->服务器属性->处理器->最大工作线程或者用 sp_configure修改max worker threads
(3)affinity mask 选项
该选项在系统负荷过重时可以提高对称多处理器系统的性能,将线程与特定的处理器相关联,并指定SQL Server使用的处理器
改变方法:
企业管理器->服务器属性->处理器->处理器控制->选择一或多个将亲和力掩码组合在一起的处理器或者用 sp_configure修改affinity mask
(4)max degree of parallelism 选项
该选项可以限制在并行计划执行中使用的处理器数量
改变方法:
企业管理器->服务器属性->处理器->并行->选择执行并行查询的处理器数量或者用 sp_configure修改max degree of parallelism。

[ORM]NHibernate的缓存管理机制

mikel阅读(766)

转载:http://www.cnblogs.com/RicCC/archive/2009/12/28/nhibernate-cache-internals.html

缓存管理面临的主要问题
缓存作为一个数据中心,具备添加、更新、删除数据的操作,因此跟数据库类似,会存在事务性、并发情况下数据一致性等问题需要解决
使用缓存比较典型的方式如下面代码:

Database db = new Database();
Transaction tx 
= db.BeginTransaction();
try
{
    
//从缓存读取
    MyEntity1 entity1 = cache.Get<MyEntity1>("pk of entity1"); 
    
//缓存中没有时从数据库读取
    if (entity1 == null) entity1 = db.Get<MyEntity1>("pk of entity1");
    
    
//对entity1进行处理

    updated 
= db.Update(entity1); //entity1的更新保存到数据库中
    if (updated) cache.Put(entity1); //数据库更新成功,则更新缓存
    
//事务中的其他处理

    tx.Commit();
}
catch
{
    tx.Rollback();
    
throw;
}

上 面的示例代码,是在一个事务性环境中使用缓存,存在更新操作(非只读缓存),如果这是一个共享缓存,这样的使用方式存在很多问题,比如说: 如果事务中的其他处理导致异常,数据库中对entity1的更新可以被回滚掉,但是cache中的entity1已经被更新了,如果不处理这样的情况后续 从cache中读出的entity1就是一个不正确的数据
所以,要正确的使用缓存,必须有一个完善的方案,充分考虑事务、并发等状况,确保数据的正确性、一致性
NHibernate 2个级别的缓存机制
   
相对于session来说,一级缓存是私有缓存,二级缓存是共享缓存
session加载实体的搜索顺序为: 1. 从一级缓存中查找;2. 从二级缓存中查找;3. 从数据库查找
一级缓存在事务之间担当了一个隔离区域的作用,事务内对实体对象的所有新增、修改、删除,在事务提交之前对其他session是不可见的,事务提交成功之后批量的将这些更新应用到二级缓存中
这样的2级缓存机制能够在很大程度上确保数据的正确性(比如前面示例代码中事务失败的情况下,就不会将数据更新到二级缓存中,防止了二级缓存出现错误的数据),以及防止ReadUncommited等其他一些事务一致性问题
内部实现上,对一级缓存的管理很简单,所有已加载的实体(以及已经创建proxy但未加载的实体等)都被缓存在持久化上下文(NHibernate.Engine.StatefulPersistenceContext)中
待 新增、更新、删除的实体,使用3个列表缓存起来,事务提交的时候将他们应用到数据库和二级缓存中(Flush调用或者因为查询等导致的 NHibernate自动执行的Flush操作也会将他们应用到数据库,但不会应用到二级缓存中,二级缓存只在事务提交成功之后才更新)
NH1.2中这3个列表维护在SessionImpl中,NH2.0以后添加的新功能特性以及代码本身的重构动作相当多,这3个列表维护在NHibernate.Engine.ActionQueue中
二级缓存因为是共享缓存,存在并发更新冲突,但又必须保证二级缓存数据的正确性,因此处理机制就复杂得多。下面是详细的二级缓存处理机制
二级缓存的主要结构
主要接口:
   
接口职责:
ICache: 统一的缓存存取访问接口
ICacheProvider: 工厂类、初始化类,用于创建ICache对象,启动时对cache server或组件进行初始化,退出时对cache server或组件进行必要的退出处理等
处理过程:
1. 配置文件中指定ICacheProvider的实现类
2. SessionFactory启动时创建ICacheProvider对象,执行ICacheProvider.Start()方法,并为每一个cache region创建一个ICache对象
3. 整个运行过程中,NHibernate可以使用SessionFactory创建的ICache完成缓存的存取操作
4. SessionFactory关闭时调用ICacheProvider.Stop()方法
实体状态的转换:

以memcached为例,实体缓存时的状态转换如上图
1. CacheEntry表示一个需要存储到缓存中或者从缓存中返回的对象
    CacheEntry中包含拆解后的实体属性值(DisassembledState,object[]类型,数组中是每个属性的值)、实体的版本(乐观 锁时使用)、类型名称。采用这样的处理方式,我们定义的domain对象就不需要实现Serializable接口,也可以被序列化存储到缓存 中
    对于primitive type的实体属性,拆解和组装过程没有特殊的处理;对于composite component、one-to-one、one-to-many的collection等实体属性,分解之后在DisassembledState中 存放的是owner(即当前被缓存的实体对象)的id值,组装过程中根据这个id值去取相关的对象设置到这个属性上(可能从一级缓存、二级缓存,或者数据 库加载,依赖于具体的设置和运行时的状态)
2. CacheItem用于解决并发更新二级缓存时的数据一致性问题(不考虑这个问题的话,直接将CacheEntry存到缓存中就可以了),主要是对soft lock机制的处理,后面详细介绍
3. 将CacheItem转换成DictionaryEntry的处理,是由NHibernate.Caches.Memcache进行的,完全是一个多余的处理
    NHibernate使用规则 [完整的类名#id值] 生成cache key,NHibernate.Caches.Memcache会在NHibernate生成的key前面再添加上 [region名称@](如果类的hbm文件中没有设置region名称,默认region为完整的类名,这样完整类名会在cache key中出现2次)
    memcached的key最长只能是250个字符,NHibernate.Caches.Memcache在cache key超过250字符时,取key的hash值作为新的memcached key值,因为这样会存在hash冲突,所以NHibernate.Caches.Memcache构造一个DictionaryEntry对象(原 key值的MD5作为DictionaryEntry的key值,被缓存的对象作为value),将 DictionaryEntry存到memcached中。从缓存get对象时,NHibernate.Caches.Memcache对返回的 DictionaryEntry的key值再做一次比较,排除掉hash冲突的情况
    这样的方式使用memcached,效率上太浪费了。一不留神,完整的类名就会在缓存数据中出现4次!
    基于NHibernate的机制和memcached的特点,可以考虑使用cache region来区分不同的memcached集群,比如说用A、B 2台服务器作为只读缓存,region取名为readonly_region;C、D、E 3台服务器作为读写缓存,region取名为readwrite_region
4. 从DictionaryEntry到Memcached Server这段处理由Memcached.ClientLibrary完成,关于Memcached.ClientLibrary的分析,参 考memcached client – memcacheddotnet (Memcached.ClientLibrary)
解决并发更新冲突
NHibernate定义了3中缓存策略: 只读策略(useage="read-only")、非严格的读写策略(useage="nonstrict-read-write")和读写策略(useage="read-write")
处理并发更新的结构
   
ICacheConcurrencyStrategy聚 合了一个ICache对象,NHibernate操作缓存时不是直接使用ICache对象,而是通过ICacheConcurrencyStrategy 完成,这样确保系统对二级缓存的操作,都是在特定的缓存策略下进行的
ICacheConcurrencyStrategy和ICache接口的语义有差别,ICache纯粹是缓存的操作接口,而ICacheConcurrencyStrategy则与实体的状态变化相关
ICacheConcurrencyStrategy的语义
Evict: 让缓存项失效
Get, Put, Remove, Clear: 与ICache的相关方法相同,纯粹的缓存读取、存储等操作
Insert, AfterInsert: 新增实体时的方法,实体新增到数据库之后会执行Insert方法,事务提交后会执行AfterInsert方法。这些方法中如何处理二级缓存,由具体的缓存策略确定
Update, AfterUpdate: 更新实体时的方法,实体修改update到数据库之后会执行Update方法,事务提交后会执行AfterUpdate方法。这些方法中如何处理二级缓存,由具体的缓存策略确定
Lock, Release: 这2个方法分别对缓存项进行加锁、解锁。语义上,事务中开始更新实体时对缓存项执行Lock方法,事务提交后对缓存项执行Release方法,在这些方法中如何处理二级缓存由具体的缓存策略确定
在 前面实体状态转换的图中,CacheEntry到CacheItem的转换由ICacheConcurrencyStrategy接口完 成,CacheItem只被ICacheConcurrencyStrategy使用,NHibernate内部其他需要与缓存交互的地方均使用 CacheEntry和ICacheConcurrencyStrategy接口
ReadOnly策略
运用场景为,数据不会被更新,NHibernate不更新二级缓存的数据。采用只读策略的实体不能执行update操作,否则会抛出异常,可以执行新增、删除操作。只读策略只在实体从数据库加载后写到缓存中
UnstrictReadWrite策略
运用场景为,数据会被更新,但频率不高,并发存储情况很少
采用该策略的实体,新增时不会操作二级缓存;更新时只是简单的将二级缓存的数据删除掉(Update, AfterUpdate方法中都会删除二级缓存数据),这样期间或者后续的请求将从数据库加载数据并重新缓存
因为更新过程没有对缓存数据使用lock,读取时也不会进行版本检查,因此并发存取时无法保证数据的一致性,下面是一个这样的示例场景:
   
1, 2: 请求1在事务中执行更新,NH更新数据库并从二级缓存删除该数据
3: 某些操作(例如ISession.Evict)导致请求1的一级缓存中该数据失效
4, 5: 请求2从数据库加载该数据,并放入二级缓存。因为请求2在另外的事务上下文中,因此加载的数据不包含请求1的更新
6: 请求1需要重新加载该数据,因为一级缓存中没有,因此从二级缓存读取,结果读到的将是一份错误的数据
ReadWrite策略
运用场景为,数据可能经常并发更新,NHibernate确保ReadCommitted的事务隔离级别,如果数据库的隔离级别为RepeatableRead,该策略也能基本保证二级缓存满足RepeatableRead的隔离级别
NHibernate通过使用版本、timestamp检查、soft lock等机制实现这一目标
soft lock的原理比较简单,假如事务中需要更新key为839的数据,首先创建一个soft lock对象,用839这个key存到cache中(如果cache中原来已经用839的key缓存了这个数据,也直接用soft lock覆盖他),然后更新数据库,完成事务的其他处理,事务提交之后将id为839的实体对象再重新存入cache中。事务期间其他所有从二级缓存读取 839的请求都将返回soft lock对象,表明二级缓存中这个数据已经被加锁了,因此转向数据库读取
   
ReadWriteCache.ILockable为soft lock接口,CacheItem和CacheLock两个类实现了这个接口
更新数据时的处理步骤
   
1: 更新操作前先锁定二级缓存的数据
2,3: 从二级缓存取数据,如果返回的是null或者CacheItem,则新建一个CacheLock并存入二级缓存;如果返回的是一个CacheLock,则表明有另外的事务已经锁定该值,将并发锁定计数器增1并更新回二级缓存中
4: 返回lock对象给EntityAction
5, 6, 7: 更新数据库,完成事务的其他处理,提交事务。ReadWriteCache的Update不做任何处理
8: 事务提交后执行ReadWriteCache的AfterUpdate方法
    先从二级缓存读取CacheLock对象,如果返回null说明锁已经过期(事务时间太长造成)
    如果锁已经过期,或者返回的CacheLock已经不是加锁时返回的那个(锁过期后又被其他线程重新加锁了),则新建一个CacheLock,设为 unlock状态放回二级缓存,结束整个更新处理
    如果CacheLock为并发锁状态,则将CacheLock并发锁计数器减一,更新回二级缓存,结束整个更新处理
    如果不是上面这些情况,则说明期间没有并发更新,将新的实体状态更新到二级缓存(锁自然被解除掉了)
一 旦发生并发更新,并发的最后一个事务提交之后,NHibernate也不会将实体重新存入二级缓存,此时在二级缓存中存储的是一个unlock状态的 CacheLock对象,在这个CacheLock过期以后,实体才可能被重新缓存到二级缓存中。采用这样的处理方式,是因为并发事务发生 时,NHibernate不知道数据库中哪一个事务先执行、哪一个后执行,为了确保ReadWrite策略的语义,强制这段时间内二级缓存失 效
ReadWriteCache的Get方法,除了在二级缓存的数据被锁定时将返回null之外,还会将缓存项的时间戳与请求线程的事务时间进行比较,也可能返回null,使得请求转向数据库查询,由数据库保证事务隔离级别
而put方法还会比较实体的版本(使用乐观锁的情况)
看 源代码时,Timestamper类是一个时间戳与计数器结合的产物,在时间上精确到毫秒,每毫秒内采用1-4096的一个计数器,增量分配。 NHibernate.Caches.MemCache将ReadWriteCache的二级缓存锁超时时间设置为0xea60000,换算过来就是1分 钟

[架构]关于web架构设计

mikel阅读(893)

转载:http://eroading.com/e-other/web-architecture-design

本人作为一位web工程师,着眼最多之处莫过于性能与架构,本次幸得参与sd2.0大会,得以与同行广泛交流,于此二方面,有些架构设计的心得,不敢独享,与众友分享,本文是这次参会与众同撩交流的心得.

架构设计的几个心得:

一,不要过设计:never over design

这是一个常常被提及的话题,但是只要想想你的架构里有多少功能是根本没有用到,或者最后废弃的,就能明白其重要性了,初涉架构设计,往往倾向于设计大而化 一的架构,希望设计出具有无比扩展性,能适应一切需求的增加架构,web开发领域是个非常动态的过程,我们很难预测下个星期的变化,而又需要对变化做出最 快最有效的响应。

ebay的工程师说过,他们的架构设计从来都不能满足系统的增长,所以他们的系统永远都在推翻重做。请注意,不是ebay架构师的能力有问题,他们 设计的架构总是建立旧版本的瓶颈上,希望通过新的架构带来突破,然而新架构带来的突破总是在很短的时间内就被新增需求淹没,于是他们不得不又使用新的架构
web开发,是个非常敏捷的过程,变化随时都在产生,用户需求千变万化,许多方面偶然性非常高,较之软件开发,希望用一个架构规划以后的所有设计,是不现实的

二,web架构生命周期:web architecture‘s life cycle
既然要杜绝过设计,又要保证一定的前瞻性,那么怎么才能找到其中的平衡呢?希望下面的web架构生命周期能够帮到你

architecture_life_cycle

所设计的架构需要在1-10倍的增长下,通过简单的增加硬件容量就能够胜任,而在5-10倍的增长期间,请着手下一个版本的架构设计,使之能承受下一个10倍间的增长

google之所以能够称霸,不完全是因为搜索技术和排序技术有多先进,其实包括baidu和yahoo,所使用的技术现在也已经大同小异,然而,google能在一个月内通过增加上万台服务器来达到足够系统容量的能力确是很难被复制的


三,缓存:Cache

空间换取时间,缓存永远计算机设计的重中之重,从cpu到io,到处都可以看到缓存的身影,web架构设计重,缓存设计必不可少,关于怎样设计合理的缓 存,jbosscache的创始人,淘宝的创始人是这样说的:其实设计web缓存和企业级缓存是非常不同的,企业级缓存偏重于逻辑,而web缓存,简单快 速为好。。

缓存带来的问题是什么?是程序的复杂度上升,因为数据散布在多个进程,所以同步就是一个麻烦的问题,加上集群,复杂度会进一步提高,在实际运用中,采用怎样的同步策略常常需要和业务绑定

老钱为搜狐设计的帖子设计了链表缓存,这样既可以满足灵活插入的需要,又能够快速阅读,而其他一些大型社区也经常采用类此的结构来优化帖子列表,memcache也是一个常常用到的工具

链接:钱宏武谈架构设计视频 http://211.100.26.82/CSDN_Live/140/qhw.flv

Cache的常用的策略是:让数据在内存中,而不是在比较耗时的磁盘上。从这个角度讲,mySQL提供的heap引擎(存储方式)也是一个值得思考的方法,这种存储方法可以把数据存储在内存中,并且保留SQL强大的查询能力,是不是一举两得呢?

我们这里只说到了读缓存,其实还有一种写缓存,在以内容为主的社区里比较少用到,因为这样的社区最主要需要解决的问题是读问题,但是在处理能力低于 请求能力时,或者单个希望请求先被缓存形成块,然后批量处理时,写缓存就出现了,在交互性很强的社区设计里我们很容易找到这样的缓存

四,核心模块一定要自己开发:DIY your core module
这点我们是深有体会,钱宏武和云风也都有谈到,我们经常倾向于使用一些开源模块,如果不涉及核心模块,确实是可以的,如果涉及,那么就要小心了,因为当访 问量达到一定的程度,这些模块往往都有这样那样的问题,当然我们可以把问题归结为对开源的模块不熟悉,但是不管怎样,核心出现问题的时候,不能完全掌握其 代码是非常可怕的


五,合理选择数据存储方式:reasonable data storage

我们一定要使用数据库吗,不一定,雷鸣告诉我们搜索不一定需要数据库,云风告诉我们,游戏不一定需要数据库,那么什么时候我们才需要数据库呢,为什么不干脆用文件来代替他呢?
首先我们需要先承认,数据库也是对文件进行操作。我们需要数据库,主要是使用下面这几个功能,一个是数据存储,一个是数据检索,在关系数据库中,我们其实非常在乎数据库的复杂搜索的能力,看看一个统计用的tSQL就知道了(不用仔细读,扫一眼就可以了)

select   c.Class_name,d.Class_name_2,a.Creativity_Title,b.User_name,(select   count(Id)   from   review   where   Reviewid=a.Id)   as   countNum   from   Creativity   as   a,User_info   as   b,class   as   c,class2   as   d   where   a.user_id=b.id   and   a.Creativity_Class=c.Id   and   a.Creativity_Class_2=d.Id 
select   a.Id,max(c.Class_name),(max(d.Class_name_2),max(a.Creativity_Title),max(b.User_name),count(e.Id)   as   countNum   from   Creativity   as   a,User_info   as   b,class   as   c,class2   as   d,review   as   e   where   a.user_id=b.id   and   a.Creativity_Class=c.Id   and   a.Creativity_Class_2=d.Id   and   a.Id=e.Reviewid   group   by   a.Id ……………………………………….

我们可以看出需要数据库关联,排序的能力,这个能力在某些情况下非常重要,但是如果你的网站的常规操作,全是这样复杂的逻辑,那效率一定是非常低 的,所以我们常常在数据库里加入许多冗余字段,来减小简单查询时关联等操作带来的压力,我们看看下面这张图,可以看到数据库的设计重心,和网站(指内容型 社区)需要面对的问题实际是有一些偏差的

database

同样其他一些软件产品也遇到同样的问题所以具我了解,有许多特殊的运用都有自己设计的特殊数据存储结构与方法,比如有的大型服务程序采取树形数据存储结构,lucene使用文件来存储索引和文件

从另外一个角度上看,使用数据库,意味着数据和表现是完全分离的(这当然是经典的设计思路),也就是说当需要展示数据时,不得不需要一个转换的过 程,也可以说是绑定的过程,当网站具备一定规模的时候,数据库往往成为效率的瓶颈,所以许多网站也采用直接书写静态文件的方法来避免读取操作时的绑定

这并不是说我们从今天起就可以把我们亲爱的数据库打入冷宫,而是我们在设计数据的持久化时,需要根据实际情况来选择存储方式,而数据库不过是其中一个选项


六,搞清楚谁是最重要的人:who’s the most important guy

在用例需求分析的时候常常讲到涉众,就是和你的设计息息相关的人,在web中我们一定以为最重要的涉众莫过于用户了。,在一个传统的互动社区开发中,最重 要的东西是内容,用户产生内容,所以用户就是上帝,至于内容挑选工具,不就是给坐我后面三排的妹妹们用的吗?凑或行了,实在有问题我就在数据里手动帮你加 得了。。这大概是眼下许多小型甚至中型网站技术人员的普遍想法。钱宏武在他的讲座里谈到了这个问题:实际上网站每天产生的内容非常的多,普通人是不可能看 完的,而编辑负责把精华的内容推荐到首页上,所以很多用户读到的内容其实都依赖于编辑的推荐,所以设计让编辑工作方便的工具也是非常重要,有时甚至是最重 要的。


七,不要执着于文档:don’t be crazy about document

web开发的文档重要吗?什么文档最重要?我的看法是web开发中交流>文档,

现在大的软件公司比较流行的做法是:
注重产品设计文档,在这种方法里,产品文档非常详尽,并且没有歧义,开发人员基于设计文档开发,测试人员基于设计文档制定测试方案,任何新人都可以通过阅读产品设计文档来了解项目的概况

而web项目从概念到实现的时间是非常短的,而且越短越好,并且由于变化迅速,要想写出完整的产品和需求文档是几乎不可能的,大多数情况是等你写出 完备的文档,项目早就是另外一个样子,但是没有文档的问题是,如果团队发生变化,添加新成员怎样才能了解软件的结构和概念呢,一种是每个人都了解软件的整 个结构,除非你的团队整体消失,否则任何一个人都能够担当培养新人的责任,这种face2face交流比文档有效率很多。

于是就有了前office开发者,现任yahoo中国某产品开发负责人的刘振飞所感觉到的落差,他说,我们的项目是吵出来的,我听完会心一笑


八,团队:team

不要专家团队,而要外科手术式的团队,你的团队里一定要有清道夫,需要有弓箭手,让他们和项目一起成长,才是项目负责人的最大成就

 

总结:

架构是一种权衡

architecture

web开发的特点是是:没有太复杂的技术难点,一切在于迅速的把握需求,其实这正式敏捷开发的要旨所在,一切都可以非常快速的建立,非常快速的重构,我们的开发工具,底层库和框架,包括搜索引擎和web文档提供的帮助,都提我们供给了敏捷的能力。

此外,相应的,最有效率的交流方式必须留给web开发,那就是face2face(面对面),不要太担心你的设计不能被完备的文档所保留下来,他们会以交流,代码和小卡片的方式保存下来

人的因素会更加重要,无论是对用户的需求,还是开发人员的素质。

[性能]網站效能調校經驗談

mikel阅读(1319)

转载:http://blog.miniasp.com/post/2008/09/Website-Performance-Tunning-Talks.aspx

我之前在 iT邦幫忙 參加 it實戰經驗大募集 的活動,在 網站效能調校實戰經驗大募集,你有什麼精彩的調校經驗嗎? 的回答獲得了實戰王的獎賞(NT$ 1,000),而昨天有朋友問我怎麼都沒有發表這方面的文章,我才想說這篇文章應該也可以寫在我的部落格中,以下是完整的文章,不過大多是以條列式的方式呈現,牽涉的領域也很廣泛,各位僅供參考。

  1. 前端可採用 Squid 建置 Reverse Proxy Server 加速網站回應速度,且也能擴大每一台 Server 的網頁吞吐量(Throughput),不過網站的程式也需要送出適當的 HTTP Cache Header 跟 Reverse Proxy Server 做到充分搭配。流量大的網站也可以架設多台 Reverse Proxy Server 分流,除了可以用 Squid 之外,也可用 Nginx HTTP Server 做 Load Balance 或許多其他的 Reverse Proxy Server。除了用 Reverse Proxy Server 之外,也可以採用 Cluster 架構架設 Load Balancing 環境,向這類 Load Balancing 的商業產品一大堆,例如:F5, A10, Cisco, … 都有,但免費的也很多,例如 Linux Virtual Server,而 Windows Server 2003 也有 Windows Cluster 與 Network Load Balance (NLB) 等機制。
  2. 徹底分離「靜態頁面/檔案」與「動態網頁」,妥善運用主機資源(CPU,Memory),主機也是有講究專業分工的,將個別主機負責的工作切割可有效提高網頁吞吐量與運行效率:
    1. 將 靜態網頁/圖檔/Flash/JavaScript/CSS/…等,集中在特定幾台Web伺服器,這樣配置的主機資源利用率會更高,因為靜態頁面的 CPU與Memory的資源消耗率低,純粹消耗頻寬而已,用專屬或小型的Web伺服器發送這些檔案可節省不少記憶體,且運作的效率也會比 Apache, IIS 這類多功能的 Web Server 來的效率還高,例如說:NginxTUX Web ServerBoa Webserver
    2. 將 動態網頁依照功能性與流量做水平分割或垂直分割,水平分割是指單一功能流量大,可採用 Load Balance 機制將主機負載分流,垂直分割是指依照單一服務拆分為多個子功能,每個功能配置獨立的主機專門提供此功能的相關資源運用與CPU運算。有個重點就是主機跟 主機間、服務跟服務之間的關連性或耦合姓越低的話,網站架構的延展性(Scalability)越高,因為大型網站最需要的就是延展性,要讓網站經營突破 甜蜜點(Sweet Point)時隨時可從 5 台主機擴充到 50 台而不用大幅修正網站架構與程式!
  3. 適度的使用多層快取(Cache)機制,從最前端的 HTTP Client Cache, Reverse Proxy Server 到 Server-side Cache 機制都要妥善規劃,才能達到最佳效能。
    1. 我有找到一篇介紹 HTTP Cache 的文章,寫的很棒,各位可以看看:Caching Tutorial – for Web Authors and Webmasters [ 用 Google Langage Tool 翻譯成中文的版本 ]
  4. 關連式資料庫的規劃架構尤其重要,除了可以採用叢集(Cluster)架構架設資料庫主機外,流量真的很大也可以採用資料庫分割的技巧,而資料庫分割技術也有分「水平分割」與「垂直分割」。
    1. 水平分割只指將一個資料庫中的表格拆分在不同的資料庫主機中,以分散資料量、降低資料庫伺服器負擔。
    2. 垂直分割就更複雜了,將「同一個表格的資料」拆分到不同資料庫伺服器中,這大概要到千萬流量才會用到這種架構吧,這時你可能要自行規劃演算法將資料拆分或資料搬移。
  5. 分析出主機的效能瓶頸是出在 IO 問題、CPU 問題、還是 Memory的問題。
    1. IO 問題:將部分 IO 改用 Memory 做快取(Cache),或看是否有辦法將檔案存取優化,若單一主機的 IO 優化已經到了極限,可改採 NAS 或 SAN 解決方案。
    2. CPU 問題:檢查程式的寫法是否有改善的空間,或是因為網站的 Exception 問題太多、或是 GC (Garbage Collection) 執行過於頻繁所致。
    3. Memory 問題:看是否是程式寫不好導致 Memory leak 的問題(通常是這樣),若無法再調校就加 RAM 吧,當然是越大越好摟,建議採用 64bits 的作業系統,對大量記憶體支援度較好。
  6. 要進行效能調校一定要先進行「效能檢測」或「壓力測試」,我也曾經寫了一篇使用 ApacheBench 工具進行壓力測試的文章,這應該是最「低級」的壓力測試方法了,只能測試單點功能的效能,無法測出整體網站的運作效能,但有興趣的人可以參考一下:使用 ApacheBench 進行網站的壓力測試

[IIS]簡易克服 CSS 被瀏覽器快取(Cache)的問題

mikel阅读(784)

转载:http://blog.miniasp.com/post/2008/02/Avoid-browser-cache-problem-on-css-or-JavaScript-file.aspx

我們建置網站完成後,接下來就是將網站上線了,但當發現網站有問題要修改的時候,可能會遇到一個問題,就是當我們修改 JavaScript 或 CSS 的時候,預設的情況下 *.js 或 *.css 這類靜態網頁通常會被 Browser 快取(Cache)起來,除非你先主動清除這些快取資料,或人工將頁面重新整理才能讀到新的資料。但這個「人工手動」的動作客戶可無法接受!因為大部分網 友不會知道需要做這個「重新整理」的步驟,看到的可能是錯誤的畫面、或 JavaScript 的問題。

一個最簡單的方法就是將 css 載入的地方加上「日期」或「版本編號」,請看下圖:

這是原本擺在 <head> 裡面的 CSS 與 JavaScript 宣告:

尚未修改前的 CSS, JavaScript

 加上版本編號就會變成這樣:

加上版本編號就會變成這樣

這兩個語法對 Web Server 來說是一樣的,因為這兩個檔都是「靜態頁面」;但是對 Browser 來說就不一樣了,因為 Browser 才不管你抓的是不是同一個檔案,對 Browser 來說唯一個依據就只有「網址」而已,所以只要是不同的網址就會重新向 Web Service 抓取新的檔案內容!

所以當修改網站的 CSS 或 JavaScript 後,你可以直接修改 ASP.NET 的 MasterPage 將檔案後面加上「日期」或「版本編號」就可以啦,一個最簡單、實用、好用又很少人知道的技巧喔!