[转载]使用jcrop进行头像剪切 - 树上月 - 博客园

mikel阅读(1238)

[转载]使用jcrop进行头像剪切 – 树上月 – 博客园.

        项目需要做一个头像截取的功能,类似于QQ头像截取功能。在网上搜了下,最后使用JQuery插件jcrop来截取,

在后台来进行图片切割。

        头像截取的原理:在前台使用jcrop获取切面的x轴坐标、y轴坐标、切面高度、切面宽度,然后将这四个值传给后

台。在后台要进行放大处理:将切面放大N倍,N=原图/前台展示的头像。即X = X*原图宽/前图宽,Y = Y*原图高/

图高,W = W*原图宽/前图宽,H = H*原图高/前图高。

        实例:

        JSP:

<div id="cutImage" style="display: none;">
<div class="bigImg" style="float: left;"><img id="srcImg" alt="" width="400px" height="270px" /></div>
<div id="preview_box" class="previewImg"><img id="previewImg" alt="" width="120px" /></div>
&lt;div &gt;<form id="crop_form" action="" method="post"><input id="bigImage" type="hidden" name="bigImage" />
<input id="x" type="hidden" name="x" />
<input id="y" type="hidden" name="y" />
<input id="w" type="hidden" name="w" />
<input id="h" type="hidden" name="h" /><input id="crop_submit" type="button" value="确认" />

</form></div>

样式:大图、小图展示都需要固定高度、宽度,因为后台需要进行放大处理。即:

然后是使用jcrop了。在使用jcrop前我们需要下载jcrop:http://deepliquid.com/content/Jcrop.html

将下载的压缩包解压后可以看到三个文件夹及一个index.html文件,/ css下放置的是Jcorp的样式文件,/demo下放置的是几个简单的例子(index.html中引用的链接就是放置在这个文件夹下),/js下放置的是Jcorp中最重要的脚本文件。我们只需要使用三个文件即可:JQuery.Jcrop.css、JQuery.Jcrop.js、jQuery.js

使用方法:

//裁剪图像
function cutImage(){
$("#srcImg").Jcrop( {
aspectRatio : 1,
onChange : showCoords,
onSelect : showCoords,
minSize :[200,200]
});
//简单的事件处理程序,响应自onChange,onSelect事件,按照上面的Jcrop调用
function showCoords(obj) {
$("#x").val(obj.x);
$("#y").val(obj.y);
$("#w").val(obj.w);
$("#h").val(obj.h);
if (parseInt(obj.w) &gt; 0) {
//计算预览区域图片缩放的比例,通过计算显示区域的宽度(与高度)与剪裁的宽度(与高度)之比得到
var rx = $("#preview_box").width() / obj.w;
var ry = $("#preview_box").height() / obj.h;
//通过比例值控制图片的样式与显示
$("#previewImg").css( {
width : Math.round(rx * $("#srcImg").width()) + "px", //预览图片宽度为计算比例值与原图片宽度的乘积
height : Math.round(rx * $("#srcImg").height()) + "px", //预览图片高度为计算比例值与原图片高度的乘积
marginLeft : "-" + Math.round(rx * obj.x) + "px",
marginTop : "-" + Math.round(ry * obj.y) + "px"
});
}
}
}

在使用jcrop前一定要先将$(“”).jcrop();进行预初始化,否则没有效果。

还有一种调用的方法,

var api = $.Jcrop('#cropbox',{
onChange: showPreview,
onSelect: showPreview,
aspectRatio: 1
});

这种方法是将Jcrop生成的对象赋给一个全局变量,这样操作就会比较方便。

通过上面的js,就将X轴坐标、Y轴坐标、高度H、宽度W这个四个值传递给后台了,后台就只需要根据这四个值

进行放大处理,然后切割即可。

Action

/**
* 裁剪头像
*/
public String cutImage(){
/*
* 获取参数
* x,y,w,h,bigImage
*/
HttpServletRequest request = (HttpServletRequest) ActionContext.getContext().get(ServletActionContext.HTTP_REQUEST);
int x = Integer.valueOf(request.getParameter("x"));
int y = Integer.valueOf(request.getParameter("y"));
int w = Integer.valueOf(request.getParameter("w"));
int h = Integer.valueOf(request.getParameter("h"));
String bigImage = request.getParameter("bigImage");

//获取文件真实路径
//获取文件名
String[] imageNameS = bigImage.split("/");
String imageName = imageNameS[imageNameS.length-1];
//文件正式路径
String imagePath = getSavePath()+"\\"+imageName;

//切割图片
ImageCut imageCut = new ImageCut();
imageCut.cutImage(imagePath, x, y, w, h);

//头像裁剪完成后,将图片路径保存到用户
UserBean userBean = (UserBean) request.getSession().getAttribute("userBean");
userBean.setUserPhoto(bigImage);
//保存头像
UserCenterService centerService = new UserCenterService();
centerService.updatePhoto(userBean);
//将修改后的用户保存到session中
request.getSession().setAttribute("userBean", userBean);

return "updatePhoto";
}
}

裁剪图片工具类:ImageCut.java

public class ImageCut {

/**
* 图片切割
* @param imagePath 原图地址
* @param x 目标切片坐标 X轴起点
* @param y 目标切片坐标 Y轴起点
* @param w 目标切片 宽度
* @param h 目标切片 高度
*/
public void cutImage(String imagePath, int x ,int y ,int w,int h){
try {
Image img;
ImageFilter cropFilter;
// 读取源图像
BufferedImage bi = ImageIO.read(new File(imagePath));
int srcWidth = bi.getWidth(); // 源图宽度
int srcHeight = bi.getHeight(); // 源图高度

//若原图大小大于切片大小,则进行切割
if (srcWidth >= w && srcHeight >= h) {
Image image = bi.getScaledInstance(srcWidth, srcHeight,Image.SCALE_DEFAULT);

int x1 = x*srcWidth/400;
int y1 = y*srcHeight/270;
int w1 = w*srcWidth/400;
int h1 = h*srcHeight/270;

cropFilter = new CropImageFilter(x1, y1, w1, h1);
img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(w1, h1,BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(img, 0, 0, null); // 绘制缩小后的图
g.dispose();
// 输出为文件
ImageIO.write(tag, “JPEG”, new File(imagePath));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
[/js]

    效果图:

        点击确认后,就会在指定路径下生成相应的图片:

        OVER!!

每一个不曾起舞的日子,都是对未来的一种辜负.

[转载]若干个游戏辅助的分析手记(三) - 漓江里的大虾 - 博客园

mikel阅读(930)

[转载]若干个游戏辅助的分析手记(三) – 漓江里的大虾 – 博客园.

若干个游戏辅助的分析手记(三)

—-飞五棋牌平台幸运骰子自动投注

目标进程进程名(幸运骰子):Sicbo.exe

  OK,开工。
进入游戏后,用OD附加,随后设断点:bp send,然后下注。没有断下,依次断WSASend等函数都断不下来。此路不通,祭出CE。
CE搜索投注额,最后定为到下面代码:

0040303A - 89 18  - mov [eax],ebx // 修改了自己在东风的押注数额


先走马观花逛 一圈,一路回溯会先经过GamePubl.dll模块,最后会回溯到ProcMsg模块。在ProcMsg模块中是一个大的消息循环。这几个模块的相关 CALL浪费了分析了整整一个通宵的时间,=_=!最后终于搞明白了它的大体框架,但是让我崩溃的是—-随后得出的结论这几个CALL仅仅是根据消息 更新界面(即相当于CE搜到的是界面的”Text”),所以得另寻方法。

  哎,一晚上的体力活没想最后成了杨白劳。睡一觉起来再说。

  zzZZZ.。o0O…

  一觉起来顿觉神清气爽。昨晚运气不佳,浪费了一个通宵。这活不但要看体力,还有一部分运气也是非常重要的。好了,不灰心。继续开工。

  整理下思路—-经过昨天一个晚上的分析,既然知道了上面是根据消息来更新界面,那么得找到消息的源头。在OD中对Sicbo.exe的主窗口下WM_LBUTTONUP消息断点,断下来后,返回程序领空,来到下面代码:

复制代码
10011AC0 >|$  53 PUSH EBX 10011AC1 |.  55 PUSH EBP 10011AC2 |.  56 PUSH ESI 10011AC3 |. 8BF1 MOV ESI,ECX 10011AC5 |.  8B86 08020000 MOV EAX,DWORD PTR DS:[ESI+208] 10011ACB |. 33DB XOR EBX,EBX 10011ACD |. 85C0 TEST EAX,EAX 10011ACF |.  57 PUSH EDI 10011AD0 |.  0F8E 95000000 JLE GamePubl.10011B6B 10011AD6 |.  8B6C24 1C     MOV EBP,DWORD PTR SS:[ESP+1C] 10011ADA |.  8D9B 00000000 LEA EBX,DWORD PTR DS:[EBX] 10011AE0 |>  85DB          /TEST EBX,EBX 10011AE2 |.  0F8C 9E000000 |JL GamePubl.10011B86 10011AE8 |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011AEE |.  0F8D 92000000 |JGE GamePubl.10011B86 10011AF4 |.  8B86 FC010000 |MOV EAX,DWORD PTR DS:[ESI+1FC] 10011AFA |.  8B0C98        |MOV ECX,DWORD PTR DS:[EAX+EBX*4] 10011AFD |.  8A41 56       |MOV AL,BYTE PTR DS:[ECX+56] 10011B00 |.  84C0          |TEST AL,AL 10011B02 |.  8D3C9D 000000>|LEA EDI,DWORD PTR DS:[EBX*4] 10011B09 |.  74 51         |JE SHORT GamePubl.10011B5C 10011B0B |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011B11 |.  7D 73         |JGE SHORT GamePubl.10011B86 10011B13 |.  8B96 FC010000 |MOV EDX,DWORD PTR DS:[ESI+1FC] 10011B19 |.  8B043A        |MOV EAX,DWORD PTR DS:[EDX+EDI] 10011B1C |.  8A48 55       |MOV CL,BYTE PTR DS:[EAX+55] 10011B1F |.  84C9          |TEST CL,CL 10011B21 |.  74 39         |JE SHORT GamePubl.10011B5C 10011B23 |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011B29 |.  7D 5B         |JGE SHORT GamePubl.10011B86 10011B2B |.  8BC2          |MOV EAX,EDX 10011B2D |.  8B0C38        |MOV ECX,DWORD PTR DS:[EAX+EDI] 10011B30 |.  03C7          |ADD EAX,EDI 10011B32 |.  E8 39F7FEFF   |CALL GamePubl.?GetVisible@CCardCtrl@@QAE> 10011B37 |.  84C0          |TEST AL,AL 10011B39 |.  74 21         |JE SHORT GamePubl.10011B5C 10011B3B |.  3B9E 08020000 |CMP EBX,DWORD PTR DS:[ESI+208] 10011B41 |.  7D 43         |JGE SHORT GamePubl.10011B86 10011B43 |.  8B4C24 18     |MOV ECX,DWORD PTR SS:[ESP+18] 10011B47 |.  8B5424 14     |MOV EDX,DWORD PTR SS:[ESP+14] 10011B4B |.  8B86 FC010000 |MOV EAX,DWORD PTR DS:[ESI+1FC] 10011B51 |.  55            |PUSH EBP 10011B52 |.  51            |PUSH ECX 10011B53 |.  8B0C38        |MOV ECX,DWORD PTR DS:[EAX+EDI] 10011B56 |.  52            |PUSH EDX 10011B57 |.  E8 C426FFFF   |CALL GamePubl.?OnLButtonUp@CCardCtrl@@QA> 10011B5C |>  8B86 08020000 |MOV EAX,DWORD PTR DS:[ESI+208] 10011B62 |.  43            |INC EBX 10011B63 |.  3BD8          |CMP EBX,EAX 10011B65 |.^ 0F8C 75FFFFFF \JL GamePubl.10011AE0 10011B6B |> 8BCE MOV ECX,ESI 10011B6D |.  E8 CC430000   CALL <JMP.&MFC71.#1903_?Default@CWnd@@IAE> 10011B72 |.  C686 28020000>MOV BYTE PTR DS:[ESI+228],0;返回程序领空后回到这里       10011B79  |.  FF15 C4D80110 CALL DWORD PTR DS:[<&USER32.ReleaseCaptur> [ReleaseCapture 10011B7F |. 5F POP EDI 10011B80 |. 5E POP ESI 10011B81 |. 5D POP EBP 10011B82 |. 5B POP EBX 10011B83 |.  C2 0C00       RETN 0C
复制代码

回到程序领空的时候是停在了VA:10011B72,下面开始分析。
因为真正的投注(网络操作)跟用户界面更新是分开的,所以为了方便分析,要得找到网络操作跟用户界面更新的分界线,因为bp send行不通,所以换个方法。
打开虚拟机,在虚拟机中用另一个账号登陆游戏后申请坐庄,接着回到本机。
在上面的函数的开头F2下断,即下面这句代码下断:

10011AC0 >|$  53            PUSH EBX

在本机投注后,程序断了下来,接着观察虚拟机中发现已经有了投注信息,说明网络操作的代码不在这个代码段中。OK,回溯到上一层,继续上面的思路。
下面代码是上一层的代码:

复制代码
00409670   .  53 PUSH EBX 00409671   .  8B1D A4E04000 MOV EBX,DWORD PTR DS:[<&GDI32.PtInRegion>>; GDI32.PtInRegion 00409677   .  55 PUSH EBP 00409678   .  8B6C24 14     MOV EBP,DWORD PTR SS:[ESP+14] ; 第三个参数 Y 0040967C . 56 PUSH ESI 0040967D . 57 PUSH EDI 0040967E . 8B7C24 18     MOV EDI,DWORD PTR SS:[ESP+18] ; 第二个参数 X 00409682 . 8BF1 MOV ESI,ECX 00409684 . 8BCD MOV ECX,EBP 00409686   .  51            PUSH ECX                                  ; /Y 00409687   .  8BC7          MOV EAX,EDI                               ; |
00409689   .  50            PUSH EAX                                  ; |X 0040968A . 8B86 CC040000 MOV EAX,DWORD PTR DS:[ESI+4CC]            ; |
00409690   .  50            PUSH EAX                                  ; |hRegion 00409691 . FFD3 CALL EBX ; \PtInRegion 00409693 . 85C0 TEST EAX,EAX 00409695 . 0F84 9F000000 JE Sicbo.0040973A 0040969B . 80BE 02060000>CMP BYTE PTR DS:[ESI+602],1 004096A2 . 0F85 92000000 JNZ Sicbo.0040973A 004096A8 . 80BE 01060000>CMP BYTE PTR DS:[ESI+601],1 004096AF . 0F85 85000000 JNZ Sicbo.0040973A 004096B5 . 8BCD MOV ECX,EBP 004096B7 . 51            PUSH ECX                                  ; /Y 004096B8 . 8B8E AC040000 MOV ECX,DWORD PTR DS:[ESI+4AC]            ; | 004096BE . 8BC7 MOV EAX,EDI ; | 004096C0 . 50            PUSH EAX                                  ; |X 004096C1 . 51            PUSH ECX                                  ; |hRegion 004096C2 . FFD3 CALL EBX ; \PtInRegion 004096C4 . 85C0 TEST EAX,EAX 004096C6 . 74 04 JE SHORT Sicbo.004096CC 004096C8 . B3 01         MOV BL,1 004096CA . EB 3F JMP SHORT Sicbo.0040970B 004096CC >  8B96 B4040000 MOV EDX,DWORD PTR DS:[ESI+4B4] 004096D2 . 8BCD MOV ECX,EBP 004096D4 . 51 PUSH ECX 004096D5 . 8BC7 MOV EAX,EDI 004096D7 . 50 PUSH EAX 004096D8 . 52 PUSH EDX 004096D9 . FFD3 CALL EBX 004096DB . 85C0 TEST EAX,EAX 004096DD . 74 04 JE SHORT Sicbo.004096E3 004096DF . B3 02         MOV BL,2 004096E1 . EB 28 JMP SHORT Sicbo.0040970B 004096E3 >  55 PUSH EBP 004096E4 . 57 PUSH EDI 004096E5 . 8D8E B8040000 LEA ECX,DWORD PTR DS:[ESI+4B8] 004096EB . E8 C0D6FFFF CALL Sicbo.00406DB0 004096F0 . 85C0 TEST EAX,EAX 004096F2 . 74 04 JE SHORT Sicbo.004096F8 004096F4 . B3 03         MOV BL,3 004096F6 . EB 13 JMP SHORT Sicbo.0040970B 004096F8 >  55 PUSH EBP 004096F9 . 57 PUSH EDI 004096FA . 8D8E C0040000 LEA ECX,DWORD PTR DS:[ESI+4C0] 00409700 . E8 ABD6FFFF CALL Sicbo.00406DB0 00409705 . 85C0 TEST EAX,EAX 00409707   .  74 31 JE SHORT Sicbo.0040973A 00409709   .  B3 04         MOV BL,4 0040970B >  E8 12260000   CALL <JMP.&MFC71.#1091_?AfxGetThread@@YGP>
00409710 . 85C0 TEST EAX,EAX 00409712   .  74 09 JE SHORT Sicbo.0040971D 00409714 . 8B10 MOV EDX,DWORD PTR DS:[EAX] 00409716 . 8BC8 MOV ECX,EAX 00409718   .  FF52 7C       CALL DWORD PTR DS:[EDX+7C] 0040971B . EB 02 JMP SHORT Sicbo.0040971F 0040971D > 33C0 XOR EAX,EAX 0040971F >  0FB696 550800>MOVZX EDX,BYTE PTR DS:[ESI+855] 00409726   .  8B40 20       MOV EAX,DWORD PTR DS:[EAX+20] 00409729 . 0FB6CB MOVZX ECX,BL 0040972C . 51            PUSH ECX                                  ; /lParam 0040972D . 52            PUSH EDX                                  ; |wParam 0040972E . 68 C9040000   PUSH 4C9                                  ; |Message = MSG(4C9) 00409733   .  50            PUSH EAX                                  ; |hWnd 00409734   .  FF15 34E54000 CALL DWORD PTR DS:[<&USER32.SendMessageA>>; \这里是下注关键 0040973A >  8B4C24 14     MOV ECX,DWORD PTR SS:[ESP+14] 0040973E . 55 PUSH EBP 0040973F . 57 PUSH EDI 00409740   .  51 PUSH ECX 00409741 . 8BCE MOV ECX,ESI 00409743   .  C686 02060000>MOV BYTE PTR DS:[ESI+602],0 0040974A . FF15 CCE04000 CALL DWORD PTR DS:[<&GamePublic.?OnLButto>;  GamePubl.?OnLButtonUp@CGameFrameView@@QAEXIVCPoint@@@Z 00409750 . 5F POP EDI 00409751 . 5E POP ESI 00409752 . 5D POP EBP 00409753 . 5B POP EBX 00409754   .  C2 0C00       RETN 0C
复制代码

  按照上面的思路,依然在函数头下断,投注后程序断了下来,观察虚拟机中此次并没有投注信息,很好!这段代码就是关键了!
重点分析此段代码。
首先分析该函数的参数,这个函数一共有3个参数,第一个参数是什么没去分析了这个不是重点。重点是第二个参数代表X坐标,第三个参数是Y坐标。
函数开头的代码是把XY的坐标传进PtInRegion来判断投注区。随后对XY坐标进行转化成相应投注区的索引。
继续往下看代码发现了这里:

复制代码
0040972C   .  51            PUSH ECX                                  ; /lParam 0040972D . 52            PUSH EDX                                  ; |wParam 0040972E . 68 C9040000   PUSH 4C9                                  ; |Message = MSG(4C9) 00409733   .  50            PUSH EAX                                  ; |hWnd 00409734   .  FF15 34E54000 CALL DWORD PTR DS:[<&USER32.SendMessageA>>; \这里是下注关键
复制代码

  

  这里的代码非常非常可疑,因为这里是一个SendMessage的调用,还记得刚才说要找界面更新的消息源头么?为了验证这个 SendMessage,在 00409734   .  FF15 34E54000 CALL DWORD PTR DS:[<&USER32.SendMessageA>> 这句代码下断,游戏投注断了下来,观察虚拟机中没有投注信息,回到本机单步执行该条语句,再次回到虚拟中发现了投注信息,说明这里就是下注的关键了!已经 离成功很近了。

下面着重分析SendMessage的参数 首先是HWND,该句柄通过SPY++查找,发现该句柄就是游戏本身主窗口的句柄了, 接着是消息号是4C9,很明显这是一个自定义的消息。 WPARAM 是投注筹码的索引, LPARAM 是投注区域的索引。 OK了,参数分析完毕。如下:

复制代码
SendMessage( HWND hWnd, // 游戏本身主窗口的句柄 
 UINT Msg, // 4C9,自定义的消息 
 WPARAM wParam, // 投注筹码的索引,2是1000,3是10000以此类推
 LPARAM lParam // 投注区域的索引,1是东,2是南以此类推
 );
复制代码

那么游戏本身实现投注就是靠SendMessage来传递一个自定义的消息,而该自定义的消息处理函数内部进行了什么处理在这里不用去分析已经 可以得出结论:该自定义消息处理函数内部进行投注的网络操作,网络操作完毕后,随后再次发送一个自定义消息来更新界面,这里就解释了第一天通宵的时候跟的 那几个CALL是在一个消息循环里的原因了。 所以我们不必去分析该自定义的消息处理函数了,因为我们也可以直接向该窗口发送此消息就OK。因为SendMessage是跨进程的,这样做没有任何问 题。 有了上面的参数分析,非常容易就写出投注的测试程序。这里提一下一点比较有意思的事情是,投注额的索引是从2开始,2代表1000.我们可以传递“1”。 这样虽然在游戏中没有100元的筹码,但是我们可以投注100.呵呵,有意思。

至此,投注的功能已经搞定。

后记:投注功能写好后,就是根据客户提供的“赌徒算法”,进行挂机自动投注了。测试了几次,短时间(连续挂一两个小时)收益还不错。但是长时间比如每天睡觉的时候挂机,起床的时候,发现有赢有亏,看来“赌徒算法”有待改进,哈哈。

[转载]SQL SERVER 查询性能优化——分析事务与锁(一) - DotNet菜园 - 博客园

mikel阅读(1101)

[转载]SQL SERVER 查询性能优化——分析事务与锁(一) – DotNet菜园 – 博客园.

一、使用工具观察与分析数据库中锁信息

      对于并发系统、对于有大量读写数据库操作的业务系统等,当多人同时访问数据库时,最复杂的情况之一就是大量的事务与资源纠缠在一起,相互被锁而彼此等待, 也称为死锁。当数据库中出现很多很多锁时,系统瞬间就无法提供正常服务。此时观察系统资源的使用情况,会发现CPU使用率不高,内存占用量也不高,还有很多未使用的内存,网络带宽也充足,硬盘也不繁忙,通过数据库管理工具查询的话,SQL SERVER中的数据也正常无误,但是使用系统的用户访问此数据库时却要需要等很多久很久,更多的就出现连接超时,数据库无响应。

      此现象类似高速公路上有大量的车辆进入,而在收费口却只开了一个、二个。造成收费口的车辆拥堵,而后方却又有大量的车辆涌入,这时所有的车辆都会慢下来,驾驶员彼此再减速刹车互相等待,最后导致后方的车子会被迫停下来,形成高速公路停车场。(就如第一次节假日高速公路免费通行那天,即2012年国庆高速公路大堵车的情形,高速公路成停车场)

      如果数据库中在设计时,设计不当,设计有过热数据表时,也就是所有应用程序的访问行为都跟某张数据表有关,甚至集中访问某些字段,这就像前述例子中的所有 车辆都在同一地点(高速公路收费口)驶上高速公路。这时耗时与快速的两种访问行为混合在一起,就像大货车与小轿车一起争车道,很容易发生彼此锁定,形成死 锁而动弹不得的情况。

      此类性能延迟现象,尤其在数据使用一段较长时间之后容易发生,当新系统刚上线时,或SQL语句写的不好,如Select * from SaleInfo 且没有where条件,初期因数据量不大,可以瞬间取回结果而不影响别人。但当系统使用一段时间之后,数据库中的数量增加,使用人数增多,使用方式改变。不好的SQL语句就出现运行迟缓,这时数据库开始积聚等待处理的需求,也就是彼此干扰而造成锁定,交互作用后,可能瞬间瘫痪整个系统。当你观察当前数据库的锁定时,可能会发现存在几千个锁定,很难理出头绪。

       本文首先来讨论可以使用哪些工具来观察与分析SQL SERVER中的锁信息。当然,所有的性能调校都需要先了解情况,最好是可重复发生的情景,例如在每天什么时间,多少用户上线后,执行了什么样的功能后,等等。先通盘了解情况,然后评估SQL SERVER的问题,再进一步深入观察SQL SERVER

第一步,使用SQL SERVER提供图形界面工具观察哪些进程被加锁

 1 ) SQL SERVER 2005可以通过Microsoft SQL Server Management Studio的“管理–活动监视器”》“右键”》“进程信息”,这里呈现连接、锁定的各种细节。如下图1。

 

                                                                             图1

2) SQL SERVER 2005 的Microsoft SQL Server Management Studio提供图形界面工具中的信息不会自动更新。刷新“活动监视器”界面中的内容,需要手动使用鼠标点击上方工具栏上的“刷新”按钮。如下图2,是未点击“刷新”按钮之前的显示的内容,图3,是点击“刷新”按钮之后的,显示的内容。

        图2

        图3

2) SQL SERVER 2005中可以通过下图中的设置,设置成自动更新的时间周期。如下图4。

 

 

                               图4

4) SQL SERVER 2005Microsoft SQL Server Management Studio中的“活动监视器》进程信息”提供相当多的信息,其中“等待类型”与“阻塞者”可呈现进程彼此的关系及进程的状态信息。

如果要查询“锁”的细节信息,则可以点击右键窗口中的“活动监视器按进程分类的锁”及“活动监视器按对象分类的锁”两个标记进行观察。

活动监视器按进程分类的锁

如下图5。

 

                                               图5

活动监视器按对象分类的锁

如下图6。

 

                                                图6

 

接下来说一下SQL SERVER 2008中的活动监视器的使用:

1) SQL SERVER 2008活动监视器就不在管理中了。一、点击工具栏上的图标。二、使用右键点击数据库服务器,如下图7。然后都会出现如图8的图形界面工具。

 

 

                图7

 

                       图8

 

2) SQL SERVER 2008中默认就是自动更新的,自动更新时间间隔为10秒。可以通过下图中的设置,设置成自动更新的时间周期。如下图9。右键单击“概述,选择“刷新间隔,然后选择活动监视器获取新的实例信息所用的间隔。

 

 

          图9

 

3) SQL SERVER 2008Microsoft SQL Server Management Studio中的“活动监视器–》进程”只提供了一些基本的信息,其中“等待类型”与“阻塞者”可呈现进程彼此的关系及进程的状态信息。所提供的锁定进程的信息没有SQL SERVER 2005中所提供的详细。

                                      图10

[转载]编码的奥秘摩尔斯电码 - 许宏川 - 博客园

mikel阅读(940)

[转载]【编码的奥秘】摩尔斯电码 – 许宏川 – 博客园.

所谓编码就是用某种方法来替代文字,而交流的双方都熟知这种替代规则便可以在特殊情景下交流了。

摩尔斯电码就是其中一种。

 

摩尔斯电码(Morse alphabet)是美国人摩尔斯(Samuel Finley Breese Morse)于1837年发明的。

是一种用二进制(点和划)组合来表示文字的编码规则。

规则是:

用1~4位的二进制组合来表示26个英文字母,因为4位二进组组合一共可以表示2+4+8+16=30个符号,足够表示26个英文字母。

然后用5位的二进制组合来代表数字

然后再用6位的二进制组合表示标点符号

这样只要双方都熟记以上规则便可以用摩尔斯编码进行交流了。(当然还有其它语言的编码表,但是作为了解并不需要知道那么多)

例如可以用手电筒,手电筒闪烁一下代表点,亮一会代表划,点划之间要停顿一下,因为一个字母由一串电话组成,一个单词由一串字母组成,一个句子由一串单词组成,那么就规定停顿时间是单词>字母>点划就行了。

如果交流的双方之间有遮挡物,光线无法通过,那么也可以用某种能发出两种(例如嘀嗒)声音的工具,例如嘀代表点,嗒代表划。

但是光线和声音都只能是短距离交流,如果想长距离使用交流那就是使用电报机了。

原理是短脉冲信号电表点,长脉冲信号代表划。

另外为了提高传递速度,把短位用于常用字母,例如e、t等,还使用了一些缩写,例如u代表you,hv代表have,vy代表very等等。

注:摩尔斯电码只是提供了一种编码的思想,并不是全世界统一使用以上规定的编码表,特别是军事上,为了保守秘密,每个军方可以有自己的编码表,甚至会不定期更换。

[转载]HTTP 长连接 - lengyuhong - 博客园

mikel阅读(933)

[转载]HTTP 长连接 – lengyuhong – 博客园.

HTTP 长连接

上周再做一个easyproxy的小项目,实现代码反向代理的工作,具体就是在tcp层对各个请求(只要遵循建立在tcp层之上的协议即可)进行解析,然后分发各个具体服务上。

这中间遇到的一个问题就是HTTP中的长连接问题,重新去看了下具体的http协议,发现之前对这块知识还是存在盲点。

这篇文章可以算是自己的学习笔记, 很多内容更是直接使用我看到和觉得讲得不错的资料,希望对大家也有所帮助。

基础知识

 1. 名称

维基百科中的介绍:

“HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair.”

讲这个,是因为我觉得“长连接”这个称呼并不是非常准确,用“持久连接”,而“Long Connection”这种直接翻译词就更不用再用了。但毕竟这个名字已经沿用这么久了,我们也就继续用这个名

2. 原理

在HTTP协议中长连接中同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。

下图是网上一张很形象讲解普通连接和长连接区别的图

从图中,我们就能清晰地看到,两者区别就是长连接会“复用”原先建立TCP连接

3. 实现

在 HTTP 1.0 中

没有官方的 keepalive 的操作。通常是在现有协议上添加一个指数。如果浏览器支持 keep-alive,它会在请求的包头中添加:

Connection: Keep-Alive

然后当服务器收到请求,作出回应的时候,它也添加一个头在响应中:

Connection: Keep-Alive

这样做,连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接。这一直继续到客户端或服务器端认为会话已经结束,其中一方中断连接。

比如我用chrome访问博客园首页

request header:

response header:

在 HTTP 1.1 中 

所有的连接默认都是持续连接,除非特殊声明不支持。

所以现在各个浏览器的高版本基本都是支持长连接。

4. 优势

  • 较少的CPU和内存的使用(由于同时打开的连接的减少了)
  • 允许请求和应答的HTTP pipelining
  • 降低网络阻塞 (TCP连接减少了)
  • 减少了后续请求的延迟(无需再进行握手)
  • 报告错误无需关闭TCP连接

其实它一切的优势就在于复用了原先建立的TCP连接,减少重新建立TCP连接的消耗

5. 劣势

  • 空闲的连接需要过段时间后才能被断开,可能影响整体性能(比如那些单次访问次数多的web 服务)

6. 浏览器

现在的高版本浏览器都是默认支持长连接的,而它们维护长连接的方式就是用“超时管理”。

在一段时间内没有请求和相应时,就自动将连接断掉。

参考文章:

维基百科介绍:英文

Persistent HTTP Connections in RFC 2616 “Hypertext Transfer Protocol — HTTP/1.1

[转载]jQuery获取CSS样式中的颜色值的问题,不同浏览器格式不同怎么办 - 八戒Done - 博客园

mikel阅读(812)

[转载]jQuery获取CSS样式中的颜色值的问题,不同浏览器格式不同怎么办 – 八戒Done – 博客园.

 使用JQuery获取样式中的background-color的值时发现在获取到的颜色值在IE10以下版本中是以HEX格式显示【#ffff00】,而IE10,、Chrome、Firefox中则是以GRB格式显示【rgb(255,0,0)】,由于需要对颜色值进行判断处理,所以需要得到统一的颜色格式,最好是HEX格式的,方便处理点。搜索了一下,从国外的一个网站上得到一段代码:

1
2
3
4
5
6
$.fn.getHexBackgroundColor = function() {
    var rgb = $(this).css('background-color');
    rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
    function hex(x) {return ("0" + parseInt(x).toString(16)).slice(-2);}
    return rgb= "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}

上面定义的是一个JQuery函数,我们可以通过 $(“#bg”).getHexBackgroundColor(); 获取到标签id=”bg”的background-color的RGB值。

下面做一下小小的修改,就是加一个判断,如果是显示HEX值(IE10以下)就直接拿值,如果是非IE浏览器则将值转换成RGB格式:

复制代码
 1 $.fn.getBackgroundColor = function() {  2     var rgb = $(this).css('background-color');  3     if(rgb >= 0) return rgb;//如果是一个hex值则直接返回
 4    else{  5         rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);  6         function hex(x) {return ("0" + parseInt(x).toString(16)).slice(-2);}  7         rgb= "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);  8  }  9     return rgb; 10 }
复制代码

[转载]瀚海拾贝(一)HTTP协议/IIS 原理及ASP.NET运行机制浅析【图解】 - wenthink - 博客园

mikel阅读(623)

转载瀚海拾贝(一)HTTP协议/IIS 原理及ASP.NET运行机制浅析【图解】 – wenthink – 博客园.

前言

前一段在整理邮件的时候发现几年前和CDD老师交流时的一份邮件.下面是简单摘要:

“从技术角度来说,无论哪一个阵营,跟新技术都是不可避免的,也是很累的,当然作 为一个程序员来说,也是必须的。要想让技术的更新对自己的影响减小,基础就必须打牢。所以,底层的东西和抽象层的东西需要下一番功夫。因为说到底,无论什 么技术,无非就是架构和最终的实现,技术框架只是应用开发的一个平台一种技术,如果了解了具体的东西,技术更新对你来说就没什么影响了,或者换句话说,你 要学一种新的技术,速度和效率会非常之高。”

上面一段话对自己的影响很大,可能大家在踏入“程序人生”的时候都会存在一些迷茫和彷徨。尽管我是属于那种相当热爱Proramming的一份子,但是面对万花筒般的技术分支也曾徘徊犹豫过.徘徊之余要做的事情便是夯实基础,寻找自己的兴趣与方向.对技术的迭代,以不变应万变才是王道.

正因为如此,所以也不会存在银弹之说.如果真的有银弹的话那么我信奉的是:程序=数据结构+算法

我选择的方向是Web,也相信Web终究会是互联网的未来.这篇文章简单谈一下自己对.NET平台下Web基础的一些浅解,由于自己水平有限,不足之处烦请见谅.

HTTP协议

HTTP协议是浏览器和服务器双方共同遵循的规范.是一种基于TCP/IP(传输层协议,相对应的有UDP)的”应用层协议”

PS:TCP/UDP是广泛使用的网络通信协议,UDP协议具有不可靠性和不安全性,

相对来说TCP协议是基于连接和三次握手的(相对可靠与安全),然而B/S架构的网站,由于同时在线的人数会很多,如果都与服务器保持连接状态.服务器的承载是相当大的,

因而衍生出HTTP协议.简单的说:请求发起之后服务器端立刻关闭连接并释放资源.也正因为如此,HTTP协议通常被理解为”无状态”的.

当然维系”状态”的手段有很多;如 Session/Cookie等 这里暂且不多做讨论.

先来看一下典型的OSI七层模型 图解

OSI2

HTTP最通俗的理解 请求/响应.

图示:

Http

HTTP报文信息

HTTP Request Header

HttpRequest

HTTP Response Header

HttpRespond

当然,也可以通过设置改变浏览器的选项.这里不做详细说明.不清楚的可以Google.微笑

给出ASP.NET下添加P3P头信息的例子

HttpContext.Current.Response.AddHeader(“p3p”, “CP=\”IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\””); 

有兴趣详细了解的可以参考 MSDN 中关于部署 P3P的文章。

 

下面是老生常谈的内容了(熟悉的朋友,自行跳过,权当温习下了 : )   )

请求头(消息头)包含(客户机请求的服务器主机名,客户机的环境信息等):
Accept:用于告诉服务器,客户机支持的数据类型  (例如:Accept:text/html,image/*)
Accept-Charset:用于告诉服务器,客户机采用的编码格式
Accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式
Accept-Language:客户机语言环境
Host:客户机通过这个服务器,想访问的主机名
If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间
Referer:客户机通过这个头告诉服务器,它(客户端)是从哪个资源来访问服务器的(防盗链)
User-Agent:客户机通过这个头告诉服务器,客户机的软件环境(操作系统,浏览器版本等)
Cookie:客户机通过这个头,将Coockie信息带给服务器
Connection:告诉服务器,请求完成后,是否保持连接
Date:告诉服务器,当前请求的时间

一个http响应代表服务器端向客户端回送的数据,它包括:
一个状态行,若干个响应消息头,以及实体内容
状态行:  例如:  HTTP/1.1  200 OK   (协议的版本号是1.1  响应状态码为200  响应结果为 OK)

响应头(消息头)包含:
Location:这个头配合302状态吗,用于告诉客户端找谁
Server:服务器通过这个头,告诉浏览器服务器的类型
Content-Encoding:告诉浏览器,服务器的数据压缩格式
Content-Length:告诉浏览器,回送数据的长度
Content-Type:告诉浏览器,回送数据的类型
Last-Modified:告诉浏览器当前资源缓存时间
Refresh:告诉浏览器,隔多长时间刷新
Content- Disposition:告诉浏览器以下载的方式打开数据。例如: context.Response.AddHeader(“Content-Disposition”,”attachment:filename=icon.jpg”);                                        context.Response.WriteFile(“icon.jpg”);
Transfer-Encoding:告诉浏览器,传送数据的编码格式
ETag:缓存相关的头(可以做到实时更新)
Expries:告诉浏览器回送的资源缓存多长时间。如果是-1或者0,表示不缓存
Cache-Control:控制浏览器不要缓存数据   no-cache
Pragma:控制浏览器不要缓存数据          no-cache
Connection:响应完成后,是否断开连接。  close/Keep-Alive
Date:告诉浏览器,服务器响应时间

IIS运行过程

有了上面的HTTP协议的知识回顾,下面来让我们看下IIS是怎样工作的?

IIS 5.X 已经距离我们很远了.好吧 XP默认的好像是的… 为万恶的IE6 默哀下0.0  .

这里我们来看一下IIS 6 的图示

IIS6-3

 

根据上图简单分析下IIS6的运行过程

在 User Mode 下,http.sys 接收到 http request,然后它会根据 IIS 中的 Metabase 查看基于该 Request 的 Application 属于哪个 Application Pool, 如果该 Application Pool 不存在,则创建之。否则直接将 request 发到对应 Application Pool 的 Queue中。

每个 Application Pool 对应着一个 Worker Process — w3wp.exe,(运行在 User Mode 下)。在 IIS Metabase 中维护着 Application Pool 和 Worker Process 的Mapping。WAS(Web Administrative Service)根据这样一个 mapping,将存在于某个 Application Pool Queue 的 request 传递到对应的 Worker Process (如果没有,就创建这样一个进程)。在 Worker Process 初始化的时候,加载 ASP.NET ISAPI,ASP.NET ISAPI 进而加载 CLR。最后通过 AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的  ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。

PS几个知识点:

  1. HTTP.SYS:(Kernel)的一个 组件,它负责侦听(Listen)来自于外部的HTTP请求,根据请求的URL将其转发给相应的应用程序池 (Application Pool)。当此HTTP请求处理完成时,它又负责将处理结果发送出去.为了提供更好的性能,HTTP.SYS内部建立了一个缓冲区,将最近的HTTP请 求处理结果保存起来。
  2. Application Pool:  IIS总会保持一个单独的工作进程:应用程序池。所有的处理都发生在这个进程里,包括ISAPI dll的执行。对于IIS6而言,应用程序池是一个重大的改进,因为它们允许以更小的粒度控制一个指定进程的执行。你可以为每一个虚拟目录或者整个Web 站点配置应用程序池,这可以使你很容易的把每一个应用程序隔离到各自的进程里,这样就可以把它与运行在同一台机器上其他程序完全隔离。从Web处理的角度 看,如果一个进程死掉,至少它不会影响到其它的进程。
    当应用程序池接收到HTTP请求后,交由在此应用程序池中运行的工作者进程Worker Process: w3wp.exe来处理此HTTP请求。
  3. Worker Process: 当工作者进程接收到请求后,首先根据后缀找到并加载对应的ISAPI扩展 (如:aspx 对应的映射是aspnet_isapi.dll),工作者进程加载完aspnet_isapi.dll后,由aspnet_isapi.dll负责加载 ASP.NET应用程序的运行环境即CLR (.NET Runtime)。
    Worker Process运行在非托管环境,而.NET中的对象则运行在托管环境之上(CLR),它们之间的桥梁就是ISAPI扩展。
  4. WAS(Web Admin Service:这是一个监控程序,它一方面可以存取放在InetInfo元数据库(Metabase)中的各种信息,另一方面也负责监控应用程序池(Application Pool)中的工作者进程的工作状态况,必要时它会关闭一个老的工作者进程并创建一个新的取而代之。

再来看下网上对IIS7经典模式下的图解

IIS 7 应用程序池的托管管道模式“经典”模式也是这样的工作原理。这种模式是兼容 IIS 6 的方式, 以减少升级的成本。

2011101914255884

小插曲

场景假定:

截获客户端的请求,并对请求进行重写。在IIS6中,请求的截获动作只能被限制在IIS加载aspnet_isapi.dll后,也就是说:如果该请求不是明确针对asp.net资源的请求(比如这个请求只是一个静态文件的请求,如www.cnblogs.com/index.html, 这时我们就便不能在代码中编写截获请求的逻辑,因为IIS6是根据URL的后缀来映射并加载对应的isapi的,如果一个请求的url 是:www.cnblogs.com/index.aspx,根据”.aspx”这个后缀,IIS6可以得知这个请求是针对asp.net资源的,应该加 载aspnet_isapi.dll创建.net运行时并运行asp.net页面的代码,但很明显,诸 如”www.cnblogs.com/index.html”这种请求,IIS6通常认为不是对asp.net资源的请求,因此不会加载 aspnet_isapi.dll来运行asp.net,我们即使在asp.net页面中编写了拦截请求的代码,也不会被执行。当然,这里我说通常是有原 因的,因为我们可以在IIS6中添加通配符程序映射的方式,或者在web.config中对某种请求手动添加处理程序的方式,来迫使IIS6为非 asp.net资源类型的请求加载aspnet_isapi.dll。IIS6中对请求的执行流程如上.

咦,有木有人和我一样想到了URL Routing 和URL Rewriting ?

这里不做说明,大叔手记16传送门:http://www.cnblogs.com/TomXu/archive/2011/12/27/2303486.html

这个问题先放一下~~了解II7的集成模式也许可以有一些思绪 微笑

让我们再来看下IIS官网上对IIS7的图解

传送门 :http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture

introduction-to-iis-architecture-101-OverviewOfHTTPRequest

1、当客户端浏览器开始 HTTP 请求一个WEB 服务器的资源时,HTTP.sys 拦截到这个请求。

2、HTTP.sys 联系 WAS 获取配置信息。

3、WAS 向配置存储中心(applicationHost.config)请求配置信息。

4、WWW 服务接收到配置信息,配置信息指类似应用程序池配置信息,站点配置信息等等。

5、WWW 服务使用配置信息去配置 HTTP.sys 处理策略。

6、WAS为请求创建一个进程(如果不存在的话)

7、工作者进程处理请求并对HTTP.sys做出响应.

8、客户端接受到处理结果信息。

IIS  7 应用程序池的托管管道模式(集成模式)华丽的变身

IIS7ASP.NET

IIS7中对asp.net的请求不再是分两条处理管道,而是将asp.net和 IIS集成起来,这样做的好处是统一了请求验证工作,加强了asp.net对于请求的控制能力等等。在IIS7中,asp.net不再像IIS6一样只限 定于aspnet_isapi.dll中,而是被解放出来,从IIS接收到HTTP请求开始,即进入asp.net的控制范围,asp.net可以存在于一个请求在IIS中各个处理阶段。甚至可以为部署在IIS7中的PHP应用提供基于asp.net的验证身份验证功能(传送门:http://msdn.microsoft.com/zh-cn/magazine/cc135973.aspx)。

好吧,戛然而止一下,篇幅有限:IIS部分告一段落 留一些遐想空间.

再来分析ASP.NET的运行机制

ASP.NET运行机制

在IIS6图示中我们分析到“ AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的  ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。”

下面我们来看一下AppDomain运行过程图示

AppDomain-3

AppDomain的作用,相信大家都很了解了吧.这里简明扼要的写几点:

一个AppDomain中的代码创建的对象不能由另一个AppDomain中的代码直接访问(只能使用按引用封送或者按值封送,起到了很好的隔离作用).

AppDomain可以卸载 CLR不支持从AppDomain中卸载一个程序集的能力,但可以告诉CLR卸载一个AppDomain,从而达到卸载当前包含在该AppDomain内的所有程序集.

AppDomain 可以单独保护 当宿主加载一些代码之后,可以保证这些代码不会被破坏(或读取)宿主本身使用的一些重要的数据结构.

AppDomain可以单独配置 设置主要影响CLR在AppDomain中加载程序集的方式,涉及搜索路径、版本绑定重定向、卷影复制及加载器的优化。

由以上几点可以看出AppDomain确保了Windows系统及其中运行的应用程序的健壮性。AppDomain提供了保护、配置和终止其中每一个应用程序所需的隔离性。

再来看下ProcessRequest的过程

HttpApplication_Analysis

简单分析一下上图

ProcessRequest(HttpWorkerRequest wr)中判断wr是否为null,然后判断管线是否完整,再调用ProcessRequestNoDemand(wr)方法,

并判断当前RequestQueue 是否为null,接着计算等待时间并更新管线数 CalculateWaitTimeAndUpdatePerfCounter(wr);

重置wr开始时间wr.ResetStartTime();调用ProcessRequestNow(wr)方法,并调用ProcessRequestInternal(wr)方法

继续图例

ProcessRequestInternal

ProcessRequestInternal方法如下:

复制代码
 1  private void ProcessRequestInternal(HttpWorkerRequest wr)  2  {  3  HttpContext context;  4      try 
 5  {  6          context = new HttpContext(wr, false);//由HttpWorkerRequest生成HttpContext 
 7  }  8      catch 
 9  { 10  
11        //常见的400错误,就是在这里捕捉到滴 
12          wr.SendStatus(400, "Bad Request"); 13          wr.SendKnownResponseHeader(12, "text/html; charset=utf-8"); 14          byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>"); 15  wr.SendResponseFromMemory(bytes, bytes.Length); 16          wr.FlushResponse(true); 17  wr.EndOfRequest(); 18          return; 19  } 20      wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context); 21      Interlocked.Increment(ref this._activeRequestCount); 22  HostingEnvironment.IncrementBusyCount(); 23      try 
24  { 25          try 
26  { 27              this.EnsureFirstRequestInit(context); 28  } 29          catch 
30  { 31              if (!context.Request.IsDebuggingRequest) 32  { 33                  throw; 34  } 35  } 36  context.Response.InitResponseWriter(); 37          IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); 38  //得到HttpApplication 
39  
40  if (applicationInstance == null) 41  { 42              throw new HttpException(System.Web.SR.GetString("Unable_create_app_object")); 43  } 44          if (EtwTrace.IsTraceEnabled(5, 1)) 45  { 46              EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start"); 47  } 48          if (applicationInstance is IHttpAsyncHandler) 49  { 50              IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance; 51              context.AsyncAppHandler = handler2; 52              handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);//届时 HttpApplication处理请求 
53  } 54          else 
55  { 56  applicationInstance.ProcessRequest(context); 57              this.FinishRequest(context.WorkerRequest, context, null); 58  } 59  } 60      catch (Exception exception) 61  { 62  context.Response.InitResponseWriter(); 63          this.FinishRequest(wr, context, exception); 64  } 65  }
复制代码

再看下GetApplicationInstance(context) 实例化Application的方法

View Code

 

最后调用的GetNormalApplicationInstance方法中对当前空闲的application数目进行判断,调用

application.InitInternal(context, this._state, this._eventHandlerMethods)方法,

this.InitModules()初始化所有的Modules,包含用户自定义的HttpModules

this._stepManager.BuildSteps(this._resumeStepsWaitCallback);//管道事件序列

贴一下源码:

复制代码
 1 internal override void BuildSteps(WaitCallback stepCallback)  2 {  3     ArrayList steps = new ArrayList();  4     HttpApplication app = base._application;  5     bool flag = false;  6     UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;  7     flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);  8     steps.Add(new HttpApplication.ValidatePathExecutionStep(app));  9     if (flag) 10  { 11         steps.Add(new HttpApplication.UrlMappingsExecutionStep(app)); 12  } 13  app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps); 14  app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps); 15  app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps); 16  app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps); 17  app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps); 18  app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps); 19  app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps); 20  app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps); 21     steps.Add(new HttpApplication.MapHandlerExecutionStep(app)); 22  app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps); 23  app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps); 24  app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps); 25  app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps); 26     steps.Add(new HttpApplication.CallHandlerExecutionStep(app)); 27  app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps); 28  app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps); 29  app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps); 30     steps.Add(new HttpApplication.CallFilterExecutionStep(app)); 31  app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps); 32  app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps); 33     this._endRequestStepIndex = steps.Count; 34  app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps); 35     steps.Add(new HttpApplication.NoopExecutionStep()); 36     this._execSteps = new HttpApplication.IExecutionStep[steps.Count]; 37     steps.CopyTo(this._execSteps); 38     this._resumeStepsWaitCallback = stepCallback; 39 }
复制代码

到这里想必能够使大家对ASP.NET管道机制能够有一个简单的回顾.当然还有很多地方没有详细分析.

 

再来总结一下IIS运行过程及ASP.NET管道机制:

Request→ (Internet )  HTTP.sys 监听 → WAS (IIS6 web Admin Service /IIS7 (Windows Activation Service) 接收请求

→(传入)Application Pool’s → w3wp.exe(检查URL后缀)

→(加载)ISAPI扩展[aspnet_isapi.dll] → 注册映射

构造HttpRuntime类 →ProcessRequest方法

HttpContext实例产生(Request,Response,Session  and so on…)

HttpRuntime 调用 HttpApplicationFactory加载HttpApplication对象

穿越HttpModule到达HttpHandler

简单用140个字符(即一条微博的字数微笑)概括:

Request→ (Internet ) HTTP.sys →(WAS)→Application Pool’s → w3wp.exe→ISAPI→ Map→ (Pipeline)HttpWorkerRequest→AppDomain→HttpRuntime→ProcessRequest()→ HttpContext(Request,Response)→ HttpRuntime→HttpApplicationFactory→HttpApplication→ HttpModule→HttpHandler→EndRequest

以上为个人学习摘要,如有错误,欢迎指正!!

补充

1:刚刚看到dudu发的一个闪存,里面提到了Application pool 与 AppDomain 的区别 来自stackoverflow,希望对大家有所帮助.

2:WAS缩写在IIS6中的指的是(Web Admin Service),在IIS7中指的是(Windows Activation Service)  缩写一样.这个在写文章的时候注意到过,但是没有深入考虑. 理解不是很到位.  暂不妄下断言.  欢迎斧正!! 🙂

参考资料:

http://www.cnblogs.com/tenghoo/archive/2009/11/04/IIS_And_ASPNET_Http_Runtime_Pipeline.html

http://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.80).aspx

http://www.cnblogs.com/panchunting/archive/2013/04/11/ASPNET_Architecture.html

后续

由于一些个人原因,放弃了原本在无锡相对安逸的工作,踏上了北漂之路,也希望在大环境下能够得到更多的锻炼与打磨.

当然,最近也正在找工作,如果您有合适的职位,烦请介绍一下,感谢之至!!

如果你觉得本文对您有所帮助,请点击一下推荐,感谢您的阅读!!红玫瑰

作者:wenthink
出处:http://www.cnblogs.com/wenthink/archive/2013/05/06/HTTP_IIS_ASPNET_Pipeline.html
本文版权归【wenthink】和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
我们每个人都是梦想家,当离开家了,就只剩梦想了。。。 道路,或许坎坷,但你终会到达!!!

[转载]Jquery实现中国地图插件 - 漫画之迷 - 博客园

mikel阅读(871)

[转载]原创Jquery实现中国地图插件 – 漫画之迷 – 博客园.

,此Jquery插件是一款原创作品,实现中国地图提示特效,现在只是第一个版本,会不断的完善,插件使用非常灵活,引入了JQuery.adamMap.1.0库之后在页面初始化一下插件代码即可。

 

JQuery插件初始化代码如下:

复制代码
AMAP.making( { img : "AMAP",//图片id
        imgWidth : 385,//图片宽
        imgHeight : 305,//图片高
        points : aPoint,// 坐标列表 aPoint
        fill : "#44ccee",//填充颜色
        fillOpacity : .5,//透明度
        strokeFill : "#00AADD",//边框颜色
        strokeWidth : 1,//边框宽度
        event : "mouseover",//绑定事件 
        def : "重庆"//是默认城市 
 } );
复制代码

 

页面使用代码如下:

<!-- 地图功能结构 -->
<img src="map.jpg" height="305" width="385" id="AMAP" usemap="#Adam01" style="border:0;" />
<!-- 地图功能结构 结束 -->

 

效果如下:

网友Adam分享原创Jquery实现中国地图插件

 

在线演示

[转载]Asp.net MVC 插件式应用框架 - Mobwiz - 博客园

mikel阅读(1071)

[转载]Asp.net MVC 插件式应用框架 – Mobwiz – 博客园.

这几年来做了很多个网站系统,一直坚持使用ASP.NET mvc建站,每次都从头开始做Layout,CSS,做权限管理等等,有点恼火,将这几年的代码好好整理了一下,准备搭建一个框架。

一、目标

0)面向领域:网站后台管理系统、MIS等结构相对规范的管理类应用;

a)基于ASP.NET MVC 4搭建系统框架

b)集成RBAC基于角色的权限管理机制,能够控制到控件级别

c)集成一些基础服务:数据字典、日志记录等

d)使用AJAX特性(使用JQuery与MVC的Partiview等实现)

e)插件机制:框架搭建完毕之后,新开发的功能模块能够直接以插件的形式安装到系统框架中运行

f)使用EF Codefirst

二、系统设计

A)基于RBAC的权限管理系统

参考了园子里一些权限管理系统,使用了  模块->资源->操作 作为最基础的权限元素,[资源,操作]作为 基本的权限原子,将权限原子赋给“角色”

为了配合RBAC的使用,继承 AuthorizeAttribute 来修饰Action

B)菜单框架

此应用框架主要面向管理系统,所以导航菜单设计为两级,顶部导航为一级菜单,左侧 边为二级菜单,菜单能与权限进行关联,配置权限时可影响到菜单;如图所示,Menu2根据所选择Menu1发生变化;

C)Ajax框架

此应用框架中,Content Area为Ajax更新内容,使用JQuery的 $.get 操作访问 Action,在Action中返回 PartialView,然后更新到 ContentArea中;

全部使用Ajax来载入,带来一个问题,无法使用前进、后退、还有刷新等操作,借鉴网上一些资料,使用了 “jQuery.bbq.js”的插件,通过url的hashchange使系统能够在浏览器中保持状态,能够使用 “前进“,”后退“,”刷新“等操作;通过将真实的请求代码加入到URL”#”后,来载入所需要的内容,JS代码如下:

复制代码
$(window).bind('hashchange', function (e) { var hash = window.location.hash || ''; if (oldHash == '') { // 需要选择默认菜单
 } if (hash != '' && hash != oldHash) { if (hash == "#top") { //TODO, Load the index page!!!!
                    return; }  $('#contentPanel').html('<div class="wp"><img src="@Url.Content("~/Content/loading.gif")" border="0" /> &nbsp;载入中...</div>'); oldHash = hash; $.get(hash.substr(1, hash.length - 1), {}, function (data, status) { if (status == "success") {                         if (data.substr(0, 7) == '"logon"') { window.location = "/Home/Login"; } else { $('#contentPanel').html(data); } //}
                    } else { $('#contentPanel').html('error.. - ' + status); } }, "html"); } }); $(window).trigger('hashchange');
复制代码

 

在这样的框架中,只需要关注Content Area中的部分视图即可,在该Content Area中还可以使用Ajax + JSON的方式实现更多的功能;

表单的处理

由于采用了上面的AJAX机制,不能再使用以往的表单提交方式,在本框架中,使用下面几种方式:

1)使用  $.post(url, $(‘#form’).serialize(), callback, “html”) 的方式,将返回值更新到 Content Area中;

2)使用  $.post(url, {‘field1′,’value1’} , callback, “json”) 的方式,根据结果,再通过JavaScript进行页面跳转;

D)配套的若干Helper

ajaxMenu,ajaxPager等:由于使用了上面的机制来载入部分视图,所有的URL都要以 /Home#/Real/Url?para1=value1 的方式来请求,所以写了一个Helper来完成该功能;分页也需要做同样的处理。

E)IoC Container

本例使用了Unity Block作为IoC容器,无他,因为这个比较熟悉;同时将MVC与Unity Container做了整合;

F)表单验证

由于使用了AJAX框架,unobtrusive扩展的验证大部分失效,所以直接使用 jQuery.validate进行客户端验证

G)插件机制

定义IPlugin接口,其中有Initialize方法,在系统启动时进行初始化,例如初始化Unity Container,MapRoute等等;

插件的使用:直接将插件复制到框架中的插件目录,并将插件所需要使用的Views文件复制到框架中的Views目录中。(暂无更好的解决方案,就这样搞定。考虑复制到插件目录,重写MVC中的视图引擎的代码来实现在插件目录中查找视图)

工作原理:系统启动时,通过反射载入插件中的Dll,并查找IPlugin接口,调用其初始化方法,插件在Unity容器中注册所需要使用的控制器,这样框架中就可以调用到所需要的控制器以及视图;

插件格式:插件也采用ASP.NET MVC 4 Web APP来写,最后只使用编译出的DLL与部分必要的View视图,其他资源均使用框架中的资源;

三、系统实现

1)数据库:采用了EntityFramework(Version 5.0) – CodeFirst完成,使用Enable Migrations;

2)数据访问层:定义仓储模式,IRepository实现基本的增删查改功能,

3)数据服务层:根据需要进行定义;

4)Portal:采用了ASP.NET MVC 4,Razor视图引擎,JQuery,JQuery UI, JQuery.Validate, jquery.bbq, Jquery BlockUI 等;

5)为面向内容管理,集成了CkEditor与CkFinder

——————-

目前系统代码还比较乱,先发个截图

 

[转载]尝试MVP模式 - 猴健居士 - 博客园

mikel阅读(908)

[转载]尝试MVP模式 – 猴健居士 – 博客园.

对MVP模式的接触,是我偶然一次在百度上搜MVC的时候开始,当时对MVC都不了解,甭说MVP了。后来MVC弄懂了,现在就来了解一下MVP。

MVP 是从经典的模式MVC演变而来的,难怪看那个结构图有点相像。

MVC模式的结构图,M,V,C各代表什么不说了

MVP模式的结构图,M和V的含义跟MVC中的结构一样,区别的就是C(Controller)和P(Presenter)。感觉这个区别就导 致了模式产生性质的变化。至少从几何角度来看,由一个稳定的三角型变成一条直线。在MVC中即使在Controller对View和Model的控制之 下,View和Model之间仍然有联系,至少View上控件绑定的数据是与Model的某个字段有关的。不过在MVP中Presenter则把原本 MVC中View与Model的联系砍断了,View上面那个控件绑定什么数据它本身不知道,Presenter才知道。这样View只是负责呈现部分, 使得它的职责更单一了。再者Presenter不是调用View本身,而是调用一个由View实现的接口,这样使得View与Presenter的联系更 松散了。这么说来,整个MVP模式中的成员一共有四个

  •   View(视图):实现IVew接口,负责界面呈现。
  •   IView(视图接口):提供一些方法,属性供展示器调用获取,从而得知视图的状态或某些信息或对视图进行某些操作,同时也外放了一些方法供展示器注册,使得视图能在需要的时刻对展示器发出某些请求。
  •   Presenter(展示器):整个MVP模式的核心,负责对视图的操作,数据的绑定,必要时响应来自视图的请求,在有需要的时候会借助模型完成一些业务。
  •   Model(模型):完成整个模式中必要的业务逻辑。

浏览了一些园友的博文后,我也尝试实现了一个MVP模式。项目的结构如下图。

从上图可以很明显的看出MVP的三部分,另外Common目录下存放的主要是MVP模式里的一些基类,接口等等,本项目还使用了一个轻量级的 Ioc框架Ninject,为了尽量改动Common里的类,使用Ninject时要绑定的接口是实现类以配置的形式来实现,配置的信息就存放在 BindingConfig.xml文件里面。

看一下Common里面包含的类

IocContainer.cs

Ioc的容器

IView.cs

视图接口的基接口

MyEventArgs.cs

扩展了事件和委托的参数

PresenterBase.cs

所有展示器的基类

PresenterManager.cs

通过展示器展示其视图

WinFormInjectModule .cs

Ioc的接口与实现类的绑定

由于对Ninject还不是很熟悉,对它的用法解释不了太多

IocContainer的定义如下

复制代码
 1     public class IocContainer  2  {  3         private static IKernel _kernel;  4 
 5         public static IKernel Container  6  {  7             get
 8  {  9                 if (_kernel == null) 10                     _kernel = new StandardKernel(new WinFormInjectModule()); 11                 return _kernel; 12  } 13  } 14     }
复制代码

  这里用到了WinFormInjectModule类,它继承了NinjectModule,里面就重写了Load方法实现绑定,由于这里的绑定时通过配置实现的,所以这里还涉及到读取和分析配置信息

复制代码
 1     public class WinFormInjectModule : Ninject.Modules.NinjectModule  2  {  3         public override void Load()  4  {  5 
 6             List<Tuple<string, string>> bindingList = GetBindingConfig();  7  Type bindType,toType;  8             foreach (Tuple<string,string> item in bindingList)  9  { 10                 bindType=Type.GetType(item.Item1); 11                 if (item.Item2.Length == 0) 12  { 13  Bind(bindType).ToSelf(); 14                     continue; 15  } 16                 toType = Type.GetType(item.Item2); 17  Bind(bindType).To(toType); 18  } 19  } 20 
21         private List<Tuple<string, string>> GetBindingConfig() 22  { 23             List<Tuple<string, string>> result = new List<Tuple<string, string>>(); 24             XmlDocument xmlDoc = new XmlDocument(); 25             if (!File.Exists("BindingConfig.xml")) throw new IOException("BindingConfig.xml 不存在"); 26             xmlDoc.Load("BindingConfig.xml"); 27             XmlNodeList nodelist = xmlDoc.SelectNodes("//BindingSetting/Binding"); 28             string bind,to; 29             foreach (XmlNode node in nodelist) 30  { 31                 bind=string.Empty; 32                 to=string.Empty; 33                 bind = node.Attributes["bind"].Value; 34                 if (node.Attributes["to"] != null) to = node.Attributes["to"].Value; 35                 result.Add(new Tuple<string, string>(bind,to)); 36  } 37             return result; 38  } 39     }
复制代码

配置的定义如下

1 <BindingSetting>
2   <Binding bind="TestMVP.Model.IUser" to="TestMVP.Model.UserModel"/>
3   <Binding bind="TestMVP.View.ILoginView" to="TestMVP.View.LoginView"/>
4   <Binding bind="TestMVP.Presenter.LoginPresenter"/>
5 </BindingSetting>

bind属性就是要绑定的类或者接口,to就是绑定到的类,如果只是绑定自己的话就在bind属性填类名则可,to不用填了。

展示器的基类定义如下

复制代码
 1     public class PresenterBase<T> where T : IView  2  {  3         private T _view;  4 
 5         public PresenterBase(T view)  6  {  7             this.View = view;  8  }  9 
10         public T View 11  { 12             get { return _view; } 13             set { _view = value; } 14  } 15     }
复制代码

以接口的形式对视图进行访问的话,就可以避免直接访问视图的实例,减少了对视图的依赖。

考虑到在展示器里打开别的展示器管理的视图时,原本可以构造一个展示器实例,然后获取其视图进行展示,可是在一个展示器里构造另一个展示器,这样的做法好像不妥,于是定义了一个类专门用于打开别的视图用的。

当要打开某个视图(也就是窗体)时,就可以调用PresenterManager的静态方法

复制代码
 1     public class PresenterManager  2  {  3         public static void ShowView(string presenterName,FormAction formAction)  4  {  5             Type type = Type.GetType("TestMVP.Presenter." + presenterName);  6             object p = Common.IocContainer.Container.GetService(type);  7             System.Windows.Forms.Form frm = type.GetProperty("View").GetValue(p, null) as System.Windows.Forms.Form;  8             switch (formAction)  9  { 10                 case FormAction.Run: System.Windows.Forms.Application.Run(frm); 11                     break; 12                 case FormAction.Show: frm.Show(); 13                     break; 14                 case FormAction.ShowDialog: frm.ShowDialog(); 15                     break; 16                 default: 17                     break; 18  } 19  } 20 
21         public static void ShowView(string presenterName) 22  { 23  ShowView(presenterName, FormAction.Show); 24  } 25  } 26 
27     public enum FormAction { Run,Show,ShowDialog }
复制代码

 

下面则做一个简单的Demo,是登录功能的

首先是模型的,先定义了一个IUser接口,届时展示器想调用模型的方法是就通过这个接口来调用,免除了对模型其他成员的访问

1     public interface IUser 2  { 3         bool CheckLogin(string user, string password); 4     }

再由一个IModelUser实现这个接口

复制代码
1     public class UserModel:IUser 2  { 3         public bool CheckLogin(string user, string password) 4  { 5             if (user == "admin" && password == "123456") 6                 return true; 7             return false; 8  } 9     }
复制代码

接着到展示器

复制代码
 1     public class LoginPresenter:PresenterBase<ILoginView>
 2  {  3  [Inject]  4         public IUser UserModel { set; get; }  5 
 6         public LoginPresenter(ILoginView view):base(view)  7  {  8             this.View = view;  9             this.View.OnLogin += new MyEventHandler(View_OnLogin); 10  } 11 
12         void View_OnLogin(object sender, MyEventArgs e) 13  { 14             bool result =  UserModel.CheckLogin(this.View.IDBoxText, this.View.PasswordBoxText); 15             e.OptionResult=result; 16             if(result) 17  { 18                 PresenterManager.ShowView("SystemPresenter"); 19                 (sender as Form).Hide(); 20  } 21  } 22     }
复制代码

在构造展示器实例时,给视图的事件绑定一个方法,相应登录视图的登录验证请求,在改方法内调用模型的方法验证用户名密码,把结果通过委托的参数传递给视图。如果验证通过了就隐藏登录视图,显示主界面。

 

最后到视图

复制代码
 1     public interface ILoginView:IView  2  {  3         event MyEventHandler OnLogin;  4         string IDBoxText { get; set; }  5 
 6         string PasswordBoxText { get; set; }  7  }  8 
 9     public partial class LoginView : Form,ILoginView 10  { 11         public LoginView() 12  { 13  InitializeComponent(); 14  } 15 
16         private void button1_Click(object sender, EventArgs e) 17  { 18             MyEventArgs args=new MyEventArgs(); 19             if (OnLogin != null) OnLogin(this, args); 20             if (!args.OptionResult) 21                 MessageBox.Show("Fail"); 22  } 23 
24         public event MyEventHandler OnLogin; 25 
26         public string PasswordBoxText 27  { 28             get { return this.tbPw.Text; } 29             set { this.tbPw.Text = value; } 30  } 31 
32         public string IDBoxText 33  { 34             get { return this.tbID.Text; } 35             set { tbID.Text = value; } 36  } 37     }
复制代码

视图这里ILoginView是继承了IView接口,里面声明了登录视图应该外放的事件和属性,那登录界面来说

虽然很明显看得出ID后的输入框的值是用户ID,Password后面的输入框的值是用户密码,但是这些对于一个视图来说都是不知其含义的,知 道含义的是展示器,视图只是把值外放出去给展示器获取。正如一位园友说的,视图就该尽量吧控件多外放出去。不过我觉得某些简单的界面逻辑还是放在视图上比 较好,例如单击了某个按钮使得另一个输入框变灰之类的。

 

这样就牵强地使用了一下MVP模式,有位园友在讨论MVC时说过,没发挥到MVC的优势时干脆用回以前的WebForm,MVP也一样吧,期待 能真正用上它的时候。由于最近都是从事C/S的开发,对C/S比较熟悉,做的这个小尝试也是用WinForm的,但转到WebForm上估计也不难,展示 器管理那里要更改一下。