<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_Leo_wlCnBlogs</title><subtitle type="text">自由、创新、研究、探索Linux/Windows Mono/DotNet [ Open Source .NET Development/ 使用开源工具进行DotNet软件开发]锐意进取，志存高远.成就梦想，只争朝夕.从你开始，创新世界.【That I exist is a perpetual supprise which is life.专注电子商务 – Focus on eCommerce】</subtitle><id>http://feed.cnblogs.com/blog/u/64532/rss</id><updated>2012-05-28T01:36:42Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/64532/rss"/><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520827.html</id><title type="text">基于mongoDB和C#分布式海量文件存储实验</title><summary type="text">本实验将按以下几步进行：实验环境monodevelop ，ubuntu10.04，实验目的：探索分布式文件存储方案1、单机小文件的存储，逐步增加上传文件的大小，观察mongoDB中文件对磁盘分配大小的变化。2、采用分片的方式存储大量的数据实验一：首先建立一个数据库gywdb，上传一个574.5kB大小的文件，代码如下：View Codeusing System;using System.Collections;using System.Collections.Generic;using MongoDB.Bson;using MongoDB.Driver;using MongoDB.Driver</summary><published>2012-05-28T01:34:00Z</published><updated>2012-05-28T01:34:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520827.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520827.html"/><content type="html">&lt;p&gt;本实验将按以下几步进行：实验环境monodevelop ，ubuntu10.04，实验目的：探索分布式文件存储方案&lt;/p&gt;&lt;p&gt;1、单机小文件的存储，逐步增加上传文件的大小，观察mongoDB中文件对磁盘分配大小的变化。&lt;/p&gt;&lt;p&gt;2、采用分片的方式存储大量的数据&lt;/p&gt;&lt;p&gt;&lt;span&gt;实验一：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;首先建立一个数据库gywdb，上传一个574.5kB大小的文件&lt;/span&gt;，代码如下：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div id="cnblogs_code_open_2a52dcf1-206e-4ca4-9d00-454307504a4d" class="cnblogs_code_hide"&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System.Collections;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System.Collections.Generic;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Bson;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Driver;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Driver.GridFS;&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; mongoDBClient&lt;br/&gt;{&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MainClass&lt;br/&gt;    {&lt;br/&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Main (&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;             &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;mongoDb服务实例连接字符串&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; con=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;mongodb://localhost:27017&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;得到一个于mongoDB服务器连接的实例&lt;/span&gt;&lt;br/&gt;            MongoServer server=&lt;span&gt;MongoServer.Create(con);            &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;获得一个与具体数据库连接对象,数据库名为gywdb&lt;/span&gt;&lt;br/&gt;            MongoDatabase mydb=server.GetDatabase(&lt;span&gt;"&lt;/span&gt;&lt;span&gt;gywdb&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;);    &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;定义一个本地文件的路径字符串&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; localFileName=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;/home/guoyuanwei/学习资料/Google三大论文中文版.pdf&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;定义mongoDB数据库中文件的名称&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; mongoDBFileName=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;Google三大论文中文版&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;设置GridFS文件中对应的集合前缀名&lt;/span&gt;&lt;br/&gt;              MongoGridFSSettings fsSetting=&lt;span&gt;new&lt;/span&gt; MongoGridFSSettings(){Root=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;gyw&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;};&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;实例化一个GridFS&lt;/span&gt;&lt;br/&gt;              MongoGridFS gridfs=&lt;span&gt;new&lt;/span&gt;&lt;span&gt; MongoGridFS(mydb,fsSetting);            &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;将本地文件上传到mongoDB中去,以默认块的大小256KB对文件进行分块&lt;/span&gt;&lt;br/&gt;&lt;span&gt;            gridfs.Upload(localFileName,mongoDBFileName);&lt;br/&gt;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;运行命令：&amp;gt; show collections&lt;br /&gt;gyw.chunks&lt;br /&gt;gyw.files&lt;br /&gt;可以看到得到了一个前缀为gyw的文件元数据存储的集合gyw.files和存储文件数据块的gyw.chunks&lt;br /&gt;运行命令&amp;gt; db.gyw.chunks.find({},{"_id":1,"n":1})&lt;br /&gt;{ "_id" : ObjectId("4fc0a6d91d41c808f45cbfec"), "n" : 0 }&lt;br /&gt;{ "_id" : ObjectId("4fc0a6d91d41c808f45cbfed"), "n" : 1 }&lt;br /&gt;{ "_id" : ObjectId("4fc0a6da1d41c808f45cbfee"), "n" : 2 }&lt;/p&gt;&lt;p&gt;得到3个块数据，因为文件总大小为574.5KB，每个块采用的是默认大小256KB，所以得到了3个块。此时磁盘文件系统中，mongoDB自动生成了3个文件分别是：&lt;/p&gt;&lt;p&gt;gywdb.0(大小为64MB),gywdb.1(大小为128MB),gywdb.ns(存储命名空间源数据)，这里分配机制体现了mongoDB的文件分配策略，每个数据库有一个.ns文件和若干个数据文件&lt;/p&gt;&lt;p&gt;，数据文件以递增的数字结尾。每个新的以数字结尾的数据文件大小会加倍，直到达到最大值2GB，这是为了让小数据库不浪费太多的磁盘空间，同时让大数据使用磁盘上连续的空间。&lt;/p&gt;&lt;p&gt;MongoDB为了保证性能还会预分配数据文件，这意味着MongoDB服务器总是试图为每一个数据库保留一个额外的空数据文件，来避免文件分配所产生的阻塞。&lt;/p&gt;&lt;p&gt;&lt;span&gt;接着利用上面的代码再上传一个大小为2.4MB大小的文件到mongoDB的文件系统中&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;运行命令：&amp;gt; db.gyw.chunks.find({},{"_id":1,"n":1,"files_id":1})&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a6d91d41c808f45cbfec"), "files_id" : ObjectId("4fc0a6d91d41c808f45cbfe9"), "n" : 0 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a6d91d41c808f45cbfed"), "files_id" : ObjectId("4fc0a6d91d41c808f45cbfe9"), "n" : 1 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a6da1d41c808f45cbfee"), "files_id" : ObjectId("4fc0a6d91d41c808f45cbfe9"), "n" : 2 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191105"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 0 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191106"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 1 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191107"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 2 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191108"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 3 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191109"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 4 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c8091019110a"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 5 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c8091019110b"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 6 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c8091019110c"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 7 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c8091019110d"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 8 }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c8091019110e"), "files_id" : ObjectId("4fc0a8cf1d41c80910191102"), "n" : 9 }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;可以看到块中多了10个块，而且第一次上传的文件块中&amp;ldquo;files_id" 为 ObjectId("4fc0a6d91d41c808f45cbfe9")，第二次上传的文件为ObjectId("4fc0a8cf1d41c80910191102")，&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;这个files_id代表了这个块是属于那个文件的。如下命令显示了文件系统中文件元数据信息&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;gt; db.gyw.files.find()&lt;br /&gt;{ "_id" : ObjectId("4fc0a6d91d41c808f45cbfe9"), "filename" : "百度大规模数据处理", "length" : 588276, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-26T09:48:09.357Z"), "md5" : "5e55fb7496d41a52eb90daeac9e06936" }&lt;br /&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191102"), "filename" : "Google三大论文中文版", "length" : 2526950, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-26T09:56:31.143Z"), "md5" : "605f1deec1a277e3d878dfc6f3491cce" }&lt;br /&gt;&lt;span&gt;这个里面的"_id"值正好对应了上面块中的&amp;ldquo;files_id&amp;rdquo;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;这时在观察磁盘文件系统中mongoDB自动生成的文件，发现和第一次一样，说明数据还没达到第一个数据文件gywdb.0的总大小（64MB）。&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;接着再利用上面的代码上传一个大小为112.2MB大小的文件到mongoDB的文件系统中，利用命令：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&amp;gt; db.gyw.files.find()&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a6d91d41c808f45cbfe9"), "filename" : "百度大规模数据处理", "length" : 588276, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-26T09:48:09.357Z"), "md5" : "5e55fb7496d41a52eb90daeac9e06936" }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0a8cf1d41c80910191102"), "filename" : "Google三大论文中文版", "length" : 2526950, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-26T09:56:31.143Z"), "md5" : "605f1deec1a277e3d878dfc6f3491cce" }&lt;/span&gt;&lt;br /&gt;&lt;span&gt;{ "_id" : ObjectId("4fc0b3a31d41c8099f80a9ac"), "filename" : "微软官方2010年宽屏PPT图表全集400张锐普PPT论坛首发", "length" : 117652480, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-26T10:42:43.773Z"), "md5" : "506810e215c773addc3ce10a035695d9" }&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;发现多了一个文件微软官方2010年宽屏PPT图表全集400张锐普PPT论坛首发，证明上传成功了，再观察磁盘文件系统中mongoDB自动生成的文件，发现多了一个gywdb.2（大小为256MB），这证明了mongoDB文件分配机制。&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;实验二：数据的分布式存储即分片测试&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;准备：两台分片服务器（在同一台主机上，通过端口来区分，分别为：20000，20001），一台路由服务器（端口为：40000，所有的读写请求都必须经过它），一台配置服务器（端口为：30000）&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;root@ubuntu:/# mkdir -p /data/shard/s0&lt;br /&gt;root@ubuntu:/# mkdir -p /data/shard/s1&lt;br /&gt;root@ubuntu:/# mkdir -p /data/shard/log&lt;br /&gt;上面的命令为建立两个分片服务器的数据存放路径以及日志文件路径&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;root@ubuntu:/usr/local/mongoDB/bin# ./mongod --shardsvr --port 20000 --dbpath /data/shard/s0 --fork --logpath /data/shard/log/s0.log --directoryperdb&lt;br /&gt;root@ubuntu:/usr/local/mongoDB/bin# ./mongod --shardsvr --port 20001 --dbpath /data/shard/s1 --fork --logpath /data/shard/log/s1.log --directoryperdb&lt;br /&gt;上面的命令为启动两个分片服务器，注意其中各参数的意义&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;br /&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;root@ubuntu:/#&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;mkdir -p /data/shard/config&lt;br /&gt;root@ubuntu:/usr/local/mongoDB/bin# ./mongod --configsvr --port 30000 --dbpath /data/shard/config --fork --logpath /data/shard/log/config.log --directoryperdb&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;上面的命令为启动配置服务器端口为3000&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;# ./mongos --port 40000 --configdb localhost:30000 --fork --logpath /data/shard/log/route.log&lt;br /&gt;上面的命令是启动路由服务器，注意里面的参数configdb表示配置服务器的位置，因为路由服务器启动时需要从配置服务器上获取相应的信息&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;下面的命令是连接路由服务器并做一些配置：&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;root@ubuntu:/usr/local/mongoDB/bin# ./mongo admin --port 40000&lt;br /&gt;MongoDB shell version: 2.0.4&lt;br /&gt;connecting to: 127.0.0.1:40000/admin&lt;br /&gt;mongos&amp;gt; db.runCommand({addshard:"localhost:20000"})&lt;br /&gt;{ "shardAdded" : "shard0000", "ok" : 1 }&lt;br /&gt;mongos&amp;gt; db.runCommand({addshard:"localhost:20001"})&lt;br /&gt;{ "shardAdded" : "shard0001", "ok" : 1 }&lt;br /&gt;mongos&amp;gt; db.runCommand({enablesharding:"userDB"})&lt;br /&gt;{ "ok" : 1 }&lt;br /&gt;上面的命令完成了将两个服务器添加到分片集群中，在端口为40000的路由服务器上建立了一个数据库userDB，并将此数据库配置为可以进行分片。&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;接下来通过C#编写代码，完成文件的上传，观察数据被分片存储的情况，代码如下：&lt;/span&gt;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div id="cnblogs_code_open_c1315a90-218f-4e61-9606-906e981ace81" class="cnblogs_code_hide"&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System.Collections;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System.Collections.Generic;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Bson;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Driver;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Driver.GridFS;&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; mongoDBClient&lt;br/&gt;{&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MainClass&lt;br/&gt;    {&lt;br/&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Main (&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;             &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;mongoDb服务实例连接字符串&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; con=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;mongodb://localhost:40000&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;得到一个于mongoDB服务器连接的实例&lt;/span&gt;&lt;br/&gt;            MongoServer server=&lt;span&gt;MongoServer.Create(con);            &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;获得一个与具体数据库连接对象,数据库名为gywdb&lt;/span&gt;&lt;br/&gt;            MongoDatabase mydb=server.GetDatabase(&lt;span&gt;"&lt;/span&gt;&lt;span&gt;userDB&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;);    &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;定义一个本地文件的路径字符串&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; localFileName=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;/home/guoyuanwei/学习资料/微软官方2010年宽屏PPT图表全集400张锐普PPT论坛首发.ppt&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;定义mongoDB数据库中文件的名称&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; mongoDBFileName=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;微软官方2010年宽屏PPT图表全集400张锐普PPT论坛首发&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;设置GridFS文件中对应的集合前缀名&lt;/span&gt;&lt;br/&gt;              MongoGridFSSettings fsSetting=&lt;span&gt;new&lt;/span&gt; MongoGridFSSettings(){Root=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;userdata&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;};&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;实例化一个GridFS&lt;/span&gt;&lt;br/&gt;              MongoGridFS gridfs=&lt;span&gt;new&lt;/span&gt;&lt;span&gt; MongoGridFS(mydb,fsSetting);            &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;将本地文件上传到mongoDB中去,以默认块的大小256KB对文件进行分块&lt;/span&gt;&lt;br/&gt;&lt;span&gt;            gridfs.Upload(localFileName,mongoDBFileName);&lt;br/&gt;            &lt;br/&gt;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;上传了一个112.2MB的文件到路由服务器中。通过以下命令观察下此时数据库中集合情况&lt;/p&gt;&lt;p&gt;mongos&amp;gt; use userDB&lt;br /&gt;switched to db userDB&lt;br /&gt;mongos&amp;gt; show collections&lt;/p&gt;&lt;p&gt;system.indexes&lt;br /&gt;userdata.chunks&lt;br /&gt;userdata.files&lt;br /&gt;&lt;span&gt;此时数据库中有新增加了两个集合userdata.chunks和userdata.files，其中userdata.chunks是真正存储数据的地方。以下命令观察此集合的概况&lt;/span&gt;&lt;/p&gt;&lt;p&gt;mongos&amp;gt; db.userdata.chunks.stats()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span&gt;&amp;nbsp;&amp;nbsp;"sharded" : false,&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"primary" : "shard0000",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ns" : "userDB.userdata.chunks",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"count" : 449,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"size" : 117711616,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"avgObjSize" : 262163.95545657014,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"storageSize" : 135131136,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"numExtents" : 12,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nindexes" : 2,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"lastExtentSize" : 26034176,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"paddingFactor" : 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"flags" : 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"totalIndexSize" : 57232,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"indexSizes" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"_id_" : 24528,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1_n_1" : 32704&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ok" : 1&lt;br /&gt;}&lt;br /&gt;&lt;span&gt;其中&lt;span&gt;&amp;nbsp; "sharded" : false表明此表还没进行分片存储，路由服务器此时将数据全部保存到分片0所对应的服务器中了，文件的大小为117711616字节，在linux下通过文件系统的命令：&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;root@ubuntu:/usr/local/mongoDB/bin# cd /data/shard/s0/userDB&lt;br /&gt;root@ubuntu:/data/shard/s0/userDB# ls -l&lt;br /&gt;总用量 475156&lt;br /&gt;drwxr-xr-x 2 root root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4096 2012-05-27 16:57 _tmp&lt;br /&gt;-rw------- 1 root root&amp;nbsp; 67108864 2012-05-27 16:57 userDB.0&lt;br /&gt;-rw------- 1 root root 134217728 2012-05-27 16:57 userDB.1&lt;br /&gt;-rw------- 1 root root 268435456 2012-05-27 16:57 userDB.2&lt;br /&gt;-rw------- 1 root root&amp;nbsp; 16777216 2012-05-27 16:57 userDB.ns&lt;br /&gt;发现文件确实被存在分片0所对应的服务器中。&lt;/p&gt;&lt;p&gt;&lt;span&gt;继续上传更大的文件到路由服务器，突破分片设置中的默认块的大小200MB，看是什么情况？代码仍然如上，只是修改上传的文件，上传的文件大小为：393.0MB&lt;/span&gt;&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;div id="cnblogs_code_open_c09137bd-0fca-4a1d-a2fa-2c20071c4da3" class="cnblogs_code_hide"&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System.Collections;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; System.Collections.Generic;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Bson;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Driver;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt; MongoDB.Driver.GridFS;&lt;br/&gt;&lt;br/&gt;&lt;/span&gt;&lt;span&gt;namespace&lt;/span&gt;&lt;span&gt; mongoDBClient&lt;br/&gt;{&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MainClass&lt;br/&gt;    {&lt;br/&gt;        &lt;/span&gt;&lt;span&gt;public&lt;/span&gt; &lt;span&gt;static&lt;/span&gt; &lt;span&gt;void&lt;/span&gt; Main (&lt;span&gt;string&lt;/span&gt;&lt;span&gt;[] args)&lt;br/&gt;        {&lt;br/&gt;             &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;mongoDb服务实例连接字符串&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; con=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;mongodb://localhost:40000&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;得到一个于mongoDB服务器连接的实例&lt;/span&gt;&lt;br/&gt;            MongoServer server=&lt;span&gt;MongoServer.Create(con);            &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;获得一个与具体数据库连接对象,数据库名为gywdb&lt;/span&gt;&lt;br/&gt;            MongoDatabase mydb=server.GetDatabase(&lt;span&gt;"&lt;/span&gt;&lt;span&gt;userDB&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;);    &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;定义一个本地文件的路径字符串&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; localFileName=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;/home/guoyuanwei/学习资料/Platform5.24.rar&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;定义mongoDB数据库中文件的名称&lt;/span&gt;&lt;br/&gt;            &lt;span&gt;string&lt;/span&gt; mongoDBFileName=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;软件源代码&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;设置GridFS文件中对应的集合前缀名&lt;/span&gt;&lt;br/&gt;              MongoGridFSSettings fsSetting=&lt;span&gt;new&lt;/span&gt; MongoGridFSSettings(){Root=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;userdata&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;};&lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;实例化一个GridFS&lt;/span&gt;&lt;br/&gt;              MongoGridFS gridfs=&lt;span&gt;new&lt;/span&gt;&lt;span&gt; MongoGridFS(mydb,fsSetting);            &lt;br/&gt;            &lt;/span&gt;&lt;span&gt;//&lt;/span&gt;&lt;span&gt;将本地文件上传到mongoDB中去,以默认块的大小256KB对文件进行分块&lt;/span&gt;&lt;br/&gt;&lt;span&gt;            gridfs.Upload(localFileName,mongoDBFileName);&lt;br/&gt;            &lt;br/&gt;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;首先通过以下命令观察root@ubuntu:/data/shard/s0/userDB# ls -l&lt;br /&gt;总用量 2048028&lt;br /&gt;drwxr-xr-x 2 root root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4096 2012-05-27 17:22 _tmp&lt;br /&gt;-rw------- 1 root root&amp;nbsp;&amp;nbsp; 67108864 2012-05-27 17:22 userDB.0&lt;br /&gt;-rw------- 1 root root&amp;nbsp; 134217728 2012-05-27 17:22 userDB.1&lt;br /&gt;-rw------- 1 root root&amp;nbsp; 268435456 2012-05-27 17:22 userDB.2&lt;br /&gt;-rw------- 1 root root&amp;nbsp; 536870912 2012-05-27 17:22 userDB.3&lt;br /&gt;-rw------- 1 root root 1073741824 2012-05-27 17:22 userDB.4&lt;br /&gt;-rw------- 1 root root&amp;nbsp;&amp;nbsp; 16777216 2012-05-27 17:22 userDB.ns&lt;br /&gt;&lt;span&gt;发现文件还是被存到默认的分片服务器0上。分片1上的数据还没有。尽管文件的总大小已经达到了需要分片存储的条件，即块的大小200MB&lt;/span&gt;&lt;br /&gt;&lt;span&gt;因此下面需要重新设置路由服务器，使其对集合userDB.userdata.chunks（这个里面存储了用户上传的文件）进行分片。&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;要想实现海量数据的分布式存储，那么就要对集合进行分片，因此片键的选择是至关重要的，它直接决定了集群中数据分布是否均衡、集群性能是否合理。那么我们究竟该选择什么样的字段来作为分片Key呢？这是个需要反复实践总结的地方。由于这里利用了分布式文件系统GridFS，因此有几点要说明（碰到错误，在网上查到的http://blog.csdn.net/zhangzhaokun/article/details/6324389）：&lt;strong&gt;GridFS&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;根据需求的不同，GridFS有几种不同的分片方法。基于预先存在的索引是惯用的分片办法：&lt;/p&gt;&lt;p&gt;1）&amp;ldquo;files&amp;rdquo;集合（Collection）不会分片,所有的文件记录都会位于一个分片上,高度推荐使该分片保持高度灵活（至少使用由3个节点构成的replica set）。&lt;/p&gt;&lt;p&gt;2）&amp;ldquo;chunks&amp;rdquo;集合（Collection）应该被分片，并且用索引&amp;rdquo;files_id:1&amp;rdquo;。已经存在的由MongoDB的驱动来创建的&amp;ldquo;files_id,n&amp;rdquo;索引不能用作分片Key（这个是一个分片约束，后续会被修复），所以不得不创建一个独立的&amp;rdquo;files_id&amp;rdquo;索引。使用&amp;ldquo;files_id&amp;rdquo;作为分片Key的原因是一个特定的文件的所有Chunks都是在相同的分片上，非常安全并且允许运行&amp;ldquo;filemd5&amp;rdquo;命令（要求特定的驱动）。&lt;/p&gt;&lt;p&gt;&lt;span&gt;&amp;nbsp;前面已经分析过，在&lt;span&gt;userDB.userdata.chunks&lt;/span&gt;集合中每一个记录都有一个字段file_id,代表了此块属于哪个文件，因此这里建一个以file_id的索引，以此字段键做为片键，同一个文件的块会被分配到同一个分片下&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;mongos&amp;gt; db.userdata.chunks.ensureIndex({files_id:1})&lt;br /&gt;设置片键的命令如下：&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;mongos&amp;gt; db.runCommand({shardcollection:"userDB.userdata.chunks",key:{files_id:1}}&lt;br /&gt;{ "collectionsharded" : "userDB.userdata.chunks", "ok" : 1 }&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;再次查看此表的状态：&amp;nbsp;"sharded" : true说明此集合被分片了。&lt;/p&gt;&lt;p&gt;mongos&amp;gt; db.userdata.chunks.stats()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"sharded" : true,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"flags" : 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ns" : "userDB.userdata.chunks",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"count" : 2022,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"numExtents" : 19,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"size" : 529953820,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"storageSize" : 538714112,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"totalIndexSize" : 245280,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"indexSizes" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"_id_" : 73584,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1" : 73584,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1_n_1" : 98112&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"avgObjSize" : 262093.87734915924,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nindexes" : 3,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nchunks" : 2,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"shards" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"shard0000" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ns" : "userDB.userdata.chunks",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"count" : 2022,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"size" : 529953820,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"avgObjSize" : 262093.87734915924,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"storageSize" : 538714112,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"numExtents" : 19,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nindexes" : 3,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"lastExtentSize" : 93306880,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"paddingFactor" : 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"flags" : 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"totalIndexSize" : 245280,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"indexSizes" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"_id_" : 73584,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1_n_1" : 98112,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1" : 73584&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ok" : 1&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ok" : 1&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;多次执行如下代码上传文件后：&lt;span&gt;执行如下命令观看系统中存在的文件&lt;/span&gt;&lt;/p&gt;&lt;p&gt;mongos&amp;gt; db.userdata.files.find()&lt;br /&gt;{ "_id" : ObjectId("4fc1ec6c1d41c809ed4230d3"), "filename" : "微软官方2010年宽屏PPT图表全集400张锐普PPT论坛首发", "length" : 117652480, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-27T08:57:16.676Z"), "md5" : "506810e215c773addc3ce10a035695d9" }&lt;br /&gt;{ "_id" : ObjectId("4fc1f2451d41c80b0f29eda1"), "filename" : "软件源代码", "length" : 412116413, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-27T09:22:13.443Z"), "md5" : "28cadbb180093dbcd15b25f0e741ae0a" }&lt;br /&gt;{ "_id" : ObjectId("4fc1fe7d1d41c80b4d4a17cb"), "filename" : "新的软件源代码", "length" : 412116413, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-27T10:14:21.845Z"), "md5" : "28cadbb180093dbcd15b25f0e741ae0a" }&lt;br /&gt;{ "_id" : ObjectId("4fc212541d41c80ba8e1180f"), "filename" : "新微软PPT", "length" : 117652480, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-27T11:39:00.598Z"), "md5" : "506810e215c773addc3ce10a035695d9" }&lt;br /&gt;{ "_id" : ObjectId("4fc21b9e1d41c80747e207e8"), "filename" : "新微软PPT1", "length" : 117652480, "chunkSize" : 262144, "uploadDate" : ISODate("2012-05-27T12:18:38.183Z"), "md5" : "506810e215c773addc3ce10a035695d9" }&lt;br /&gt;可以看到实验上传了5个文件&lt;/p&gt;&lt;p&gt;&lt;span&gt;执行以下命令观察文件分块后在集群中的部署情况：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;mongos&amp;gt; db.userdata.chunks.stats()&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"sharded" : true,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"flags" : 0,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ns" : "userDB.userdata.chunks",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"count" : 4493,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"numExtents" : 35,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"size" : 1177568872,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"storageSize" : 1274904576,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"totalIndexSize" : 531440,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"indexSizes" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"_id_" : 163520,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1" : 155344,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1_n_1" : 212576&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"avgObjSize" : 262089.66659247718,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nindexes" : 3,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nchunks" : 3,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;"shards" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"shard0000" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ns" : "userDB.userdata.chunks",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"count" : 4044,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"size" : 1059857256,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"avgObjSize" : 262081.4183976261,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"storageSize" : 1139773440,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"numExtents" : 23,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nindexes" : 3,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"lastExtentSize" : 193486848,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"paddingFactor" : 1,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"flags" : 0,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"totalIndexSize" : 449680,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"indexSizes" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"_id_" : 138992,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1_n_1" : 179872,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"files_id_1" : 130816&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ok" : 1&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;},&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"shard0001" : {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"ns" : "userDB.userdata.chunks",&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"count" : 449,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"size" : 117711616,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"avgObjSize" : 262163.95545657014,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"storageSize" : 135131136,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"numExtents" : 12,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"nindexes" : 3,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;"lastExtentSize" : 26034176,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520827.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520827.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520824.html</id><title type="text">SusuCMS开发-创建Widget</title><summary type="text">SusuCMS的Widget开发非常简单。或许其他CMS那种复杂而且艰难的开发模式已经让你感到疲惫和恐惧，但是用SusuCMS你不需要创建一个新项目，只需要一个目录几个文件就可以搞定。这里我们举CMS中已经有的RichText这个widget来说明。RichText可以帮助用户在页面添加一些富文本。在项目的根目录Widgets我们可以看到一个RichText的文件夹，放在根目录Widgets下的的Widget都是属于系统级的Widget的，可以被所有的站点使用。站点模版下也可以放置Widget供使用这个站点模版的站点使用。一、Widget后台。RichText目录下的Admin.cshtml可</summary><published>2012-05-28T01:33:00Z</published><updated>2012-05-28T01:33:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520824.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520824.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;SusuCMS的Widget开发非常简单。或许其他CMS那种复杂而且艰难的开发模式已经让你感到疲惫和恐惧，&lt;/p&gt;&lt;p&gt;但是用SusuCMS你不需要创建一个新项目，只需要一个目录几个文件就可以搞定。&lt;/p&gt;&lt;p&gt;这里我们举CMS中已经有的RichText这个widget来说明。RichText可以帮助用户在页面添加一些富文本。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/37494/2012052720553052.png" alt="" /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;在项目的根目录Widgets我们可以看到一个RichText的文件夹，放在根目录Widgets下的的Widget都是属于系统级的Widget的，可以被所有的站点使用。&lt;/p&gt;&lt;p&gt;站点模版下也可以放置Widget供使用这个站点模版的站点使用。&lt;/p&gt;&lt;p&gt;一、Widget后台。&lt;/p&gt;&lt;p&gt;RichText目录下的Admin.cshtml可以使用户可以在后台添加富文本。效果如下图所示。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/37494/2012052721061220.png" alt="" width="791" height="402" /&gt;&lt;/p&gt;&lt;p&gt;好了，打开一个Dialog这些代码我们不需要写，我们只需要Admin.cshtml再插入一些简单的代码就好了。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;@inherits SusuCMS.Web.WidgetAdminPageBase&lt;br/&gt;&lt;/span&gt;&amp;lt;tr&amp;gt;&lt;br/&gt;    &amp;lt;th&amp;gt;&lt;span&gt;&lt;br/&gt;        @Html.Label(&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;, Localize(&lt;span&gt;"&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;))&lt;br/&gt;    &lt;/span&gt;&amp;lt;/th&amp;gt;&lt;br/&gt;    &amp;lt;td&amp;gt;&lt;span&gt;&lt;br/&gt;        @TelerikHelper.Editor(&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt;Content&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;, i =&amp;gt; { i.HtmlAttributes(&lt;span&gt;new&lt;/span&gt; { style = &lt;span&gt;"&lt;/span&gt;&lt;span&gt;height:420px&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&lt;span&gt; }); })&lt;br/&gt;        &lt;/span&gt;&amp;lt;script type=&lt;span&gt;"&lt;/span&gt;&lt;span&gt;text/javascript&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;&amp;gt;&lt;span&gt;&lt;br/&gt;            function getData() {&lt;br/&gt;                &lt;/span&gt;&lt;span&gt;return&lt;/span&gt; { Content: $(&lt;span&gt;'&lt;/span&gt;&lt;span&gt;#Content&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;).data(&lt;span&gt;'&lt;/span&gt;&lt;span&gt;tEditor&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;&lt;span&gt;).value() };&lt;br/&gt;            }&lt;br/&gt;&lt;br/&gt;            function setData(data) {&lt;br/&gt;                &lt;/span&gt;&lt;span&gt;if&lt;/span&gt; (data != &lt;span&gt;null&lt;/span&gt;&lt;span&gt;) {&lt;br/&gt;                    $(&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;&lt;span&gt;#Content&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;).data(&lt;span&gt;'&lt;/span&gt;&lt;span&gt;tEditor&lt;/span&gt;&lt;span&gt;'&lt;/span&gt;&lt;span&gt;).value(data.Content);&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;        &lt;/span&gt;&amp;lt;/script&amp;gt;&lt;br/&gt;    &amp;lt;/td&amp;gt;&lt;br/&gt;&amp;lt;/tr&amp;gt;&lt;/div&gt;&lt;p&gt;Admin.cshtml必须继承于SusuCMS.Web.WidgetAdminPageBase这个类。&lt;/p&gt;&lt;p&gt;1.&amp;nbsp;多语言&lt;/p&gt;&lt;p&gt;我们只需要调用Localize("Content")就可以了，Content的中文翻译我们存储在当前Widget目录下的Resources/Admin.zh-CN.resx文件里面，文件名和目录名我们都约定好了。&lt;/p&gt;&lt;p&gt;2. 初始化数据&lt;/p&gt;&lt;p&gt;初始化数据有2中方式，一种是用js来初始化，另一种就是服务器端代码。&lt;/p&gt;&lt;p&gt;用js来初始化数据，我们必须在代码里面实现setData 这个方法。data就是我们已经存储好的对象。&lt;/p&gt;&lt;p&gt;用服务器端代码初始化数据。Model就是Widget类型。GetData方法返回一个dynamic对象。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;@{&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt; data =&lt;span&gt; Model.GetData();&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt; (data != &lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;br/&gt;    {&lt;br/&gt;        ViewBag.Content &lt;/span&gt;=&lt;span&gt; data.Content;&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;p&gt;3. 保存数据。&lt;/p&gt;&lt;p&gt;保存数据只有一种方式，那就是实现js的getData方法。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;关于Widget.Config的说明&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;&amp;lt;?&lt;/span&gt;&lt;span&gt;xml version="1.0"&lt;/span&gt;&lt;span&gt;?&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;WidgetInfo&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;DisplayName&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;Rich text&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;DisplayName&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;A widget allow you to place html code.&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;AdminWindowHeight&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;540&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;AdminWindowHeight&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;    &lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;AdminWindowWidth&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;1080&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;AdminWindowWidth&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;WidgetInfo&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;span&gt;DisplayName是指Widget在后台添加的时候显示的类型文本。&lt;/span&gt;&lt;span&gt;Description是Widget描述信息，暂时没有用。&lt;br /&gt;&lt;/span&gt;&lt;span&gt;AdminWindowWidth和&lt;/span&gt;AdminWindowHeight用来设置后台Dialog弹出窗口的大小。&lt;br /&gt;&lt;br /&gt;&lt;p&gt;二 、前台显示。&lt;/p&gt;&lt;p&gt;前台显示由Widget.cshtml来处理。这个文件必须继承与SusuCMS.Web.WidgetPageBase。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;@inherits SusuCMS.Web.WidgetPageBase&lt;br/&gt;@{&lt;br/&gt;    FrontHtml.ClientScript.jQuery(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;);&lt;br/&gt;    &lt;br/&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt; data =&lt;span&gt; CurrentWidget.GetData();&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt; (data != &lt;span&gt;null&lt;/span&gt;&lt;span&gt;)&lt;br/&gt;    {&lt;br/&gt;    @Html.Raw(data.Content)&lt;br/&gt;    }&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;p&gt;1. 获取数据。&lt;/p&gt;&lt;p&gt;后台设置的数据可以通过CurrentWidget.GetData()来获取，也是dynamic类型。如果是其他数据，如文章我们可以用其他方式获取数据。&lt;/p&gt;&lt;p&gt;如下代码所示。所以获取数据还是非常自由和随意的。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;@{&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt; dataContext =&lt;span&gt; BlogWorkContext.GetDataContext();&lt;br/&gt;    &lt;/span&gt;&lt;span&gt;var&lt;/span&gt; list =&lt;span&gt; dataContext.Articles.WithSiteId(CurrentSite.Id);&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;p&gt;2. 引入js文件。&lt;/p&gt;&lt;p&gt;如果改widget需要jQuery支持，则只需调用FrontHtml.ClientScript.jQuery(true);就可以了。&lt;/p&gt;&lt;p&gt;如果需要注册js文件的话。我们也可以调用&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;    FrontHtml.ClientScript.RegisterScript(&lt;span&gt;"&lt;/span&gt;&lt;span&gt;~/Scripts/codeMirror/codemirror.js&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;);&lt;/div&gt;&lt;p&gt;页面解析的时候会将js统一在压缩，不会有重复的js引用。&lt;/p&gt;&lt;p&gt;3. Label的使用。&lt;/p&gt;&lt;p&gt;在Widget中我们可以使用WidgetLabel来实现文本在后台可修改。当然PageLabel和SiteLabel在此也是可以用的。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&amp;lt;h2&amp;gt;@WidgetLabel(&lt;span&gt;"&lt;/span&gt;&lt;span&gt;JavaScript minify&lt;/span&gt;&lt;span&gt;"&lt;/span&gt;)&amp;lt;/h2&amp;gt;&lt;/div&gt;&lt;p&gt;4. 表单提交与验证。&lt;/p&gt;&lt;p&gt;详细请看&lt;a href="http://susucms.svn.sourceforge.net/viewvc/susucms/SusuCMS.Web/Widgets/Blog.Contact/Widget.cshtml?revision=343&amp;amp;view=markup"&gt;http://susucms.svn.sourceforge.net/viewvc/susucms/SusuCMS.Web/Widgets/Blog.Contact/Widget.cshtml?revision=343&amp;amp;view=markup&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;分类:&amp;nbsp;&lt;a href="http://www.cnblogs.com/fishbin/category/147850.html"&gt;Web开发&lt;/a&gt;&lt;/div&gt;&lt;div id="EntryTag"&gt;标签:&amp;nbsp;&lt;a href="http://www.cnblogs.com/fishbin/tag/SusuCMS/"&gt;SusuCMS&lt;/a&gt;,&amp;nbsp;&lt;a href="http://www.cnblogs.com/fishbin/tag/Widget/"&gt;Widget&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;源码在&lt;/span&gt;&lt;a href="http://sourceforge.net/projects/susucms" target="_blank"&gt;http://sourceforge.net/projects/susucms&lt;/a&gt;&lt;span&gt;&amp;nbsp;上，可以直接通过svn checkout，保证源码是最新的。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520824.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520824.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520822.html</id><title type="text">HTML 5 系列文章索引</title><summary type="text">[源码下载]千呼万唤 HTML 5 系列文章索引作者：webabcd介绍千呼万唤 HTML 5 系列文章索引：HTML 51、千呼万唤 HTML 5 (1) - 根元素, 元数据元素, 脚本元素介绍HTML 5: 根元素, 元数据元素, 脚本元素根元素 - doctype, html元数据元素 - head, title, base, link, meta, style脚本元素 - script, noscript2、千呼万唤 HTML 5 (2) - 区块元素介绍HTML 5: 区块元素区块元素 - body, article, section, header, footer, h1, h2</summary><published>2012-05-28T01:31:00Z</published><updated>2012-05-28T01:31:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520822.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520822.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;&lt;a title="千呼万唤 HTML 5 - 源码下载" href="http://files.cnblogs.com/webabcd/HTML5.rar" target="_blank"&gt;[源码下载]&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;div align="center"&gt;千呼万唤 HTML 5 系列文章索引&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;作者：&lt;a title="webabcd - 专注于asp.net, Silverlight" href="http://webabcd.cnblogs.com/" target="_blank"&gt;webabcd&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;千呼万唤 HTML 5 系列文章索引：HTML 5&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1、&lt;a title="千呼万唤 HTML 5 (1) - 根元素, 元数据元素, 脚本元素" href="http://www.cnblogs.com/webabcd/archive/2011/09/15/2176955.html" target="_blank"&gt;千呼万唤 HTML 5 (1) - 根元素, 元数据元素, 脚本元素&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 根元素, 元数据元素, 脚本元素&lt;/p&gt;&lt;ul&gt;&lt;li&gt;根元素 - doctype, html&lt;/li&gt;&lt;li&gt;元数据元素 - head, title, base, link, meta, style&lt;/li&gt;&lt;li&gt;脚本元素 - script, noscript&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;2、&lt;a title="千呼万唤 HTML 5 (2) - 区块元素" href="http://www.cnblogs.com/webabcd/archive/2011/09/19/2180990.html" target="_blank"&gt;千呼万唤 HTML 5 (2) - 区块元素&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 区块元素&lt;/p&gt;&lt;ul&gt;&lt;li&gt;区块元素 - body, article, section, header, footer, h1, h2, h3, h4, h5, h6, hgroup, aside, nav, address&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;3、&lt;a title="千呼万唤 HTML 5 (3) - 内容分组元素" href="http://www.cnblogs.com/webabcd/archive/2011/09/22/2184684.html" target="_blank"&gt;千呼万唤 HTML 5 (3) - 内容分组元素&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 内容分组元素&lt;/p&gt;&lt;ul&gt;&lt;li&gt;内容分组元素&amp;nbsp;- p, hr, pre,&amp;nbsp;blockquote, ul, ol, li,&amp;nbsp;dl, dt, dd,&amp;nbsp;figure,&amp;nbsp;figcaption, div&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;4、&lt;a title="千呼万唤 HTML 5 (4) - 文本语义元素" href="http://www.cnblogs.com/webabcd/archive/2011/09/26/2190778.html" target="_blank"&gt;千呼万唤 HTML 5 (4) - 文本语义元素&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 文本语义元素&lt;/p&gt;&lt;ul&gt;&lt;li&gt;文本语义元素&amp;nbsp;- a, em, strong, small, s, cite, q,&amp;nbsp;abbr, time, code, var, dfn,&amp;nbsp;samp,&amp;nbsp;kbd,&amp;nbsp;sub, sup, i, b, u, mark,&amp;nbsp;ruby, rt, rp,&amp;nbsp;bdi,&amp;nbsp;bdo, span, br, wbr&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;5、&lt;a title="千呼万唤 HTML 5 (5) - 元素的通用属性" href="http://www.cnblogs.com/webabcd/archive/2012/02/02/2335318.html" target="_blank"&gt;千呼万唤 HTML 5 (5) - 元素的通用属性&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 元素的通用属性&lt;/p&gt;&lt;ul&gt;&lt;li&gt;元素的通用属性&amp;nbsp;-&amp;nbsp;accesskey,&amp;nbsp;style,&amp;nbsp;class,&amp;nbsp;title,&amp;nbsp;tabindex,&amp;nbsp;id,&amp;nbsp;dir,&amp;nbsp;spellcheck,&amp;nbsp;hidden,&amp;nbsp;contenteditable,&amp;nbsp;contextmenu,&amp;nbsp;draggable, dropzone&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;6、&lt;a title="千呼万唤 HTML 5 (6) - 表单元素之 input 元素" href="http://www.cnblogs.com/webabcd/archive/2012/02/06/2339534.html" target="_blank"&gt;千呼万唤 HTML 5 (6) - 表单元素之 input 元素&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 表单元素之 input 元素&lt;/p&gt;&lt;ul&gt;&lt;li&gt;表单元素之 input 元素&amp;nbsp;- text, password, url, telephone, email, search, file, radio, checkbox, button, submit, reset,&amp;nbsp;number,&amp;nbsp;range,&amp;nbsp;image,&amp;nbsp;hidden,&amp;nbsp;color,&amp;nbsp;datetime, datetime-local, date, time, month, week&lt;/li&gt;&lt;li&gt;input 元素的通用属性 -&amp;nbsp;autocomplete, placeholder, pattern, dirname, size, maxlength, readonly, required, list, multiple, min, max, step&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;7、&lt;a title="千呼万唤 HTML 5 (7) - 表单元素" href="http://www.cnblogs.com/webabcd/archive/2012/02/08/2342275.html" target="_blank"&gt;千呼万唤 HTML 5 (7) - 表单元素&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 表单元素&lt;/p&gt;&lt;ul&gt;&lt;li&gt;表单元素 - form, label, button, select, option, optgroup, datalist, textarea, fieldset, legend, progress, meter, keygen, output&lt;/li&gt;&lt;li&gt;表单验证&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;8、&lt;a title="千呼万唤 HTML 5 (8) - 画布（canvas）之绘制图形" href="http://www.cnblogs.com/webabcd/archive/2012/02/13/2348813.html" target="_blank"&gt;千呼万唤 HTML 5 (8) - 画布（canvas）之绘制图形&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 画布（canvas）之绘制图形&lt;/p&gt;&lt;ul&gt;&lt;li&gt;画布 Demo&amp;nbsp;- 画布的基本概念及 Demo，canvas.getContext(), CanvasRenderingContext2D, canvas.width, canvas.height, canvas.toDataURL()&lt;/li&gt;&lt;li&gt;在画布上绘制矩形 -&amp;nbsp;canvas.getContext(), fillRect(), fillStyle, lineWidth, strokeStyle, strokeRect(), clearRect()&lt;/li&gt;&lt;li&gt;在画布上绘制弧线（以路径的方式）-&amp;nbsp;beginPath(), arc(), fill(), stroke(), moveTo(), arcTo(), isPointInPath()&lt;/li&gt;&lt;li&gt;在画布上绘制曲线（以路径的方式）-&amp;nbsp;quadraticCurveTo(), bezierCurveTo()&lt;/li&gt;&lt;li&gt;在画布上绘制直线（以路径的方式）-&amp;nbsp;lineWidth, beginPath(), stroke(), moveTo(), lineTo(), lineCap, lineJoin, miterLimit, closePath()&lt;/li&gt;&lt;li&gt;在画布上绘制矩形（以路径的方式）-&amp;nbsp;rect()&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;9、&lt;a title="千呼万唤 HTML 5 (9) - 画布（canvas）之承载媒体" href="http://www.cnblogs.com/webabcd/archive/2012/02/16/2353563.html" target="_blank"&gt;千呼万唤 HTML 5 (9) - 画布（canvas）之承载媒体&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 画布（canvas）之承载媒体&lt;/p&gt;&lt;ul&gt;&lt;li&gt;呈现文本 - font, textAlign, textBaseline, strokeStyle, fillStyle, fillText(), measureText, TextMetrics.width&lt;/li&gt;&lt;li&gt;呈现图片 - drawImage()&lt;/li&gt;&lt;li&gt;呈现视频截图 - drawImage()&lt;/li&gt;&lt;li&gt;呈现其他画布 - drawImage()&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;10、&lt;a title="千呼万唤 HTML 5 (10) - 画布（canvas）之转换" href="http://www.cnblogs.com/webabcd/archive/2012/02/22/2362505.html" target="_blank"&gt;千呼万唤 HTML 5 (10) - 画布（canvas）之转换&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 画布（canvas）之转换（转换画布的用户坐标系）&lt;/p&gt;&lt;ul&gt;&lt;li&gt;平移 | translate()&lt;/li&gt;&lt;li&gt;旋转 | rotate()&lt;/li&gt;&lt;li&gt;缩放 | scale()&lt;/li&gt;&lt;li&gt;矩阵转换 | transform(a, b, c, d, e, f)&lt;/li&gt;&lt;li&gt;矩阵转换 | setTransform(a, b, c, d, e, f)&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;11、&lt;a title="千呼万唤 HTML 5 (11) - 画布（canvas）之效果" href="http://www.cnblogs.com/webabcd/archive/2012/02/27/2369452.html" target="_blank"&gt;千呼万唤 HTML 5 (11) - 画布（canvas）之效果&lt;/a&gt;&amp;nbsp;&lt;br /&gt;介绍&amp;nbsp;&lt;br /&gt;HTML 5: 画布（canvas）之效果&lt;/p&gt;&lt;ul&gt;&lt;li&gt;填充色, 笔划色, 颜色值 | fillStyle, strokeStyle&lt;/li&gt;&lt;li&gt;剪裁 | clip()&lt;/li&gt;&lt;li&gt;渐变色 | createLinearGradient(), createRadialGradient(), CanvasGradient.addColorStop()&lt;/li&gt;&lt;li&gt;贴图的平铺模式 | createPattern()&lt;/li&gt;&lt;li&gt;阴影效果 | shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor&lt;/li&gt;&lt;li&gt;全局 Alpha | globalAlpha&lt;/li&gt;&lt;li&gt;新颜色与画布当前已有颜色的合成方式 | globalCompositeOperation&lt;/li&gt;&lt;li&gt;保存画布上下文，以及恢复画布上下文 | save(), restore()&lt;/li&gt;&lt;li&gt;像素操作 | createImageData(), getImageData(), putImageData(), ImageData, CanvasPixelArray&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;OK&amp;nbsp;&lt;br /&gt;&lt;a title="千呼万唤 HTML 5 - 源码下载" href="http://files.cnblogs.com/webabcd/HTML5.rar" target="_blank"&gt;[源码下载]&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;分类:&amp;nbsp;&lt;a href="http://www.cnblogs.com/webabcd/category/322723.html"&gt;HTML 5&lt;/a&gt;,&amp;nbsp;&lt;a href="http://www.cnblogs.com/webabcd/category/64257.html"&gt;Index文章索引&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520822.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520822.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520817.html</id><title type="text">Microsoft StreamInsight 构建物联网</title><summary type="text">微软的首席StreamInsight™ 项目经理发布的一篇博客Big Data, Hadoop and StreamInsight™，微软的大数据解决方案中包含Microsoft StreamInsight™和微软的Hadoop-based Services for WindowsMicrosoft还计划推出运行于Windows Server之上Hadoop预览版，已经提供下载https://connect.microsoft.com/SQLServer/Survey/Survey.aspx?SurveyID=13697，并会在6月29日提供最终版本。Microsoft还将在Hadoop上集成</summary><published>2012-05-28T01:28:00Z</published><updated>2012-05-28T01:28:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520817.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520817.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;微软的首席StreamInsight&amp;trade; 项目经理发布的一篇博客&amp;nbsp;&lt;a href="http://blogs.msdn.com/b/microsoft_business_intelligence1/archive/2012/02/22/big-data-hadoop-and-streaminsight.aspx"&gt;Big Data, Hadoop and StreamInsight&amp;trade;&lt;/a&gt;，微软的大数据解决方案中包含&amp;nbsp;&amp;nbsp;&lt;a href="http://www.microsoft.com/sqlserver/en/us/solutions-technologies/business-intelligence/complex-event-processing.aspx"&gt;Microsoft StreamInsight&amp;trade;&amp;nbsp;&lt;/a&gt;和微软的&amp;nbsp;&lt;a href="http://social.technet.microsoft.com/wiki/contents/articles/6204.hadoop-based-services-for-windows.aspx"&gt;Hadoop-based Services for Windows&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-43-09/6505.StreamInsight-1.png"&gt;&lt;img src="http://blogs.msdn.com/resized-image.ashx/__size/470x350/__key/communityserver-blogs-components-weblogfiles/00-00-01-43-09/6505.StreamInsight-1.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Microsoft还计划推出运行于Windows Server之上Hadoop预览版，已经提供下载&lt;a title="https://connect.microsoft.com/SQLServer/Survey/Survey.aspx?SurveyID=13697" href="https://connect.microsoft.com/SQLServer/Survey/Survey.aspx?SurveyID=13697"&gt;https://connect.microsoft.com/SQLServer/Survey/Survey.aspx?SurveyID=13697&lt;/a&gt;&amp;nbsp;，并会在6月29日提供最终版本。Microsoft还将在Hadoop上集成Active Directory和System Center。Microsoft的更多的大数据计划将会在今年6月公布。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.sqlmag.com/article/sqlserverdenali/microsoft-sql-server-2012-takes-big-data-hadoop-141948" target="_blank"&gt;Microsoft SQL Server 2012 Takes on Big Data with Hadoop&lt;/a&gt;&lt;/p&gt;&lt;p&gt;原文：&lt;a title="http://msdn.microsoft.com/zh-cn/magazine/hh852591.aspx" href="http://msdn.microsoft.com/zh-cn/magazine/hh852591.aspx"&gt;http://msdn.microsoft.com/zh-cn/magazine/hh852591.aspx&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;最近关于&amp;ldquo;物联网&amp;rdquo;(IoT) 的争论有很多，而且理由都很充分。 Ericsson 的 CEO Hans Vestberg 预测到 2020 年将有 500 亿台设备连接到 Web（&lt;a href="http://bit.ly/yciS7r"&gt;bit.ly/yciS7r&lt;/a&gt;&amp;nbsp;[PDF 下载]）。 目前约有 15 亿台 PC 以及不足 10 亿台电话连接到 Web &amp;mdash; 500 亿相当于全球每个人约 7 台设备，这可以帮助您直观理解这一数字！ 市场研究机构 IDC 则预测到 2015 年将有超过 160 亿台设备连接到 Internet（参见&lt;strong&gt;图 1&lt;/strong&gt;）。 不可否认，也存在一些较保守的预测，但通过每个人提供的数字，我们看到 Internet 的角色正在发生巨大转变 &amp;mdash; 从为人们提供信息和娱乐内容到为支持设备的新兴应用程序提供连接服务。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh852591.GrabsMiller_Figure1_hires(en-us,MSDN.10).png" alt="" /&gt;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;图 1 IDC 预测会出现&amp;ldquo;嵌入式 Internet&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;这些庞大数字看似可信的理由是强大的驱动因素（商机和必然性）正在推动此类解决方案的快速增加。 《经济学家》(&lt;a href="http://economist.com/node/17388368"&gt;economist.com/node/17388368&lt;/a&gt;) 最近的一期杂志指出，&amp;ldquo;&amp;hellip;经济大潮的向前滚动不仅仅是为了符合技术公司和雄心勃勃的政治家的利益。 这种发展趋势已获得了动力，因为真的需要此类系统。&amp;rdquo;&lt;strong&gt;图 2&lt;/strong&gt;&amp;nbsp;按应用领域显示了此类解决方案的增长态势。 例如，在欧洲强制实施智能能源系统是一个必然结果。 如果我们还不能管理能源消耗，则无法构建所需的能源产生功能。 在商机方面，简单的、无所不在的自动售货机就是一个很好的示例。 在连接该设备之后，可以根据需要而不是某项并非最理想的计划来指派服务人员。 如果本地需求增加或商品接近到期日，甚至还可以动态更改价格。 可以报告停电情况以便督促立即更换易腐商品。 换句话说，连接实现了更高效的全新业务模型。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh852591.GrabsMiller_Figure2_hires(en-us,MSDN.10).png" alt="" /&gt;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;图 2 按行业划分的应用增长态势&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;通过引用连接设备的数量来描述此转变肯定很形象，但它也会让人产生一些误解 &amp;mdash; 此种转变并不表明几十万传统嵌入式程序员将充分就业。 这些设备仅是将其他设备集成到 Internet 的所有方面（包括分析、云、Web 应用程序、PC 和移动界面等）的复杂解决方案的终结点。&lt;/p&gt;&lt;p&gt;因此，看待此问题的方法是当前构建基于 Web 的应用程序的每个人将需要集成设备并帮助开发新业务和新业务模型。 换句话说，即使您不是嵌入式开发者，也不在构建嵌入式设备的商店中工作，这也是一个值得您评估的非常诱人的商机。 您当前具备的 Microsoft 技能将使您能够在 IoT 方面取得成功。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;对设备数据进行分析&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;ldquo;数据已成为新的货币&amp;rdquo;，Windows Embedded 团队的总经理 Kevin Dallas 在最近的采访中说道 (&lt;a href="http://bit.ly/wb1Td8"&gt;bit.ly/wb1Td8&lt;/a&gt;)。 与当前 Internet 应用程序相比，IoT 涉及信息生成、管理和访问。 让我们比较一下当今典型 Internet 应用与 IoT 应用的数据特征。 您或许和其他几百万人均使用多家金融机构共享的流行机制联机支付帐单。 您每月登录多次，查看一些页面并提交付款信息。 所有这些数据都是使用当您开始与系统互动时所运行的查询从传统数据库中提取的。 您下载的页面量可能很大，但交互非常少，即使它们生成的有价值的信息（付款信息、个人信息更新等）需要长时间保存也是如此。&lt;/p&gt;&lt;p&gt;将此应用与能源管理系统进行对比，该系统中可能有 5000 万座大楼（商业楼和住宅楼）正在提供输入。 输入由内部的多个本地终结点（例如，房子）使用发布到后端的单个聚合视图生成。 该数据包括返回给大楼的成为定价和记帐基础的即时使用信息和强制性控制。 此系统将与价值相对较低的数据进行非常频繁的交互，这些数据在您计算系统的当前状态和该终结点的趋势数据时不一定有意义。 不过，该系统需要能够立即对可能威胁其运行的情况（例如需求剧增引发网格超载和停电）做出响应。 在这种情况下，广播信息可以立即减少能源消耗。 此类系统需要连续分析传入数据并比较发展趋势，以识别指示较高停电风险的模式。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 3&lt;/strong&gt;&amp;nbsp;演示 IoT 应用的典型体系结构。 堆栈底部显示了各种资产或设备，它们根据不同应用领域配备了不同种类的传感器。 这些传感器通常生成应用领域快速处理和分析所需的连续数据源。 根据设备的功能，设备本身或许能够在本地执行一些处理。 这称为本地分析，并且 .NET Micro Framework 之类的工具可帮助您在设备传递数据之前执行该本地处理。 IoT 应用使用 Internet 协议传递设备数据，以便可以对数据进行全局分析。 而全局分析的结果（例如电网的整体运行状况）是管理运营的最终用户或业务决策者关注的内容。 分析还可以驱动根据传入数据中呈现的情况自动采取操作的封闭系统。 如果资产可接收来自全局分析的反馈（例如，影响行为更改或改进操作），则这些方法将非常有用。 需要连续计算推动这些过程的全局分析并尽快提供结果。 另外，分析频繁参考随传感器数据一起提供的时间和时间戳。 因此，仅将此类数据放入数据库中并对其运行定期查询不是适当的方法。 幸运的是，Microsoft StreamInsight 支持不同的方法。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh852591.GrabsMiller_Figure3_hires(en-us,MSDN.10).png" alt="" /&gt;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;图 3 物联网应用的典型体系结构&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Microsoft StreamInsight&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Microsoft StreamInsight 旨在对连续到达的数据提供及时反应，而不将数据写入磁盘中以进行分析和查询。 许多 IoT 应用领域需要在从源获取数据后几乎实时地分析传入数据。 考虑我们提到的智能电网应用，它需要对剧增的电力需求快速做出反应以重新平衡电网的运营能力。 许多 IoT 应用具有相同的需求： 需要连续分析的数据处理和引人注目的延迟。 分析必须连续，因为数据源不断地生成新数据。 许多方案需要识别只能通过分析传入数据而呈现的情况并对其快速做出反应，因此它们需要低延迟分析和几乎立即提供的结果。 这些要求使在执行分析之前将数据存储在关系数据库中变得不切实际。&lt;/p&gt;&lt;p&gt;我们将这些应用称为事件驱动应用，而 IoT 正是此类功能发挥作用的一个方案。 StreamInsight 是一个用于构建这些高度可伸缩、低延迟的事件驱动应用的强大平台。 它是自 2008 R2 版本发布以后的 Microsoft SQL Server 的一部分。 在事件驱动处理和基于丰富表达时间的分析方面，StreamInsight 为 SQL Server 提供了补充。 使用 StreamInsight，将以生成数据的速度，而不是处理传统数据库报告的速度提供业务见解。&lt;/p&gt;&lt;p&gt;可供人们立即使用或使应用程序能够自动对事件做出反应的分析结果可帮助企业更及时且更好地了解其相关运营情况，甚至可以自动执行部分运营工作。 它们也可以对传感器或设备数据中出现的重要情况、商机或趋势更快地做出反应。&lt;/p&gt;&lt;p&gt;要编写 StreamInsight 应用程序，开发者可使用 Microsoft .NET Framework、LINQ 和 Microsoft Visual Studio 等熟悉的工具。&amp;nbsp;&lt;strong&gt;图 4&lt;/strong&gt;&amp;nbsp;描述了 StreamInsight 应用程序的开发者和运行时体验并介绍了一些关键概念。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh852591.GrabsMiller_Figure4_hires(en-us,MSDN.10).png" alt="" /&gt;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;图 4 StreamInsight 应用程序开发和运行时&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;简单的 IoT 应用&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;让我们更深入地了解可能的 IoT 应用方案；然后我们将构建它。 在我们的端到端示例中，我们将关注一个简单方案，该方案使用运动传感器监视旋转设备，例如涡轮或风车。 这很重要，因为振动过大会导致出现紧急情况，在这种情况下，设备可能出现故障，并且如不立即停止，则会出现严重损坏。 为可靠地检测此情况，每台设备均配备多个跟踪运动的传感器。 单个传感器中的运动激增可能仅指示该传感器的数据读数不可靠，但多个传感器中同时出现异常剧烈的运动则表明出现紧急情况。 例如对于大型涡轮，您可能希望引发警报，甚至自动关闭设备。 除了持续检查此类情况外，我们还希望为操作员提供一个仪表板，它提供了设备状态的近实时视图。&lt;/p&gt;&lt;p&gt;若要构建此方案，我们需要满足以下要求和解决以下技术难题：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;设备需要捕获哪些数据？&lt;/li&gt;&lt;li&gt;我们使用哪些传感器来测量数据？&lt;/li&gt;&lt;li&gt;设备如何将其传感器读数传送到 Internet？&lt;/li&gt;&lt;li&gt;我们如何将设备数据收集到一个位置以进行分析？&lt;/li&gt;&lt;li&gt;我们如何可以连续分析传入数据并对紧急情况快速做出反应？&lt;/li&gt;&lt;li&gt;我们如何跨多台设备及时关联传感器读数，以便可以检查全局情况？&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;让我们看一下满足这些要求并实现端到端方案的方式。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;IoT 应用： 实现要点&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;下面是实现上一节中所述的 IoT 应用的一些关键步骤。 我们将首先讨论设备，再转到输出的可视化，然后转到填充仪表板的跨设备分析。&lt;/p&gt;&lt;p&gt;设备。为构建传感器设备，我们首先从 Netduino Plus 着手，它是运行 .NET Micro Framework、具有 128K SRAM 的受欢迎的小型开发板。 我们添加了名为 WiFly GSX Breakout 的常见爱好者 Wi-Fi 无线电，并在自定义 PCB 板上安装了实际传感器，包括三轴加速计。 我们对设备进行编程，以将传感器读数的每秒更新发送给 Web 服务，该服务充当从所有设备收集数据并进行处理的中心。&lt;/p&gt;&lt;p&gt;我们对 Web 服务使用 RESTful 连接 &amp;mdash; 它只是包含逗号分隔名称-值对的 HTTP POST。 当然，您可以从支持 HTTP 的任何种类的设备执行此操作。 我们选择使用 .NET Micro Framework，以便整个应用程序（包括设备、Web 服务、StreamInsight 适配器、Silverlight 仪表板等）全部可以使用单个编程模型 (.NET) 和工具链 (Visual Studio) 进行编写。 很明显，如果您具有 .NET 技能，则无需招聘新员工或将您的 IoT 项目的一部分外包给外部嵌入式商店；您具有完全执行它的技能。 例如，设置加速计时只需几行代码即可访问 AnalogInput 类并调用 Read 方法：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this.analogInputX = new AnalogInput(pinX);&lt;/li&gt;&lt;li&gt;this.analogInputY = new AnalogInput(pinY);&lt;/li&gt;&lt;li&gt;this.analogInputZ = new AnalogInput(pinZ);&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; rawZ = analogInputZ.Read();&lt;/li&gt;&lt;li&gt;rawY = analogInputY.Read();&lt;/li&gt;&lt;li&gt;rawX = analogInputX.Read();&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;在读取传感器输入并设置 HTTP 消息内容格式后，发送数据所需的一切都包括在&lt;strong&gt;图 5&lt;/strong&gt;&amp;nbsp;中。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 5 提交传感器数据&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected void submitSensorData(string uri, string payload)&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp; // Message format&lt;/li&gt;&lt;li&gt;&amp;nbsp; StringBuilder sb = new StringBuilder(256);&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; "POST /Website/Services/DataService.aspx?method=SaveDeviceData HTTP/1.1\n");&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append("User-Agent: NetduinoPlus\n");&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append("Host: 192.168.1.101\n");&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append("Connection: Keep-Alive\n");&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append("Content-Length: ");&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append(payload.Length.ToString());&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append("\n");&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append(payload);&lt;/li&gt;&lt;li&gt;&amp;nbsp; sb.Append("\n");&lt;/li&gt;&lt;li&gt;&amp;nbsp; try&lt;/li&gt;&lt;li&gt;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpResponse response = webServer.SendRequest(uri, 80, request);&lt;/li&gt;&lt;li&gt;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; catch&lt;/li&gt;&lt;li&gt;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;在服务器端，我们实现方法 SaveDeviceData，设备要将其消息发布给该方法。 我们拆分消息字符串并分析 MAC 地址、时间戳和负载数据，例如来自加速计的运动读数。 我们使用所有这些信息来填充传递给 StreamInsight 以进行后续分析的 DeviceData 对象（请参见&lt;strong&gt;图 6&lt;/strong&gt;）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 6 填充 DeviceData 对象&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; private int SaveDeviceData()&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; List&amp;lt;string&amp;gt; data = record.Split(',').ToList();&lt;/li&gt;&lt;li&gt;&amp;nbsp; DeviceData deviceData = new DeviceData();&lt;/li&gt;&lt;li&gt;&amp;nbsp; deviceData.MAC = NormalizeMAC(data[0].Trim());&lt;/li&gt;&lt;li&gt;&amp;nbsp; deviceData.DateTime = DateTime.UtcNow;&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; deviceData.Motion = Convert.ToDecimal(data[2].Substring(data[2].IndexOf(":") + 1));&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Communicate each new device data record to StreamInsight&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;li&gt;&amp;nbsp; DeviceDataStreaming streaming = (DeviceDataStreaming)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HttpContext.Current.Application[Global.StreamingIdentifier];&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; streaming.TrackDeviceData(deviceData);&lt;/li&gt;&lt;li&gt;...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;仪表板。现在我们要构建允许设备操作员查看设备上传感器的当前状态的仪表板。 为便于演示，我们将仅关注一台设备。&amp;nbsp;&lt;strong&gt;图 7&lt;/strong&gt;&amp;nbsp;显示了一个此类仪表板的示例。 让我们从左侧开始，查看传感器数据的不同视图。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh852591.GrabsMiller_Figure8_hires(en-us,MSDN.10).png" alt="" /&gt;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;图 7 用于设备监视的仪表板&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;移动平均数视图： 左下角的数据网格显示设备的传感器读数，其中包括光线、温度和运动值以及设备 ID 和时间戳。 正如您可以从时间戳中看到的，这些值每秒更新一次。 但仪表板不显示原始传感器值，而是显示 10 秒内传感器数据的移动平均数。 这意味着会使用最近 10 秒内数据的平均数每秒更新一次值。 使用移动平均数是一种常见的简单技术，可防止出现使用低成本传感器时偶尔出现的异常值和不良数据。&lt;/p&gt;&lt;p&gt;趋势线视图： 在右下角，仪表板显示传感器的趋势线。 趋势线视图的走势由左侧数据网格中显示的移动平均数决定。&lt;/p&gt;&lt;p&gt;警报视图： 右上角的视图显示警报的数据网格。 如果检测到临界情况，则会引发显示时间和其他信息（例如严重性和状态）的警报。&lt;/p&gt;&lt;p&gt;分析。现在让我们了解幕后操作并讨论处理传入传感器数据并计算仪表板可视化的结果的分析。 我们使用 StreamInsight 执行分析。 以下类表示设备数据，其中包括 MAC 地址、时间戳和传感器值：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class DeviceData&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp; public string MAC { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; public DateTime DateTime { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; public decimal?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Light { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; public decimal?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Temperature { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; public decimal?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Motion { get; set; }&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;此类定义单个事件的形状，但我们想要开始讨论许多事件。 为此，我们为 StreamInsight 定义了 Observable 数据源。 这仅是实现 System.IObservable 接口的数据源：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class DeviceDataObservable : IObservable&amp;lt;DeviceData&amp;gt;&lt;/li&gt;&lt;li&gt;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;在定义 .NET Framework 序列（例如 Enumerable 或类似的 Observable）后，即可开始编写对这些集合的 StreamInsight 查询。 让我们快速了解一下其中某些关键查询。 第一个查询获取 Observable 作为输入并生成 StreamInsight 点事件流，以使用设备数据中的&amp;ldquo;DateTime&amp;rdquo;字段作为 StreamInsight 事件的时间戳。 在下一个 LINQ 语句中，我们获取此流作为输入，并按 MAC 地址对数据进行分组。 对于每个组，我们然后应用窗口大小为 10 秒的跳跃窗口（基于时间的一部分事件），并让窗口每秒重新计算一次。 在每个窗口中，我们计算温度、光线和运动的平均数。 这为我们提供了每秒重新计算一次的每台设备的移动平均数。&amp;nbsp;&lt;strong&gt;图 8&lt;/strong&gt;&amp;nbsp;显示了用于返回 StreamInsight 事件流形式的结果的函数实现此过程的代码。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 8 获取移动平均数&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static CepStream&amp;lt;AverageSensorValues&amp;gt; GroupedAverages(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Application application,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DeviceDataObservable source)&lt;/li&gt;&lt;li&gt;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var q1 = from e1 in source.ToPointStream(application,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; e =&amp;gt; PointEvent.CreateInsert(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new DateTimeOffset(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; e.DateTime.ToUniversalTime()),e),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AdvanceTimeSettings.StrictlyIncreasingStartTime,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "Device Data Input Stream")&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select e1;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var q2 = from e2 in q1&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; group e2 by e2.MAC into groups&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; from w in groups.HoppingWindow(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TimeSpan.FromSeconds(10),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TimeSpan.FromSeconds(1))&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select new AverageSensorValues&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DeviceId = groups.Key,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Timestamp = null,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AvgTemperature = w.Avg(t =&amp;gt; t.Temperature),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AvgLight = w.Avg(t =&amp;gt; t.Light),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AvgMotion = w.Avg(t =&amp;gt; t.Motion)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return q2;&lt;/li&gt;&lt;li&gt;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;这是考虑实现警报查询的最佳位置。 请记住，当有多个运动传感器的读数同时高于运动阈值时，将触发警报。 只需对刚计算的分组平均数使用几个 StreamInsight LINQ 语句便可处理此问题。 通过将警报阈值的更改表示为名为 AlarmThresholdSignal 的事件流，第一个查询 q3 应用了一个极佳的技巧。 此查询将阈值与来自前一个查询的平均数流联接，然后仅筛选高于阈值的事件：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var q3 = from sensor in GroupedAverages(application, source)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; from refdata in AlarmThresholdSignal(application, alarmsthresholds)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where (sensor.AvgMotion !=&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; null &amp;amp;&amp;amp; (double) sensor.AvgMotion &amp;gt; refdata.Threshold)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select new&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmDevice = sensor.DeviceId,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmInfo = "This is a test alarm for a single device",&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;下一个查询使用 StreamInsight 快照窗口来识别事件状态更改的时间点。 如果从前一个筛选查询产生了一个新事件，则这是新快照，并且该快照操作生成一个新窗口，其中包含与触发快照窗口的事件一致或重叠的所有事件。 下面的代码对创建快照窗口时高于警报阈值的事件进行计数：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var alarmcount = from win in q3.SnapshotWindow()&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select new&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmCount = win.Count()&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;最后一步检查计数是否显示有多台设备将发出警报指示：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var filteralarms = from f in alarmcount&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where f.AlarmCount &amp;gt;= 2&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; select new AlarmEvent&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmTime = null,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmInfo = "Now we have an alarm across multiple devices",&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmKind = 0,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmSeverity = 10,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AlarmStatus = 1&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;现在，我们只需将包含平均传感器值和警报的输出流从 StreamInsight 传送到 UI。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;使输出流传送到 UI&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;使用在服务器端生成结果流的 StreamInsight，我们需要一种方法来将这些流传送给使用者。 使用者可能不在服务器进程中运行，并可能使用轻型 Web 应用程序来可视化结果。 如果您使用 Silverlight，则双工协议很方便，因为它支持从服务器到客户端的连续的基于推送的传送。 HTML5 Web 套接字也是引人注目的替代方法。 无论如何，您都希望轻松地在服务器端添加新分析并能够轻松地将它们与 UI 连接，而无需拆分 UI 和承载 StreamInsight 的进程之间的客户端-服务器接口。 如果 UI 和服务器之间的负载适中，则您可以将服务器端的结果序列化为 XML 并在客户端对其进行反序列化。 这样，您只需关注线路上和您的客户端-服务器接口中的 XML，以及指示要反序列化的类型的附加 Cookie。 下面是几段关键代码。&lt;/p&gt;&lt;p&gt;第一个代码段是 Windows Communication Foundation 协定，它用于传送 XML 序列化字符串形式的事件数据以及指示类型的 GUID：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [ServiceContract]&lt;/li&gt;&lt;li&gt;public interface IDuplexClient&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp; [OperationContract(IsOneWay = true)]&lt;/li&gt;&lt;li&gt;&amp;nbsp; void Receive(string eventData, Guid guid);&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;现在，我们可以使用数据协定为结果事件结构添加批注以使其可序列化，如&lt;strong&gt;图 9&lt;/strong&gt;&amp;nbsp;所示。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 9 为事件结构添加批注&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [DataContract]&lt;/li&gt;&lt;li&gt;public class AverageSensorValues : BaseEvent&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp; [DataMember]&lt;/li&gt;&lt;li&gt;&amp;nbsp; public new static Guid TypeGuid =&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Guid.Parse("{F67ECF8B-489F-418F-A01A-43B606C623AC}");&lt;/li&gt;&lt;li&gt;&amp;nbsp; public override Guid GetTypeGuid() { return TypeGuid; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; [DataMember]&lt;/li&gt;&lt;li&gt;&amp;nbsp; public string DeviceId { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; [DataMember]&lt;/li&gt;&lt;li&gt;&amp;nbsp; public DateTime?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Timestamp { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; [DataMember]&lt;/li&gt;&lt;li&gt;&amp;nbsp; public decimal?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AvgLight { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; [DataMember]&lt;/li&gt;&lt;li&gt;&amp;nbsp; public decimal?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AvgTemperature { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; [DataMember]&lt;/li&gt;&lt;li&gt;&amp;nbsp; public decimal?&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; AvgMotion { get; set; }&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;现在，我们可以轻松地序列化服务器端的结果事件并将其传送到客户端，如&lt;strong&gt;图 10&lt;/strong&gt;&amp;nbsp;所示。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 10 从服务器发送结果事件&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; static public void CallClient&amp;lt;T&amp;gt;(T eventData) where T : BaseEvent&lt;/li&gt;&lt;li&gt;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (null != client)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var xmlSerializer = new XmlSerializer(typeof(T));&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var stringBuilder = new StringBuilder();&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var stringWriter = new StringWriter(stringBuilder);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlSerializer.Serialize(stringWriter, eventData);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; client.Receive(stringBuilder.ToString(), eventData.GetTypeGuid());&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;在客户端上，我们反序列化双工服务的回调方法中的事件，然后根据接收到的事件的类型将其分支到不同的方法中，如&lt;strong&gt;图 11&lt;/strong&gt;&amp;nbsp;所示。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;图 11 在客户端上接收和反序列化事件&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void proxy_ReceiveReceived(object sender, ReceiveReceivedEventArgs e)&lt;/li&gt;&lt;li&gt;{&lt;/li&gt;&lt;li&gt;&amp;nbsp; if (e.Error == null)&lt;/li&gt;&lt;li&gt;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (AverageSensorValues.TypeGuid == e.guid)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProcessAverageSensorValues(Deserialize&amp;lt;AverageSensorValues&amp;gt;(e.eventData));&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else if (AlarmEvent.TypeGuid == e.guid)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProcessAlarms(Deserialize&amp;lt;AlarmEvent&amp;gt;(e.eventData));&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProcessUnknown();&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;使用这些查询并传送到相应的 Web 应用程序，您现在可以选取几台设备并摇动它们，直到一些设备读数高于警报阈值。 然后，UI 将生成这些红色警报之一，如&lt;strong&gt;图 12&lt;/strong&gt;&amp;nbsp;所示。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh852591.GrabsMiller_Figure13_hires(en-us,MSDN.10).png" alt="" /&gt;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;图 12 包含警报的设备仪表板&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;因为新数据会不断进入几乎实时的仪表板，所以 ObservableCollections 对更新 UI 极其有用。 如果您使数据网格和趋势线基于这些 Observable 集合，则无需担心代码中的更新部分。 这些集合将在后台为您自动执行此操作。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;前景&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在此实现中，设备与常规 Web 服务通信，该服务可以运行在连接到 Internet 的普通 PC 上。 但云计算是一个吸引人的替代方法；您不一定需要为自己的 Web 服务器拥有硬件并运行软件。 云中的服务可以充当为您的应用程序收集所有设备数据的中心。 这还使您能够在设备数量增加或部署针对设备数据的其他分析时，非常轻松且灵活地扩展您的处理能力。 Microsoft 计划将 StreamInsight 功能作为 Windows Azure 中的一项服务（StreamInsight 项目代码名称&amp;ldquo;Austin&amp;rdquo;）提供。 通过提供预定义的通信终结点和协议，Austin 将使您能够轻松地将设备连接到 Microsoft 云中丰富的分析处理功能。 如果您将 IoT 应用程序部署到 Windows Azure 中，则将自动获得灵活扩展和即付即用等云好处，以便管理设备连接和对设备数据执行丰富的分析。&lt;/p&gt;&lt;p&gt;另一个重大转变涉及最近进行的 W3C 标准化工作。 IoT 应用程序的最重要计划是 HTML5 和 Web 套接字。 HTML5 为丰富的 Web 应用程序（例如我们实现的仪表板）提供平台。 而 WebSocket 又简化了浏览器和 Web 服务器之间基于 TCP 的全双工通信，尤其是针对连续处理传感器数据时所要求的结果传送的推送模型。&lt;/p&gt;&lt;p&gt;连接的设备开创了一个令人兴奋的新应用领域，并且 Microsoft 现在提供用于构建这些 IoT 应用程序的工具。 我们在这里介绍了如何在设备级别通过熟悉的接口利用您的 .NET Framework 技能，以及如何通过 Web 服务为 StreamInsight 的强大分析功能提供数据。 立即开始使用连接设备构建您的 IoT 应用程序！&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Torsten Grabs&lt;/strong&gt;&amp;nbsp;&lt;em&gt;是 Microsoft SQL Server 部门的首席项目经理。 他具有 10 余年 Microsoft SQL Server 产品的使用经验，并获得瑞士苏黎世的瑞士联邦理工学院的计算机科学博士学位。&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Colin Miller&lt;/strong&gt;&amp;nbsp;&lt;em&gt;在 PC 软件领域工作了 25 年（其中有 15 年效力于 Microsoft），研究方向涉及数据库、桌面发布、消费类产品、Word、Internet Explorer、Passport (LiveID) 及联机服务。 他是 .NET Micro Framework 的产品部经理。&lt;/em&gt;&lt;/p&gt;&lt;p&gt;衷心感谢以下技术专家对本文的审阅：&amp;nbsp;&lt;strong&gt;Rafael Fernandez Moctezuma&lt;/strong&gt;&amp;nbsp;和&amp;nbsp;&lt;strong&gt;Lorenzo Tessiore&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;原文地址：&lt;a title="http://msdn.microsoft.com/zh-cn/magazine/hh205648.aspx" href="http://msdn.microsoft.com/zh-cn/magazine/hh205648.aspx"&gt;http://msdn.microsoft.com/zh-cn/magazine/hh205648.aspx&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://archive.msdn.microsoft.com/mag201106StreamInsig"&gt;&lt;strong&gt;下载代码示例&lt;/strong&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;生产线的产量下降后，将容易出现用户媒体流跳过这些流程，或者您的一个产品成为了&amp;ldquo;必需产品&amp;rdquo;的情况。 真正的窍门是在这些情况发生时进行识别，或根据以往趋势对其做出预测。&lt;/p&gt;&lt;p&gt;成功预测这些情况需要使用近乎实时的方法。 在对相关数据进行提取、转换并加载到 SQL Server Analysis Services (SSAS) 等传统商业智能 (BI) 解决方案中时，情况早已发生改变。 同样，一些系统依靠请求-响应模式来从事务性数据存储（如 SQL Server Reporting Services 或 SSRS、报告）中请求已更新的数据，像这样的系统总是在接近请求-轮询间隔结束时运行陈旧数据。 轮询间隔通常是固定的，因此即使突然发生有趣的活动，消耗系统也不会知道，直到进入下一个间隔。 相反，消耗系统应该在满足趣味条件时连续收到通知。&lt;/p&gt;&lt;p&gt;在检测新兴趋势时，时间间隔至关重要 - 在过去的五分钟内，一个特定项目发生了 100 次购买，显而易见，这比过去五个月间的持续购买更能指示新兴趋势。 SSAS 和 SSRS 等传统系统需要开发人员通过事务性存储中多维数据集或时间戳列中的单独维度来自行跟踪数据的及时性。 理论上，用于识别新兴情况的工具可能具有时间内置的概念，并能提供使用该工具所需的丰富 API。&lt;/p&gt;&lt;p&gt;最后，对未来的准确指示来源于对过去的分析。 实际上，这就是传统 BI 的所有功能 - 对大量的历史数据进行汇总和分析，从而识别趋势。 遗憾的是，与更多的事务性系统相比，在使用这些系统时需要不同的工具和查询语言。 成功识别新兴情况需要实现过去数据和当前数据的无缝关联。 只有当对这两种数据使用相同的工具和查询语言时，才可能实现这种紧密集成。&lt;/p&gt;&lt;p&gt;对于生产线监视等特定情况，可通过存在的针对性极强的自定义工具来执行这些功能，但是这些工具通常比较昂贵且用途并不广泛。&lt;/p&gt;&lt;p&gt;为了防止生产线产量下降或确保您的产品定价合适，关键在于具有足够的响应能力，能够根据情况的更改而进行识别与调整。 若要轻松快速地识别这些情况，历史查询和实时查询应使用相同的开发人员友好的工具集和查询语言，系统应该以近乎实时的方式来处理大量的数据（大约为每秒成百上千个事件），同时引擎应该足够灵活，能够处理跨越多个问题域的情况。&lt;/p&gt;&lt;p&gt;幸运的是，存在这样的工具。 它称为 Microsoft StreamInsight。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;StreamInsight 体系结构概述&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;StreamInsight 是一种复杂事件处理引擎，它每秒能够处理成百上千的事件，且延迟极低。 它可以由任何进程（如 Windows 服务）托管，也可以直接嵌入任何应用程序。 StreamInsight 具有简单的适配器模型，用于输入和输出数据，并且实时数据和历史数据的查询像任何其他来自任何 Microsoft .NET Framework 语言的程序集一样使用获取的相同 LINQ 语法。 其作为 SQL Server 2008 R2 的一部分授予许可。&lt;/p&gt;&lt;p&gt;StreamInsight 的高级体系结构非常简单：通过输入适配器从各种源收集事件。 这些事件均通过查询进行分析和转换，并且查询结果通过输出适配器分发给其他系统和人。&amp;nbsp;&lt;strong&gt;图 1&lt;/strong&gt;&amp;nbsp;显示了这一简单结构。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh205648.Pierry_Figure1_hires(en-us,MSDN.10).jpg" alt="" /&gt;&lt;/p&gt;&lt;p&gt;图 1&amp;nbsp;&lt;strong&gt;Microsoft StreamInsight 高级体系结构&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;就像面向服务的体系结构关注消息，而数据库系统关注行一样，StreamInsight 等复杂事件处理系统按照事件进行组织。 事件是简单的数据段以及与该数据相关的时间 - 与一天中特定时间的传感器读数或股票行情价格相似。 事件所携带的数据称为它的负载。&lt;/p&gt;&lt;p&gt;StreamInsight 支持三种类型的事件。 点事件是即时且不持续的事件。 间隔事件是其负载与特定时间段相关的事件。 边缘事件与间隔事件相似，但当边缘事件到达时，其持续时间未知。 而系统设置了开始时间，且事件实际上具有无限持续时间，直到另一个边缘事件到达才会为这一事件设置结束时间。 例如，速度计读数可能为点事件，因为它不断更改，但是超市的牛奶价格可能为边缘事件，因为其关联时间较长。 当牛奶的零售价格更改时（比如，由于分销商定价发生更改），新价格的持续时间未知，因此，与间隔事件相比，边缘事件要更为合适。 稍后，当分销商再次更新其定价时，新的边缘事件将覆盖先前定价更改的持续时间，而另一个边缘事件将设置新的价格以便继续。&lt;/p&gt;&lt;p&gt;StreamInsight 中的输入适配器和输出适配器是适配器设计模式的抽象示例。 StreamInsight 引擎在其自有的事件表示上运行，但是这些事件的实际来源可能有较大差异，范围从专有接口到硬件传感器到由企业的应用程序生成的状态消息。 输入适配器将源事件转换为引擎能够理解的事件流。&lt;/p&gt;&lt;p&gt;来自 StreamInsight 查询的结果表示特定商业知识，且能够高度专业化。 将这些结果路由至最合适的地点，这点至关重要。 输出适配器可用于将事件的内部表示转换为打印到控制台的文本、通过 Windows Communication Foundation (WCF) 发送到另一个系统以供处理的消息，甚至 Windows Presentation Foundation 应用程序中图表上的点。 有关使用文本文件、WCF 和 SQL 等的示例适配器可从&amp;nbsp;&lt;a href="http://streaminsight.codeplex.com/"&gt;streaminsight.codeplex.com&lt;/a&gt;&amp;nbsp;获得。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;StreamInsight Queries by Example&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;乍一看，StreamInsight 查询似乎与从数据库中查询行相似，但是两者之间存在重大差异。 查询数据库时，系统会构造并执行查询，同时返回结果。 如果基础数据发生更改，输出并不会因为已运行查询而受影响。 数据库查询结果表示某一时刻的快照，可以通过请求-响应模式使用。&lt;/p&gt;&lt;p&gt;StreamInsight 查询为现有查询。 随着新输入事件的到达，查询不断响应，并且根据需要创建新的输出事件。&lt;/p&gt;&lt;p&gt;本文中的查询示例来自可供下载的示例解决方案。 这些示例开始较简单，但随着查询语言新功能的引入，功能变得更加强大。 所有查询都使用同一负载类。 以下是一个简单类的定义，该类具有 Region 属性和 Value 属性：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class EventPayload {&lt;/li&gt;&lt;li&gt;&amp;nbsp; public string Region { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp; public double Value { get; set; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&amp;nbsp; public override string ToString() {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return string.Format("{0}\t{1:F4}", Region, Value);&lt;/li&gt;&lt;li&gt;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;示例应用程序中的查询使用一台输入适配器和一台输出适配器来进行，输入适配器可随机生成数据，输出适配器只需将各事件写入控制台。 为清晰起见，对示例应用程序中的适配器进行了简化。&lt;/p&gt;&lt;p&gt;若要运行每个查询，请在示例解决方案中取消注释 Program.cs 文件中的行，该示例解决方案可将查询分配给称为&amp;ldquo;template&amp;rdquo;的本地变量。&lt;/p&gt;&lt;p&gt;以下是一个基本查询，它通过 Value 属性来筛选事件：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var filtered =&lt;/li&gt;&lt;li&gt;&amp;nbsp; from i in inputStream&lt;/li&gt;&lt;li&gt;&amp;nbsp; where i.Value &amp;gt; 0.5&lt;/li&gt;&lt;li&gt;&amp;nbsp; select i;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;具有使用 LINQ 经验的任何开发人员应该非常熟悉此查询。 因为 StreamInsight 使用 LINQ 作为它的查询语言，因此此查询与 LINQ to SQL 查询类似，访问数据库或对 IList 进行内存中筛选。 当事件从输入适配器到达时，其负载将受到检查，并且如果 Value 属性的值大于 0.5，事件将被传递到输出适配器，并在此将其打印到控制台。&lt;/p&gt;&lt;p&gt;应用程序运行时，可以看到事件不断到达输出中。 这实际上是一个推模型。 当事件到达时，StreamInsight 会计算来自输入的新输出事件，这与数据库等拉模型不同，在拉模型中，应用程序必须定期轮询数据源，以查看新数据是否已经到达。 这能与 Microsoft .NET Framework 4 中可用的 IObservable 支持完美结合，我们将在后续章节中对此进行介绍。&lt;/p&gt;&lt;p&gt;使用推模型代替轮询来处理连续数据是个非常好的主意，但是 StreamInsight 的真正功能体现在查询时间相关的属性上。 当事件通过输入适配器到达时，它们获得了一个时间戳。 该时间戳可能来自数据源本身（假设事件表示历史数据，且带有用于存储时间的显示列），或者可以设置为事件到达的时间。 实际上，时间是 StreamInsight 查询语言中的第一个类。&lt;/p&gt;&lt;p&gt;查询通常与标准数据库查询类似，标准数据库查询在尾部粘贴有时间限制符，如&amp;ldquo;每五秒&amp;rdquo;或&amp;ldquo;五秒的时间跨度上每三秒&amp;rdquo;。例如，以下是一个简单查询，它每五秒查询一次 Value 属性的平均值：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var aggregated =&lt;/li&gt;&lt;li&gt;&amp;nbsp; from i in inputStream&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .TumblingWindow(TimeSpan.FromSeconds(5),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HoppingWindowOutputPolicy.ClipToWindowEnd)&lt;/li&gt;&lt;li&gt;&amp;nbsp; select new { Avg = i.Avg(p =&amp;gt; p.Value)};&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;数据窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;因为时间概念是复杂事件处理系统的基础必需概念，因此应以简单的方式来使用系统中查询逻辑的时间组件，这点非常重要。 StreamInsight 使用窗口概念来表示按时间分组。 之前的查询使用翻转窗口。 应用程序运行时，查询将每五秒生成单个输出事件（窗口的大小）。 输出事件表示前五秒的平均值。 像 LINQ to SQL 或 LINQ to Object 一样，聚合方法（如 Sum 和 Average）能够将按时间分组的事件汇总为单个值，或可以使用 Select 将输出投影成不同格式。&lt;/p&gt;&lt;p&gt;翻转窗口只是另一种窗口类型的特例：跳跃窗口。 跳跃窗口也有大小，但是它们也具有不等于其窗口大小的跳跃大小。 这表示跳跃窗口可以互相重叠。&lt;/p&gt;&lt;p&gt;例如，窗口大小为五秒、跳跃大小为三秒的跳跃窗口将每三秒生成输出（跳跃大小），提供前五秒的平均值（窗口大小）。 它一次向前跳跃三秒，且持续五秒。&amp;nbsp;&lt;strong&gt;图 2&lt;/strong&gt;&amp;nbsp;显示分组为翻转窗口和跳跃窗口的事件流。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh205648.Pierry_Figure2_hires(en-us,MSDN.10).jpg" alt="" /&gt;&lt;/p&gt;&lt;p&gt;图 2&amp;nbsp;&lt;strong&gt;翻转窗口和跳跃窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;请注意，翻转窗口并不重叠，但是对于跳跃窗口，如果跳跃大小小于窗口大小，则可以重叠。 如果窗口重叠，事件将可能在多个窗口中结束，如同时存在于窗口 1 和窗口 2 中的第三个事件。 边缘事件（具有持续时间）也可能在窗口边缘重叠，并在多个窗口中结束，如翻转窗口中的倒数第二个事件。&lt;/p&gt;&lt;p&gt;另一种常见窗口类型为计数窗口。 计数窗口包含特定数量的事件，而不是某一时间点或时间段内的事件。 要查询最后三个到达的事件的平均数，可能需要使用计数窗口。 计数窗口当前的一个限制是不支持 Sum 和 Average 等内置聚合方法。 您必须创建用户定义的聚合。 下文会对这一简单流程进行介绍。&lt;/p&gt;&lt;p&gt;最后一种窗口类型为快照窗口。 在边缘事件的环境下，快照窗口最容易理解。 每次事件的开始或结束即表示当前窗口的完成和新窗口的开始。&amp;nbsp;&lt;strong&gt;图 3&lt;/strong&gt;&amp;nbsp;显示如何将边缘事件分组为快照窗口。 请注意每个事件边界触发窗口边界的方式。 E1 开始，w1 也开始。 当 E2 开始时，w1 完成，而 w2 开始。 下个边缘是 E1 结束，使得 w2 完成，而 w3 开始。 结果为三个窗口：包含 E1 的 w1，包含 E1 和 E2 的 w2 以及包含 E3 的 w3。 事件分组为窗口后，它们会受到拉伸，从而使事件的开始与结束时间与窗口的相同。&lt;/p&gt;&lt;p&gt;&lt;img src="http://i.msdn.microsoft.com/hh205648.Pierry_Figure3_hires(en-us,MSDN.10).jpg" alt="" /&gt;&lt;/p&gt;&lt;p&gt;图 3&amp;nbsp;&lt;strong&gt;快照窗口&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;更多复杂查询&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在提供可用窗口与基本查询方法（如地点、分组依据和排序依据）的情况下，可以进行多种查询。 以下是一个查询，其将输入事件按地区分组，然后使用跳跃窗口来输出最后一分钟各个 Region 的负载 Value 的总和：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var payloadByRegion =&lt;/li&gt;&lt;li&gt;&amp;nbsp; from i in inputStream&lt;/li&gt;&lt;li&gt;&amp;nbsp; group i by i.Region into byRegion&lt;/li&gt;&lt;li&gt;&amp;nbsp; from c in byRegion.HoppingWindow(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TimeSpan.FromMinutes(1),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TimeSpan.FromSeconds(2),&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HoppingWindowOutputPolicy.ClipToWindowEnd)&lt;/li&gt;&lt;li&gt;&amp;nbsp; select new {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Region = byRegion.Key,&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Sum = c.Sum(p =&amp;gt; p.Value) };&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;这些窗口使用两秒的跳跃大小，因此引擎每两秒发送输出事件。&lt;/p&gt;&lt;p&gt;因为查询运算符是在 IQueryable 接口中定义的，因此可以撰写查询。 以下代码使用上一个查询，其按地区查找总和，并计算总和最高的地区。 快照窗口允许事件流按总和分类，因此可以使用 Take 方法获取总和最高的地区：&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var highestRegion =&lt;/li&gt;&lt;li&gt;&amp;nbsp; // Uses groupBy query&lt;/li&gt;&lt;li&gt;&amp;nbsp; (from i in payloadByRegion.SnapshotWindow(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; SnapshotWindowOutputPolicy.Clip)&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; from sumByRegion in i&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; orderby sumByRegion.Sum descending&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; select sumByRegion).Take(1);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;一般情况是有关快速移动事件（如传感器中的读数）到慢速移动或静态参考数据（如传感器的固定位置）流的查询。 查询使用联接来实现此目的。&lt;/p&gt;&lt;p&gt;StreamInsight 联接语法与任何其他 LINQ 联接相同，但有一点需要注意：当事件的持续时间重叠时，它们才会联接在一起。 如果传感器 1 在时间 t1 报告了一个值，但是有关传感器 1 位置的参考数据仅对时间 t2 到 t3 有效，那么联接将不匹配。 持续时间的联接条件并没有明确写入查询定义中；这是 StreamInsight 引擎的基本属性。 使用静态数据时，通常情况下，输入适配器实际上将数据处理为带有无限持续时间的边缘事件。 这样将能成功完成到快速移动事件流的所有联接。&lt;/p&gt;&lt;p&gt;通过联接来关联多个事件流是一个非常强大的概念。 装配线、石油生产设施或高容量网站通常不会因为隔离的事件而发生故障。 一个用于触发温度警报的设备部件通常不会导致生产线瘫痪；生产线瘫痪可能由于多个原因造成，如温度在某一持续时间段内过高，同时某一工具使用过多，而操作员正在换班。&lt;/p&gt;&lt;p&gt;如果没有联接，隔离事件将不会有这么多的商业价值。 通过对历史数据使用联接和 StreamInsight 查询，用户可以将隔离流与非常具体的监控条件相关联，然后进行实时监控。 现有查询能够查找可能导致故障的情况，并自动生成可路由至系统的输出事件，该系统知道如何使过热的设备部件脱机，而不是等到该部件造成整条生产线停产。&lt;/p&gt;&lt;p&gt;在零售情况中，有关某段时间按项目划分的销售量的事件可以输入到定价系统和客户订单历史记录中，从而确保每个项目具有最佳的定价，或决定在用户结账前向其推荐的项目。 由于查询易于创建、修改和撰写，因此您可以从简单的情况开始，并随时间的流逝进行优化，从而增加业务价值。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;用户定义的聚合&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;StreamInsight 附带最常见的聚合函数，包括 Count、Sum 和 Average。 当这些函数不够时（或您需要在前文提到的计数窗口进行聚合），StreamInsight 支持用户定义的聚合函数。&lt;/p&gt;&lt;p&gt;要创建用户定义的聚合，其流程包括两个步骤：编写实际聚合方法，然后通过扩展方法将该方法公布到 LINQ。&lt;/p&gt;&lt;p&gt;进行第一步时，如果聚合与时间无关，则从 CepAggregate&amp;lt;TInput, TOutput&amp;gt; 继承，如果聚合与时间有关，则从 CepTimeSensitiveAggregate&amp;lt;TInput,TOutput&amp;gt; 继承。 这些抽象类具有单独的实现方法，称为 GenerateOutput。&amp;nbsp;&lt;strong&gt;图 4&lt;/strong&gt;&amp;nbsp;显示 EveryOtherSum 聚合的实现，这种聚合将每个其他事件加起来。&lt;/p&gt;&lt;p&gt;图 4&amp;nbsp;&lt;strong&gt;EveryOtherSum 聚合&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public class EveryOtherSum :&lt;/li&gt;&lt;li&gt;&amp;nbsp; CepAggregate&amp;lt;double, double&amp;gt; {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&amp;nbsp; public override double GenerateOutput(&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; IEnumerable&amp;lt;double&amp;gt; payloads) {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var sum = default(double);&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; var include = true;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (var d in payloads) {&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (include) sum += d;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; include = !include;&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return sum;&lt;/li&gt;&lt;li&gt;&amp;nbsp; }&lt;/li&gt;&lt;li&gt;}&lt;/li&gt;&lt;li&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;进行第二步时，需要在 CepWindow&amp;lt;TPayload&amp;gt; 上创建扩展方法，以便可以在查询中使用您的聚合。 CepUserDefinedAggregateAttribute 适用于扩展方法，以便通知 StreamInsight 在哪里可以找到聚合的实现（在这种情况下，类是在第一步中创建的）。 在可下载的示例应用程序中，本流程两个步骤的代码均可在 EveryOtherSum.cs 文件中找到。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;更多适配器信息&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;查询表示对适配器提供的数据进行操作的业务逻辑。 示例应用程序使用一台简单输入适配器和一台输出适配器来进行，输入适配器可生成随机数据，输出适配器可将数据写入控制台。 它们均遵循相似的模式，CodePlex 网站上提供的适配器也遵循这一模式。&lt;/p&gt;&lt;p&gt;StreamInsight 使用 Factory 模式来创建适配器。 给定配置类后，工厂可创建相应适配器的实例。 在示例应用程序中，输入适配器和输出适配器的配置类都非常简单。 输出适配器配置具有保存格式字符串的单个字段，可在编写输出时使用。 输入适配器配置具有填写生成随机事件之间睡眠时间的字段，也具有另一个称为 CtiFrequency 的字段。&lt;/p&gt;&lt;p&gt;CtiFrequency 中的 Cti 代表当前时间增量。 StreamInsight 使用 Cti 事件来帮助确保事件以正确的顺序传递。 默认情况下，StreamInsight 支持不按顺序到达的事件。 当通过查询传递事件时，引擎将自动对事件进行相应的排序。 然而，这一重新排序具有一定的限制。&lt;/p&gt;&lt;p&gt;假设事件真的能够以任意顺序到达。 那么怎么能够确定最早的事件已经到达，并因此通过查询来推送？ 这不可能，因为下一个事件的时间可能比您收到最早事件的时间更早。 StreamInsight 使用 Cti 事件来通知引擎比已接收事件更早的事件将不会到达。 Cti 事件实际上提示引擎去处理已经到达的事件，随后忽略或调整任何带有早于当前时间的时间戳的事件。&lt;/p&gt;&lt;p&gt;示例输入适配器生成排序事件流，因此它在每个生成的事件后自动插入一个 Cti 事件，以便保持流程的进行。 如果您已编写输入适配器，而您的程序没有产生输出，则请确保您的适配器插入了 Cti，因为如果没有 Cti，引擎将一直等下去。&lt;/p&gt;&lt;p&gt;StreamInsight 附带了适配器的各种基本类：特型、泛型、点型、间隔型和边缘型。 特型适配器总是产生带有常见负载类型的事件 - 在示例案例中，为 RandomPayload 类。 泛型适配器适用于可产生多种事件类型的事件源，或不能提前得知行布局和内容的事物，如 CSV 文件。&lt;/p&gt;&lt;p&gt;示例输入适配器具有常见负载类型，可生成点事件，因此其继承自 TypedPointInputAdapter&amp;lt;RandomPayload&amp;gt;。 基本类具有两个必须实现的抽象方法：Start 和 Resume。 在示例中，Start 方法使得计时器在配置指定的间隔内触发。 计时器的 Elapsed 事件运行 ProduceEvent 方法，该方法完成适配器的主要工作。 此方法的主体遵循通用模式。&lt;/p&gt;&lt;p&gt;首先，适配器检查引擎自上次运行后是否已停止而现在仍在运行。 然后，调用基本类中的一种方法来创建点事件的实例，其负载已设置且事件已排列在流中。 在示例中，SetRandomEventPayload 方法可代替任何真实适配器逻辑 - 例如，读取文件、与传感器对话或查询数据库。&lt;/p&gt;&lt;p&gt;输入适配器工厂也非常简单。 它实现了接口 ITypedInputAdapterFactory&amp;lt;RandomPayloadConfig&amp;gt;，因为它是特性适配器的工厂。 本工厂的唯一特点在于它也实现了 ITypedDeclareAdvanceTimeProperties&amp;lt;RandomPayloadConfig&amp;gt; 接口。 此接口允许工厂处理前文所述的 Cti 插入操作。&lt;/p&gt;&lt;p&gt;示例应用程序的输出适配器遵循的模式与输入适配器基本相同。 包括配置类、工厂与输出适配器本身。 适配器类与输入适配器十分相似。 主要区别是适配器从队列中移除事件，而不是对其进行排队。 因为 Cti 事件与其他事件相似，它们也到达输出适配器，并很容易被忽略。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;可观察量&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;虽然适配器模型十分简单，但还可以使用以下一种更简单的方式来将事件输入和输出引擎。 如果应用程序使用的是 StreamInsight 的内嵌部署模型，则您可以使用 IEnumerable 和 IObservable 作为引擎的输入和输出。 给定一个 IEnumerable 或 IObservable，您可以通过调用所提供的扩展方法（如 ToStream、ToPointStream、ToIntervalStream 或 ToEdgeStream）之一创建输入流。 这将创建一个看上去与输入适配器创建的事件流极为相似的事件流。&lt;/p&gt;&lt;p&gt;同样，给定一个查询，扩展方法（如 ToObservable/Enumerable、ToPointObservable/Enumerable、ToIntervalObservable/Enumerable 或 ToEdgeObservableEnumerable）会分别将查询输出路由至 IObservable 或 IEnumerable。 这些模式特别适用于重播保存在数据库中的历史数据。&lt;/p&gt;&lt;p&gt;使用 Entity Framework 或 LINQ to SQL 创建数据库查询。 使用 ToStream 扩展方法将数据库结果转换为事件流，并定义关于该事件流的 StreamInsight 查询。 最后，使用 ToEnumerable 将 StreamInsight 结果路由至方便您 foreach 并打印的位置。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;部署模型和其他工具&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;若要使用 Observable 和 Enumerable 支持，必须在您的应用程序中嵌入 StreamInsight。 但是 StreamInsight 不支持独立模型。 在安装时，系统会询问您是否创建 Windows 服务以托管默认实例。 该服务可随后托管 StreamInsight，允许多个应用程序连接到相同的实例并共享适配器和查询。&lt;/p&gt;&lt;p&gt;通过共享服务器而非嵌入的服务器来进行的通信会使用 Server 类上的一种不同的静态方法。 不调用具有实例名称的 Create，而是调用 Connect，其带有指向共享实例的 EndpointAddress。 此部署策略更适用于企业情况，在此情况下，多个应用程序可能需要使用共享的查询或适配器。&lt;/p&gt;&lt;p&gt;在两种情况下，有时需要弄清楚为什么 StreamInsight 生成的输出不是应该生成的输出。 该产品附带名为 Event Flow Debugger 的工具，以用于此用途。 本文不介绍该工具的使用方法，但总而言之，该工具允许您连接到实例并通过查询跟踪输入和输出事件。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;灵活、反应迅速的工具&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;灵活的部署选项、熟悉的编程模型和可轻松创建的适配器使得 StreamInsight 成为各种情况下的好选择。 从查询并在一秒内关联数以千计的传感器输入的集中式实例到在单个应用程序中监控当前事件和历史事件的嵌入式实例，StreamInsight 均采用开发人员友好的框架（如 LINQ）来实现高度自定义的解决方案。&lt;/p&gt;&lt;p&gt;易于创建的适配器以及用于在事件流与 IEnumerable 和 IObservable 之间进行转换的内置支持使得它能够快速找到解决方案并运行，从而增加封装了特定商业知识的查询的创建和完善工作。 在完善过程中，这些查询提供越来越多的值，使得应用程序和组织能够在发生有趣情况时进行识别并做出反应，而不错过处理的机会。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Rob Pierry&lt;/strong&gt;&amp;nbsp;&lt;em&gt;是 Captura (&lt;a href="http://capturaonline.com/"&gt;capturaonline.com&lt;/a&gt;) 的首席顾问，其中 Captura 是一家咨询公司，提供由可扩展技术支持的创新用户体验。您可以通过&amp;nbsp;&lt;a href="http://gmail.com/"&gt;rpierry+msdn@gmail.com&lt;/a&gt;&amp;nbsp;与他联系。&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;衷心感谢以下技术专家对本文的审阅：&lt;strong&gt;Ramkumar Krishnan、Douglas Laudenschlager&lt;/strong&gt;&amp;nbsp;和&amp;nbsp;&lt;strong&gt;Roman Schindlauer&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&lt;div&gt;&lt;div&gt;&lt;a href="http://www.webweb.com/signup.asp?upline=geffzhang"&gt;&lt;img src="http://www.webweb.com/aff_banner3.gif" alt="" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;作者:&amp;nbsp;&lt;a href="http://shanyou.cnblogs.com/" target="_blank"&gt;自由、创新、研究、探索&amp;hellip;&amp;hellip;&lt;/a&gt;&lt;br /&gt;出处：&lt;a href="http://shanyou.cnblogs.com/" target="_blank"&gt;http://shanyou.cnblogs.com/&lt;/a&gt;&lt;br /&gt;版权：本文版权归作者和博客园共有&lt;br /&gt;转载：欢迎转载，为了保存作者的创作热情，请按要求【转载】，谢谢&lt;br /&gt;要求：未经作者同意，必须保留此段声明；必须在文章中给出原文连接；否则必究法律责任&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;分类:&amp;nbsp;&lt;a href="http://www.cnblogs.com/shanyou/category/262009.html"&gt;复合事件处理(CEP)&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520817.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520817.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520801.html</id><title type="text">HTML Agility Pack 搭配 ScrapySharp，彻底解除Html解析的痛苦</title><summary type="text">自从 Web 应用程序自 1993 年 W3C 设立以来就开始发展，而且 HTML 也历经了数个版本的演化（1.0 – 2.0 – 3.0 – 3.2 – 4.0 – 4.01），现在也已经成为Web网页或应用程序的最基础，想要学习如何设计 Web 网页或开发 Web 应用程序，这已经是绝对必须要学的东西了，就算是方便的控件（例如 ASP.NET），但 HTML 仍然有学习它的必要性，因此如果不会 HTML，就等于没学过 Web 网页一般。拜 HTML 与 Web 浏览器蓬勃发展之赐，各式各样的应用都在网络上迅速发展，举凡电子商务、企业门户、在线下单、企业间协同应用等，乃至于社交、个性化、We</summary><published>2012-05-28T01:22:00Z</published><updated>2012-05-28T01:22:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520801.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520801.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;自从 Web 应用程序自 1993 年 W3C 设立以来就开始发展，而且 HTML 也历经了数个版本的演化（1.0 &amp;ndash; 2.0 &amp;ndash; 3.0 &amp;ndash; 3.2 &amp;ndash; 4.0 &amp;ndash; 4.01），现在也已经成为Web网页或应用程序的最基础，想要学习如何设计 Web 网页或开发 Web 应用程序，这已经是绝对必须要学的东西了，就算是方便的控件（例如 ASP.NET），但 HTML 仍然有学习它的必要性，因此如果不会 HTML，就等于没学过 Web 网页一般。&lt;/p&gt;&lt;p&gt;拜 HTML 与 Web 浏览器蓬勃发展之赐，各式各样的应用都在网络上迅速发展，举凡电子商务、企业门户、在线下单、企业间协同应用等，乃至于社交、个性化、Web 2.0 等商务与组织运用等能力，而在信息爆炸的时代，很多信息整合的应用也随之出炉，而这些信息整合的应用程序都会连接到不同的网站下载其信息，并且在重重的 HTML 中剖析出想要的数据（例如每股价格、涨跌幅、成交量等）。&lt;/p&gt;&lt;p&gt;但是 HTML 本身并不是一个结构严谨的语言，它允许卷标（tag）可以在不 close 的情况下继续使用。这也是因为浏览器设计的高容错性（Fault Tolerance）所致，如此一来，想要依照规则来剖析 HTML 文件几乎变得不可能，而且对方的网站的 HTML 结构也可能会随时变化，在这种情况下，剖析 HTML 变得非常辛苦，虽然 W3C 有另外推展 XHTML（遵守 XML 严谨格式的 HTML），但使用它来设计网页的案例仍为少数，大多数的网站仍然是使用 HTML。因此我们会需要一个工具，能够有方法快速的解析 HTML 以取出我们需要的数据。&lt;/p&gt;&lt;p&gt;大家都知道，HTML 本身其实只是一个 HTML 标记的字符串而已，因此一般说到要解析 HTML，第一个会想到的大概就是字符串比对（string comparison），自己针对 HTML 的结构写一个 pattern，然后由函式去做逐一的比对，例如：&lt;/p&gt;&lt;p&gt;[C#]&lt;/p&gt;&lt;p&gt;1.&amp;nbsp;&lt;strong&gt;string&lt;/strong&gt;&amp;nbsp;pattern = "&amp;lt;td id='stockPrice'&amp;gt;";&lt;/p&gt;&lt;p&gt;2. html.IndexOf(pattern);&lt;/p&gt;&lt;p&gt;不过传统的字符串比对效能太差，也没有一个规则性，因而才发展出正则表达式（Regular Expression）技术，例如下列这样的语法：&lt;/p&gt;&lt;p&gt;[Regular Expression]&lt;/p&gt;&lt;p&gt;1. &amp;lt;/?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'"&amp;gt;\s]+))?)+\s*|\s*)/?&amp;gt;&lt;/p&gt;&lt;p&gt;但 Regular Expression 的学习曲线很高，若要使用它来解析 HTML，并且再加以定制化（Customization）的话，对于一般开发人员来说，实在没有什么亲和力。&lt;/p&gt;&lt;p&gt;HTML 还有一个特色，就是它是具层性（Hierarchy）的，因此浏览器在解译它的时候都会以文件树（document tree）的方式，再用递归（recursive）的方法来处理它，但 Regular Expression 没有支持层级性的剖析，而最接近阶层剖析又好用的工具，莫过于 XML Parser 了，它的 DOM 以及 XPath 的特性，都可以让解析 XML 的工作变得轻松，然而 XML Parser 无法读取一般的 HTML（XHTML 可以），因为一般的 HTML 是结构松散的类型，XML Parser 会在读入时检查语法结构是否完整（也就是 Well-known 的结构），若读入的是结构松散的内容的话会掷出例外讯息，因此无法直接使用 XML Parser 来辅助。&lt;/p&gt;&lt;p&gt;&lt;a href="http://htmlagilitypack.codeplex.com/" target="_blank"&gt;HTML Agility Pack&lt;/a&gt;&amp;nbsp;是由法国的一位软件架构师 Simon Mourier 所开发，并且由 DarthObiwan 以及 Jessynoo 辅助开发出来的一个软件工具，它可以让剖析松散格式 HTML 的工作就像剖析 XML 一样简单，它也有类似于 System.Xml 命名空间中的 XML DOM 的许多类别，除了可以使用阶层的方式存取 HTML 以外，它也支持使用 XPath 的方式来搜寻 HTML，这会较以往使用文字比对或是 Regular Expression 的比对方式来得更明确。&lt;/p&gt;&lt;p&gt;若要使用 HTML Agility Pack 组件，可先上&amp;nbsp;&lt;a href="http://htmlagilitypack.codeplex.com/" target="_blank"&gt;Codeplex&lt;/a&gt;&amp;nbsp;的 HTML Agility Pack 网站下载二进制文件（同时也提供源代码、说明文件以及 HAP Explorer 工具程序可下载），并解压缩后，在项目加入对 HtmlAgilityPack.dll 的引用。&lt;/p&gt;&lt;p&gt;Html Agility Pack 源码中的类大概有28个左右，其实不算一个很复杂的类库，但它的功能确不弱，为解析DOM已经提供了足够强大的功能支持，可以跟jQuery操作DOM媲美：）Html Agility Pack最常用的基础类其实不多，对解析DOM来说，就只有HtmlDocument和HtmlNode这两个常用的类，还有一个 HtmlNodeCollection集合类。&lt;/p&gt;&lt;p&gt;HTML Agility Pack的操作起来还是很麻烦，下面我们要介绍的这个组件是ScrapySharp，他在2个方面针对Html Agility Pack进行了包装，使得解析Html页面不再痛苦，幸福指数直线上升到90分哈。&lt;/p&gt;&lt;p&gt;ScapySharp有了一个真实的浏览器包装类（处理Reference，Cookie等），另外一个就是使用类似于jQuery一样的Css选择器和Linq语法。让我们使用起来非常的爽。它的代码放在&amp;nbsp;&lt;a href="https://bitbucket.org/rflechner/scrapysharp"&gt;https://bitbucket.org/rflechner/scrapysharp&lt;/a&gt;。也可以通过Nuget添加&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/shanyou/201205/201205272240456578.png"&gt;&lt;img title="image" src="http://images.cnblogs.com/cnblogs_com/shanyou/201205/20120527224050477.png" alt="image" width="734" height="409" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;下面我们来看一段解析博客园的博客文章的代码：&lt;/p&gt;&lt;p&gt;using System;&amp;nbsp;&lt;br /&gt;using System.Collections.Generic;&amp;nbsp;&lt;br /&gt;using System.Linq;&amp;nbsp;&lt;br /&gt;using System.Text;&amp;nbsp;&lt;br /&gt;using HtmlAgilityPack;&amp;nbsp;&lt;br /&gt;using ScrapySharp.Extensions;&amp;nbsp;&lt;br /&gt;using ScrapySharp.Network;&lt;/p&gt;&lt;p&gt;namespace HTMLAgilityDemo&amp;nbsp;&lt;br /&gt;{&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; class Program&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; static void Main(string[] args)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var uri = new Uri("&lt;a href="http://www.cnblogs.com/shanyou/archive/2012/05/20/2509435.html%22);"&gt;http://www.cnblogs.com/shanyou/archive/2012/05/20/2509435.html");&lt;/a&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var browser1 = new ScrapingBrowser();&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var html1 = browser1.DownloadString(uri);&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var htmlDocument = new HtmlDocument();&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; htmlDocument.LoadHtml(html1);&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var html = htmlDocument.DocumentNode;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var title = html.CssSelect("title");&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (var htmlNode in title)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(htmlNode.InnerHtml);&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var divs = html.CssSelect("div.postBody");&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (var htmlNode in divs)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(htmlNode.InnerHtml);&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; divs = html.CssSelect("#cnblogs_post_body");&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach (var htmlNode in divs)&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Console.WriteLine(htmlNode.InnerHtml);&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;参考文章：&lt;/p&gt;&lt;p&gt;&lt;a href="http://msdn.microsoft.com/zh-tw/ee787055" target="_blank"&gt;HTML Agility Pack：簡單好用的快速 HTML Parser&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/ttlive/archive/2011/09/10/2173210.html"&gt;开源项目Html Agility Pack实现快速解析Html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/ITmuse/archive/2010/05/27/1745040.html"&gt;c#中的jQuery&amp;mdash;&amp;mdash;HtmlAgilityPack&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/ITmuse/archive/2010/05/29/1747199.html"&gt;Html Agility Pack基础类介绍及运用&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&lt;div&gt;&lt;div&gt;&lt;a href="http://www.webweb.com/signup.asp?upline=geffzhang"&gt;&lt;img src="http://www.webweb.com/aff_banner3.gif" alt="" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;作者:&amp;nbsp;&lt;a href="http://shanyou.cnblogs.com/" target="_blank"&gt;自由、创新、研究、探索&amp;hellip;&amp;hellip;&lt;/a&gt;&lt;br /&gt;出处：&lt;a href="http://shanyou.cnblogs.com/" target="_blank"&gt;http://shanyou.cnblogs.com/&lt;/a&gt;&lt;br /&gt;版权：本文版权归作者和博客园共有&lt;br /&gt;转载：欢迎转载，为了保存作者的创作热情，请按要求【转载】，谢谢&lt;br /&gt;要求：未经作者同意，必须保留此段声明；必须在文章中给出原文连接；否则必究法律责任&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;分类:&amp;nbsp;&lt;a href="http://www.cnblogs.com/shanyou/category/77247.html"&gt;工具箱&lt;/a&gt;,&amp;nbsp;&lt;a href="http://www.cnblogs.com/shanyou/category/32025.html"&gt;开源项目&lt;/a&gt;,&amp;nbsp;&lt;a href="http://www.cnblogs.com/shanyou/category/58014.html"&gt;ASP.NET&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520801.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520801.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520797.html</id><title type="text">360 Degrees Viewer – WordPress Plugin</title><summary type="text">在今天的文章中，我们已经聚集了jQuery插件，允许你创建一个360度旋转图片.这些插件，主要用于在网上购物网站显示各个角度详细的外观.这个技术的产品是一点点付出努力，但结果是惊人的。360 Panorama for jQueryDemo&amp;Source360/3D Spin &amp; Zoom JavaScript PlayerDemo&amp;SourceReel 1.1.4Demo&amp;SourceMultiple 360 Images On One PageDemo&amp;Source360 Degrees Product ViewDemo&amp;SourceSprit</summary><published>2012-05-28T01:20:00Z</published><updated>2012-05-28T01:20:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520797.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520797.html"/><content type="html">&lt;p&gt;&amp;nbsp;&amp;nbsp; 在今天的文章中，我们已经聚集了jQuery插件，允许你创建一个360度旋转图片.这些插件，主要用于在网上购物网站显示各个角度详细的外观.这个技术的产品是一点点付出努力，但结果是惊人的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;360 Panorama for jQuery&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25830" title="3.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/3.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="186" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.mathieusavard.info/threesixty/demo.html" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;360/3D Spin &amp;amp; Zoom JavaScript Player&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25831" title="4.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/4.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="393" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.ajax-zoom.com/examples/example15.php" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Reel 1.1.4&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25828" title="1.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/1.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="133" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://jquery.vostrel.cz/reel" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Multiple 360 Images On One Page&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25829" title="2.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/2.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="321" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.360-javascriptviewer.com/learning-centre/code-examples/multiple-360-images-page.html" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;360 Degrees Product View&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25832" title="5.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/5.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="317" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.stableflow.com/jquery-plugins/360-degrees-product-view/" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Spritespin&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25833" title="6.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/6.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="315" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://spritespin.ginie.eu/index.html" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Dopeless Rotate&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25834" title="7.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/7.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="168" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.dopeless-design.de/dopeless-rotate-jquery-plugin-360-degrees-product-viewer.html" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Heartcode 360 Image Slider&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25835" title="8.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/8.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="266" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://heartcode.robertpataki.com/360-image-slider/" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3D Rotation Viewer&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25836" title="11.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/11.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="322" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.uize.com/examples/source-code/3d-rotation-viewer.html" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;360 Degrees Viewer &amp;ndash; WordPress Plugin&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img class="aligncenter size-full wp-image-25837" title="12.jquery 3d product view" src="http://designbeep.designbeep.netdna-cdn.com/wp-content/uploads/2012/05/12.jquery-3d-product-view.jpg" alt="jquery 3d product view" width="500" height="254" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="http://designbeep.com/2012/05/25/10-great-jquery-plugins-for-360-degree-image-rotation/codecanyon.net/item/360-degrees-viewer-wordpress-plugin/242232" target="_blank"&gt;Demo&amp;amp;Source&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;原文:&lt;a href="http://designbeep.com/2012/05/25/10-great-jquery-plugins-for-360-degree-image-rotation/"&gt;http://designbeep.com/2012/05/25/10-great-jquery-plugins-for-360-degree-image-rotation/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520797.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/28/2520797.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2520516.html</id><title type="text">Win32 API从应用程序到驱动程序</title><summary type="text">周末在家看了一天《Windows驱动开发技术详解》，一点点心得在这里记录一下，如果有理解不对的地方，还望海涵，指出。驱动程序I/O管理器接收应用程序的请求后，创建响应的IRP，并传送至驱动程序进行处理，有如下集中处理方法：1.根据IRP的请求，直接操作具体硬件，然后完成此IRP，并返回。2.将此IRP的请求，转发到更底层的驱动中，并等待底层驱动的返回。3.接收到IRP请求后，不急于完成，分配新的IRP发到其他驱动程序中，并等待返回。Windows与微内核：微内核和单一内核的概念是相互对立的。单一内核，一般是讲系统的主要核心组件全部在内核实现（进程管理器，内存管理器，I/O管理器），内核各组件之</summary><published>2012-05-27T13:18:00Z</published><updated>2012-05-27T13:18:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2520516.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2520516.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;周末在家看了一天《Windows驱动开发技术详解》，一点点心得在这里记录一下，如果有理解不对的地方，还望海涵，指出。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;驱动程序&lt;/strong&gt;&lt;br /&gt;I/O管理器接收应用程序的请求后，创建响应的IRP，并传送至驱动程序进行处理，有如下集中处理方法：&lt;br /&gt;1.根据IRP的请求，直接操作具体硬件，然后完成此IRP，并返回。&lt;br /&gt;2.将此IRP的请求，转发到更底层的驱动中，并等待底层驱动的返回。&lt;br /&gt;3.接收到IRP请求后，不急于完成，分配新的IRP发到其他驱动程序中，并等待返回。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Windows与微内核：微内核和单一内核的概念是相互对立的。&lt;/strong&gt;&lt;br /&gt;单一内核，一般是讲系统的主要核心组件全部在内核实现（进程管理器，内存管理器，I/O管理器），内核各组件之间关联很大，耦合性很大，不利于模块化设计，但是速度快，组件的通信全部在内核态完成，没有进程间的切换也没有用户态到内核态的切换。例如Linux操作系统。&lt;/p&gt;&lt;p&gt;微内核，操作系统的主要组件运行在独立的进程中，使用进程间通讯提供服务，耦合度降低的同时，但是效率也有下降。&lt;br /&gt;Windows不是纯粹的微内核系统，内核的各个核心组件相对耦合性较小，克服微内核效率低的特点。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;从应用程序到驱动程序&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/375453/2012052718000995.jpg" alt="" /&gt;&lt;/p&gt;&lt;p&gt;1.WIN32 API CreateFile&lt;/p&gt;&lt;p&gt;2.传递到Kernel32.dll子系统内&lt;/p&gt;&lt;p&gt;3.进而调用Ntdll.dll中的Native API NtCreateFile&lt;/p&gt;&lt;p&gt;4.通过软中断进入内核，调用内核态的NtCreateFile系统服务函数&lt;/p&gt;&lt;p&gt;5.调用I/O管理器，创建IRP并传输到设备驱动程序中&lt;/p&gt;&lt;p&gt;PS：在一层层的调用中，每层都会严格的检查保证参数的合法性。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;安全软件会对一些重点函数进行特殊过滤，不管是内核HOOK还是什么，看雪一篇文章&lt;a href="http://bbs.pediy.com/showthread.php?t=151241" target="_blank"&gt;《瑞星全功能安全软件2011内核拒绝服务漏洞》&lt;/a&gt;，刚好就是对参数的合法性没有处理，导致的BSOD。&lt;/p&gt;&lt;p&gt;大概的过程就是：&lt;strong&gt;瑞星在NtCreateKey HOOK函数中对ObjectAttributes结构体的参数校验不严格，就将参数传递给内核函数ZwOpenKey，结果引发蓝屏。&lt;/strong&gt;在未安装瑞星的系统下没有影响。可见做内核HOOK，对于设计精良的操作系统都会进行层层检查，但是HOOK的检查不足，就有可能导致BSOD。&lt;/p&gt;&lt;p&gt;好了，现在搞内核HOOK不是我的目标，还是认真的打基础。&lt;/p&gt;&lt;p&gt;对于内核部分的操作不是很熟悉，但是可以看看上层WIN32 API的调用。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;随便写个小程序调用OpenProcess函数。&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;#include &amp;lt;windows.h&amp;gt;&lt;br/&gt;&lt;br/&gt;&lt;span&gt;#define&lt;/span&gt; QQPROCESSID 7288&lt;br/&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt; main()&lt;br/&gt;{&lt;br/&gt;    HANDLE hProcess &lt;/span&gt;=&lt;span&gt; OpenProcess(PROCESS_ALL_ACCESS, FALSE, QQPROCESSID);&lt;br/&gt;    TerminateProcess(hProcess, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;br/&gt;    CloseHandle (hProcess); &lt;br/&gt;    &lt;/span&gt;&lt;span&gt;return&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;br/&gt;}&lt;/span&gt;&lt;/div&gt;&lt;p&gt;使用OD调试。大概有如下调用关系。&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/375453/2012052718145241.jpg" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/375453/2012052718241265.gif" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/375453/2012052718242048.gif" alt="" /&gt;&lt;/p&gt;&lt;p&gt;用户态：EXE--&amp;gt;kernel32.OpenProcess--&amp;gt;ntdll.ZwOpenProcess--&amp;gt;KiFastSystemCall--&amp;gt;sysenter&lt;/p&gt;&lt;p&gt;这里可以看出来，从Ring3 API--&amp;gt;Native API--&amp;gt;进入内核--&amp;gt;SSDT&lt;/p&gt;&lt;p&gt;进入Ring0之后，系统会根据服务号（eax = 0xBE）在SSDT这个系统服务描述符表中查找对应的表项，这个找到的表项就是系统服务函数NtOpenProcess在内核中的真正地址。之后，系统会根据这个地址调用相应的系统服务函数，并把结果返回给ntdll.dll中的NtOpenProcess。&amp;nbsp;&lt;/p&gt;&lt;p&gt;PS：感觉内核这个东西越看越有意思，希望能一点点掌握吧，给自己加油。&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="EntryTag"&gt;标签:&amp;nbsp;&lt;a href="http://www.cnblogs.com/revfish/tag/%E5%86%85%E6%A0%B8/"&gt;内核&lt;/a&gt;,&amp;nbsp;&lt;a href="http://www.cnblogs.com/revfish/tag/%E9%A9%B1%E5%8A%A8/"&gt;驱动&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2520516.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2520516.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519820.html</id><title type="text">python(贪吃蛇)</title><summary type="text">main 函数的对比Pythonc#if __name__ == '__main__': """ sssssss """ #ssssssssss print("hello world") /// &lt;summary&gt; /// main 函数的注释 /// &lt;/summary&gt; /// &lt;param name="args"&gt;&lt;/param&gt; static void Main(string[] args) { // 注释 输出hello </summary><published>2012-05-27T01:45:00Z</published><updated>2012-05-27T01:45:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519820.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519820.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;&lt;strong&gt;main 函数的对比&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;table border="0" cellspacing="0" cellpadding="2"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="518"&gt;Python&lt;/td&gt;&lt;td valign="top" width="456"&gt;c#&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="518"&gt;&lt;div class="csharpcode"&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; __name__ == &lt;span class="str"&gt;'__main__'&lt;/span&gt;:    &lt;span class="str"&gt;""&lt;/span&gt;&lt;span class="str"&gt;"&lt;/span&gt;    sssssss    "&lt;span class="str"&gt;""&lt;/span&gt;    #ssssssssss    print(&lt;span class="str"&gt;"hello world"&lt;/span&gt;)&lt;/div&gt;&lt;/td&gt;&lt;td valign="top" width="456"&gt;&lt;div class="csharpcode"&gt;        &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;        &lt;span class="rem"&gt;/// main 函数的注释&lt;/span&gt;        &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;        &lt;span class="rem"&gt;/// &amp;lt;param name="args"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;        &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Main(&lt;span class="kwrd"&gt;string&lt;/span&gt;[] args)        {            &lt;span class="rem"&gt;//   注释  输出hello world&lt;/span&gt;            Console.Write(&lt;span class="str"&gt;"hello world"&lt;/span&gt;);        }&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="518"&gt;print("hello world")&amp;nbsp;&lt;br /&gt;输出 hello world&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;#ssssssssss 为单行注释&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;"""&amp;nbsp;&lt;br /&gt;&amp;nbsp; sssssss&amp;nbsp;&lt;br /&gt;&amp;nbsp; """&lt;/p&gt;为函数的说明文档，可以在智能提示里面看到&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;&lt;td valign="top" width="456"&gt;Console.Write("hello world");&amp;nbsp;&lt;br /&gt;输出 hello world&amp;nbsp;&lt;br /&gt;&lt;br /&gt;//&amp;nbsp;&amp;nbsp; 注释&amp;nbsp; 输出hello world&amp;nbsp;&amp;nbsp; 为单行注释&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;/// &amp;lt;summary&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// main 函数的注释&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;/summary&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /// &amp;lt;param name="args"&amp;gt;&amp;lt;/param&amp;gt;&lt;/p&gt;&lt;p&gt;为函数的说明文档，可以在智能提示里面看到&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;了解Python的原因&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 搞.net的多数是以实用主义为原则的，时间长了不敢说是将自己局限于.NET的框框内，但要在.NET的平台内进一步深入，已经灰常困难，尤其是那些非科班出身的，例如北大青鸟、达内等，或者向我这样连培训机构也没进去过的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 时间长了脑子里除了.NET相关的啥也没有，要在.NET平台继续深入应该比较困难&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 为什么不了解JAVA呢？ java和c#都是面向企业级的业务层的，都了解的c#为什么还要重复投资呢？&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 为什么不了解php呢？看过PHP的都知道什么叫英雄迟暮，到处是$，不了解也罢&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 为什么不了解per呢？你喜欢正则表达式吗？不喜欢的话就不要看了&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 为什么不了解ruby？ 那可是日本的东西，如果你的产品说明是基于强大的日本的ruby架构，我预计公司就要破产了，原因大家懂得&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 为什么要了解Python呢，尤其是搞.net,原因如下&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1：Python 语法和c#相比较简单&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2：python 入门文档齐全&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 国内的：&lt;a title="http://sebug.net/paper/python/ch08s07.html" href="http://sebug.net/paper/python/ch08s07.html"&gt;http://sebug.net/paper/python/ch08s07.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 官方入门的：&lt;a title="http://docs.python.org/tutorial/index.html" href="http://docs.python.org/tutorial/index.html"&gt;http://docs.python.org/tutorial/index.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 官方的：&lt;a title="http://sebug.net/paper/python/ch08s07.html" href="http://sebug.net/paper/python/ch08s07.html"&gt;http://sebug.net/paper/python/ch08s07.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3类似ASP.NET或ASP.NET MVC的框架django&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 国内的文档：&lt;a title="http://djangobook.py3k.cn/2.0/" href="http://djangobook.py3k.cn/2.0/"&gt;http://djangobook.py3k.cn/2.0/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 国外的文档：&lt;a title="https://docs.djangoproject.com/" href="https://docs.djangoproject.com/"&gt;https://docs.djangoproject.com/&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;a title="https://docs.djangoproject.com/en/1.4/intro/tutorial01/" href="https://docs.djangoproject.com/en/1.4/intro/tutorial01/"&gt;https://docs.djangoproject.com/en/1.4/intro/tutorial01/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4 Python 可以灰常方便的调用c和c++ 有主意 了解c和c++&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5 一个半死不活的（但包含很多文档的）社区：&lt;a title="http://wiki.woodpecker.org.cn/moin/" href="http://wiki.woodpecker.org.cn/moin/"&gt;http://wiki.woodpecker.org.cn/moin/&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;test&lt;/div&gt;&lt;div&gt;&lt;div id="ckepop"&gt;&lt;table cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;img src="http://v1.jiathis.com/code/images/r.gif" alt="" /&gt;&lt;/td&gt;&lt;td&gt;&lt;div&gt;&lt;div class="jiadiv_01"&gt;&lt;div&gt;&lt;table style="width: 100%;"&gt;&lt;tbody&gt;&lt;tr class="jt_sharetitle"&gt;&lt;td align="left"&gt;分享到...&lt;/td&gt;&lt;td align="right"&gt;&lt;img src="http://v1.jiathis.com/code/images/img_exit.gif" alt="" border="0" /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div id="jiathis_sers" class="jiadiv_02"&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_email"&gt;邮件&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_tsina"&gt;新浪微博&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_copy"&gt;复制网址&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_qzone"&gt;QQ空间&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_msn"&gt;MSN&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_tqq"&gt;腾讯微博&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_renren"&gt;人人网&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_kaixin001"&gt;开心网&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_t163"&gt;网易微博&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_tsohu"&gt;搜狐微博&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_xiaoyou"&gt;朋友网&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_taobao"&gt;淘江湖&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_baidu"&gt;百度搜藏&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_douban"&gt;豆瓣&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_ishare"&gt;一键分享&lt;/span&gt;&lt;/a&gt;&lt;a class="jiatitle" href="http://www.cnblogs.com/qqloving/category/382806.html"&gt;&lt;span class="jtico jtico_jiathis"&gt;查看更多(124)&lt;/span&gt;&lt;/a&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div class="ckepopBottom"&gt;&lt;div&gt;&lt;a class="link_01" href="http://www.jiathis.com/help/html/what-is-jiathis" target="_blank"&gt;这是什么工具?&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://v1.jiathis.com/code/images/img_012.gif" alt="" align="absmiddle" border="0" /&gt;&lt;a class="link_01" href="http://www.jiathis.com/" target="_blank"&gt;JiaThis&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;a name="top"&gt;&lt;/a&gt;&lt;div id="home"&gt;&lt;div id="header"&gt;&lt;div id="blogTitle"&gt;&lt;a id="lnkBlogLogo" href="http://www.cnblogs.com/qqloving/"&gt;&lt;img id="blogLogo" src="http://www.cnblogs.com/Skins/custom/images/logo.gif" alt="返回主页" /&gt;&lt;/a&gt;&lt;p&gt;&lt;strong&gt;&lt;a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/qqloving/"&gt;互联网fans&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;干学习！干曝光！&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;&lt;div id="navigator"&gt;&lt;ul id="navList"&gt;&lt;li&gt;&lt;a id="MyLinks1_HomeLink" class="menu" href="http://www.cnblogs.com/"&gt;博客园&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a id="MyLinks1_MyHomeLink" class="menu" href="http://www.cnblogs.com/qqloving/"&gt;首页&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="menu" href="http://q.cnblogs.com/"&gt;博问&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a class="menu" href="http://home.cnblogs.com/ing/"&gt;闪存&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a id="MyLinks1_NewPostLink" class="menu" href="http://www.cnblogs.com/qqloving/admin/EditPosts.aspx?opt=1" rel="nofollow"&gt;新随笔&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a id="MyLinks1_ContactLink" class="menu" href="http://space.cnblogs.com/msg/send/(%e5%bc%a0%e8%b6%85)" rel="nofollow"&gt;联系&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a id="MyLinks1_Syndication" class="menu" href="http://www.cnblogs.com/qqloving/rss"&gt;订阅&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a id="MyLinks1_Admin" class="menu" href="http://www.cnblogs.com/qqloving/admin/EditPosts.aspx" rel="nofollow"&gt;管理&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogStats"&gt;随笔- 68&amp;nbsp; 文章- 0&amp;nbsp; 评论- 195&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div id="main"&gt;&lt;div id="mainContent"&gt;&lt;div class="forFlow"&gt;&lt;div class="entrylist"&gt;&lt;h1 class="entrylistTitle"&gt;python(贪吃蛇)&lt;/strong&gt;&lt;/p&gt;&lt;div class="entrylistDescription"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="entrylistItem"&gt;&lt;div class="entrylistPosttitle"&gt;&lt;a id="CategoryEntryList1_EntryStoryList_Entries_TitleUrl_0" class="entrylistItemTitle" href="http://www.cnblogs.com/qqloving/archive/2012/05/27/2519786.html"&gt;在.NET外散步之我爱贪吃蛇Python -常用数和字符串和物理行逻辑行&lt;/a&gt;&lt;/div&gt;&lt;div class="entrylistPostSummary"&gt;&lt;div class="c_b_p_desc"&gt;摘要: 基本的数字和字符串 Python c# 没有内置的常量 需要自己实现 复数 mycomplex=3+4j print(mycomplex.real)#实部 ...&lt;a class="c_b_p_desc_readmore" href="http://www.cnblogs.com/qqloving/archive/2012/05/27/2519786.html"&gt;阅读全文&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="entrylistItemPostDesc"&gt;posted @&amp;nbsp;&lt;a title="permalink" href="http://www.cnblogs.com/qqloving/archive/2012/05/27/2519786.html"&gt;2012-05-27 08:53&lt;/a&gt;&amp;nbsp;(张超) 阅读(67) |&amp;nbsp;&lt;a title="comments, pingbacks, trackbacks" href="http://www.cnblogs.com/qqloving/archive/2012/05/27/2519786.html#FeedBack"&gt;评论 (0)&lt;/a&gt;&amp;nbsp;&lt;a href="http://www.cnblogs.com/qqloving/admin/EditPosts.aspx?postid=2519786" rel="nofollow"&gt;编辑&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="postSeparator"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="entrylistItem"&gt;&lt;div class="entrylistPosttitle"&gt;&lt;a id="CategoryEntryList1_EntryStoryList_Entries_TitleUrl_1" class="entrylistItemTitle" href="http://www.cnblogs.com/qqloving/archive/2012/05/23/2515335.html"&gt;在.NET外散步之我爱贪吃蛇Python -hello world&lt;/a&gt;&lt;/div&gt;&lt;div class="entrylistPostSummary"&gt;&lt;div class="c_b_p_desc"&gt;摘要: main 函数的对比Pythonc#if __name__ == '__main__': """ sssssss """ #ssssssssss print("hello world") /// &amp;lt;summary&amp;gt; /// main 函数的注释 /// &amp;lt;/summary&amp;gt; /// &amp;lt;param name="args"&amp;gt;&amp;lt;/param&amp;gt; static void Main(string[] args) { // 注释 输出hello&amp;nbsp;&lt;a class="c_b_p_desc_readmore" href="http://www.cnblogs.com/qqloving/archive/2012/05/23/2515335.html"&gt;阅读全文&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="entrylistItemPostDesc"&gt;posted @&amp;nbsp;&lt;a title="permalink" href="http://www.cnblogs.com/qqloving/archive/2012/05/23/2515335.html"&gt;2012-05-23 21:07&lt;/a&gt;&amp;nbsp;(张超) 阅读(901) |&amp;nbsp;&lt;a title="comments, pingbacks, trackbacks" href="http://www.cnblogs.com/qqloving/archive/2012/05/23/2515335.html#FeedBack"&gt;评论 (3)&lt;/a&gt;&amp;nbsp;&lt;a href="http://www.cnblogs.com/qqloving/admin/EditPosts.aspx?postid=2515335" rel="nofollow"&gt;编&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2519820.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519820.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519815.html</id><title type="text">ASP.NET一个小技巧</title><summary type="text">在做bs项目时，网站的web层（指web项目） 有两种创建方式1 是创建一个项目2 是创建一个站点 当创建一个项目时，站点下的所有.cs文件会生成一个dll，对软件类的bs项目部署非常方便，但是对那些互联网公司来说就是噩梦，有时站点大道一定程度，dll文件上了1M后，不仅批量上传麻烦，而且发布一次补丁也很麻烦，所以很多互联网公司喜欢PHP等，可以随便改的，修改方便 当创建一个站点时，默认同一个文件名称（不管在那个文件夹下，也不管类名）会生成一个dll，文件多了会生成很多dll，对软件的部署是异常噩梦，但默认的配置也不能达到互联网公司时时刻刻发补丁的要求，尤其是表示层的修改，因为是一个文件名称生</summary><published>2012-05-27T01:42:00Z</published><updated>2012-05-27T01:42:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519815.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519815.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;在做bs项目时，网站的web层（指web项目） 有两种创建方式&lt;/p&gt;&lt;p&gt;1 是创建一个项目&lt;/p&gt;&lt;p&gt;2 是创建一个站点&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当创建一个项目时，站点下的所有.cs文件会生成一个dll，对软件类的bs项目部署非常方便，但是对那些互联网公司来说就是噩梦，有时站点大道一定程度，dll文件上了1M后，不仅批量上传麻烦，而且发布一次补丁也很麻烦，所以很多互联网公司喜欢PHP等，可以随便改的，修改方便&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 当创建一个站点时，默认同一个文件名称（不管在那个文件夹下，也不管类名）会生成一个dll，文件多了会生成很多dll，对软件的部署是异常噩梦，但默认的配置也不能达到互联网公司时时刻刻发补丁的要求，尤其是表示层的修改，因为是一个文件名称生成一个dll，所以按照一些规则命名文件还是可以满足要求的&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;规则1 文件名 为：文件夹的名称+文件的名称&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/qqloving/201205/201205262141273154.png"&gt;&lt;img title="image" src="http://images.cnblogs.com/cnblogs_com/qqloving/201205/201205262141284974.png" alt="image" width="263" height="316" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;规则2 发布站点时需要勾选&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/qqloving/201205/201205262141361117.png"&gt;&lt;img title="image" src="http://images.cnblogs.com/cnblogs_com/qqloving/201205/201205262142212425.png" alt="image" width="678" height="456" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;这样发布后的效果如下&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/qqloving/201205/201205262142325447.png"&gt;&lt;img title="image" src="http://images.cnblogs.com/cnblogs_com/qqloving/201205/201205262142349087.png" alt="image" width="573" height="446" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;a href="http://pic002.cnblogs.com/images/2012/363722/2012052623585176.gif" target="_blank"&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/363722/2012052623585176.gif" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;还可以把第一个选项取消掉,就是预编译网页了,网页性能提速好多.....&lt;/p&gt;&lt;p&gt;这样基本可以实现一个页面的cs文件生成一个dll，虽然多了点但是维护起来可能方便些&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;test&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;分类:&amp;nbsp;&lt;a href="http://www.cnblogs.com/qqloving/category/297224.html"&gt;JQUERY插件【ASP.NETmvc必备】&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2519815.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519815.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519813.html</id><title type="text">ERP产品框架</title><summary type="text">对于做java web开的多数程序员会选择SSH、Spring Mvc 、SSI之类的框架！这些框架的优点我想大家都很清楚，在这里我就不多说了！当你遇到下面的需求的，而且要达到快速开发的时候，你会发现用这些框架组合好像不太现实！而且会成为一种累赘. 本人以前做是做C++、.net 的开发，2011年8月 转入java项目组，负责一个产品开发，主要任务是负责这个产品的程序框架！目前这个框架基本上以成型，在这里我主要想和大家分享一下java开发、框架设计、以及产品设计之路的经验，不足之处希望大侠指点一下，在用做这个框架之前，我对java主要了解还只有J2SE、jsp，servlet，JDBC！ 2</summary><published>2012-05-27T01:39:00Z</published><updated>2012-05-27T01:39:00Z</updated><author><name>龙王</name><uri>http://www.cnblogs.com/Leo_wl/</uri></author><link rel="alternate" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519813.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519813.html"/><content type="html">&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;对于做java web开的多数程序员会选择SSH、Spring Mvc 、SSI之类的框架！这些框架的优点我想大家都很清楚，在这里我就不多说了！&lt;/p&gt;&lt;p&gt;当你遇到下面的需求的，而且要达到快速开发的时候，你会发现用这些框架组合好像不太现实！而且会成为一种累赘.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 本人以前做是做C++、.net 的开发，2011年8月 转入java项目组，负责一个产品开发，主要任务是负责这个产品的程序框架！&lt;br /&gt;目前这个框架基本上以成型，在这里我主要想和大家分享一下java开发、框架设计、以及产品设计之路的经验，不足之处希望大侠指点一下，&lt;br /&gt;在用做这个框架之前，我对java主要了解还只有J2SE、jsp，servlet，JDBC！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2011年8月公司决定把以前引以为豪用delphi开发一个产品做成web形式，&lt;br /&gt;&lt;span&gt;（据说是老板在一次项目投标中被打击了，一个客户对他说了一句这样的话：&amp;ldquo;这个年代ERP还有用C/S的，还好意思来投标，太out了吧&amp;rdquo;）&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 于是公司开始筹划这个web形式的产品开发，开始打算用.net 开发，没过几天产品经理的在一次同学聚会中听他同学说.net 成本高，不能跨平台！&lt;br /&gt;对于我这种做了多年.net开发人来说还是希望公司用.net的，不过mono跨平台成功的商业项目还不多，所以我没有足够的理由说服产品经理，&lt;br /&gt;于是老板决定用java开发，开始找java项目负责人，应该薪水的原因，公司没有招聘到合适的项目负责人，于是我就成了这个产品开发的技术负责人，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 应该是第一次做java项目组负责人时鸭梨山大，于是就开始研究java的各种框架，Strut，jsf，hibernate，iBATIS，Spring&amp;nbsp; 了解流行框架所拥有的优缺点，以及所能做的功能，&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; 两个星期之后需求和设计总结下来了，因为这个产品以前是c/s delphi开发的，现在改成java web方式&lt;br /&gt;所以需求总结进行的比较快，产品的需求通过产品经理审核就开始动工了！&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; 因为产品要求灵活，而且业务比较复杂的！这个产品涉及比较广。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;数据库有一千多个表（实体），两千多个视图，两百多个表量函数和普通函数，以及一千多个存储过程&lt;/p&gt;&lt;p&gt;涉及：原材（进销存管理）、设备、生产调度，产品检测、人事（CRM）、财务，预算、等管理！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 产品要求达到的基本功能&lt;br /&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第一、产品要满足行业内大小公司、集团客户的的需求&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第二、产品要为非程序员提供第二次开发接口，（用户在数据库添加几个表，可以自定义生产维护页面、和查询业务）&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第三、每个用户都有个性化数据，每个用户看到的页面、字段不一样，可以主题风格！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第四、每个用户有不同的功能模块权限，以及不同的数据查询条件，可以用管理员定义查询过滤，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第五、页面布局自定义改动，（用户可以改变控件的位置，可以添加或者删除）&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第六，下来框的数据，用户可以动态的改动、动态级联关系用户可以自定义设置&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第七、用户对每个字段可以指定验证规则（比如有写用户可以录入重复的数据，二其他的用户不能录重复的数据，指定有些用户必填有些用户可以为空）&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第八、数据添加、修改、删除、日志记录, 系统异常记录&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 权限设计&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 权限分角色、角色组，部门权限，个人权限，功能模块权限，菜单权限，查询条件权限，操作权限、字段（增删改查权限）&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;&amp;nbsp;流程设计&lt;br /&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 生产流程、工作流程、审批流程&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;strong&gt;报表&lt;br /&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 要求、可以在线编辑打印报表，图形化报表、导出excel，pdf，在线打印速度必须快，一秒钟要可以打印一张！&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;strong&gt;&amp;nbsp; 框架要求统一规范，&lt;br /&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 代码统一规范、命名统一规范！格式统一规范，&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 类设计必须到底可以扩展，可以维护，要实现多数据库、业务逻辑层的代码可以替换方便以后维护和升级、跨平台，多国语言，&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在设计上尽量可以达到兼容多种开发语言模式！（比如方便以后用 .net 重写）&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 公司要求，我两个月的时候把框架设计出来！公司再招聘七八个一年多工作经验的java程序员，然后一年之后上线！&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp; 这样的需求对于没有java开发的我欲哭无泪 ，于是找做java开发同学、朋友要他们开发的项目源码。我想最后的结果大家应该知道的！&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 下一篇讲框架的基本设计！&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 设计之路、如何应对灵活变动的需求，以及能够快速开发&lt;br /&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;分类:&amp;nbsp;&lt;a href="http://www.cnblogs.com/and/category/244274.html"&gt;java&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;h1 class="postTitle"&gt;&lt;a id="cb_post_title_url" class="postTitle2" href="http://www.cnblogs.com/lengyuhong/archive/2012/05/26/2507498.html"&gt;分布式系统缓存设计浅析&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;div class="clear"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="postBody"&gt;&lt;div id="cnblogs_post_body"&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 前几天听了部门内&lt;a href="http://weibo.com/ialeafs"&gt;朋春&lt;/a&gt;大牛讲分布式缓存的一个技术分享，还是非常有收获。&lt;/p&gt;&lt;p&gt;PPT如下：&lt;/p&gt;&lt;p&gt;&lt;iframe src="http://www.slideshare.net/slideshow/embed_code/12977939" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" width="500" height="417"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 这个分享的副标题是&amp;ldquo;简单的事情从来不简单&amp;rdquo;，这句话讲得非常在理。缓存看似简单，但要做&amp;ldquo;好&amp;rdquo;一个缓存系统也是很有讲究的。 &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 写点自己的心得收获吧：&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;span&gt;&lt;strong&gt;1. 分布式缓存面临比较大的三个问题：&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;strong&gt;（1） 数据一致性。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;在分布式系统这点显得尤为重要，主要原因有三点：&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;缓存系统与底层数据的一致性。这点在底层系统是&amp;ldquo;可读可写&amp;rdquo;时，写得尤为重要&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;有继承关系的缓存之间的一致性。为了尽量提高缓存命中率，缓存也是分层：全局缓存，二级缓存。他们是存在继承关系的。全局缓存可以有二级缓存来组成。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;多个缓存副本之间的一致性。为了保证系统的高可用性，缓存系统背后往往会接两套存储系统（如memcache，redis等），以上的ppt也主要是讲这方面的内容。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;strong&gt;（2）缓存雪崩&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;当缓存系统重启或者所有缓存在同一时刻失效（比如某些系统为了提高速度，会在系统启动是统一将大部分数据刷到缓存中，此时如果设置缓存时间都是24小时，那24小时过后，那就悲剧）时，应用系统由于扛不住压力而直接挂掉。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;strong&gt;（3）缓存穿透&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;查询一个必然不存在的数据，查询一个必然不存在的key，每次都会访问DB，如果有人恶意破坏，那么很可能直接对DB造成影响。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 第一点偏重数据的真实性和实时性，而第二点和第三点更多从性能上考虑。同时缓存并不一定是必需的，特别是当写操作特别频繁时。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span&gt;&lt;strong&gt;2. 缓存数据的淘汰&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;原先缓存数据的淘汰往往是用设置缓存时间，比如我设置某个数据的缓存时间是24小时，之后的24内这个缓存是不会失效的。优点当然是简单，缺点也很明显就是不都灵活，没做到好的精细化管理。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 我们能利用的资源就是：1. 给缓存加tag，2. 版本号（必须单调递增，时间戳是最好的选择）3. 提供手动清理缓存的接口。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 相关步骤可以参见以上的PPT内容。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 缓存相关接口：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;var&lt;/span&gt; me =&lt;span&gt; Cache.create(...);&lt;br/&gt;me.set(key, value, ttl, tags);&lt;br/&gt;me.get(key);&lt;br/&gt;me.tagrm(tag, offset, flush);&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 缓存的数据结构如下：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;&lt;span&gt;var&lt;/span&gt; data =&lt;span&gt; {&lt;br/&gt;  &amp;lsquo;i&amp;rsquo;:now, &lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt;* 数据写入时间戳 &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;span&gt;&lt;br/&gt;  &amp;lsquo;e&amp;rsquo;:now &lt;/span&gt;+ ttl,&lt;span&gt;/*&lt;/span&gt;&lt;span&gt;* 预期过期时间 &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;span&gt;&lt;br/&gt;  &amp;lsquo;k&amp;rsquo;:key, &lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt;* 原始key &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;span&gt;&lt;br/&gt;  &amp;lsquo;v&amp;rsquo;:value, &lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt;* 原始值 &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;span&gt;&lt;br/&gt;  &amp;lsquo;t&amp;rsquo;:tags &lt;/span&gt;&lt;span&gt;/*&lt;/span&gt;&lt;span&gt;* tag列表 &lt;/span&gt;&lt;span&gt;*/&lt;/span&gt;&lt;span&gt;&lt;br/&gt;};&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 我刚开始的也没弄明白为什么在data中还要存&amp;ldquo;原始的key&amp;rdquo;，后来经朋春提醒，才弄明白：原始的key可能过长或者存在特殊字符时，是不能直接作为某些系统的key，因此往往会对原始key做一次hash来作为缓存的新key。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;strong&gt;&lt;span&gt;3. 缓存淘汰的策略&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 缓存淘汰的策略有两种：&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; (1) 定时去清理过期的缓存。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp;（2）当有用户请求过来时，再判断这个请求所用到的缓存是否过期，过期的话就去底层系统得到新数据并更新缓存。&lt;/p&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; 两者各有优劣，第一种的缺点是维护大量缓存的key是比较麻烦的，第二种的缺点就是每次用户请求过来都要判断缓存失效，逻辑相对比较复杂，具体用哪种方案，大家可以根据自己的应用场景来权衡。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;&lt;div id="MySignature"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="blog_post_info_block"&gt;&lt;div id="blog_post_info"&gt;&lt;div id="BlogPostCategory"&gt;&amp;nbsp;&lt;/div&gt;&lt;div id="EntryTag"&gt;标签:&amp;nbsp;&lt;a href="http://www.cnblogs.com/lengyuhong/tag/%E5%88%86%E5%B8%83%E5%BC%8F/"&gt;分布式&lt;/a&gt;,&amp;nbsp;&lt;a href="http://www.cnblogs.com/lengyuhong/tag/%E7%BC%93%E5%AD%98/"&gt;缓存&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/Leo_wl/aggbug/2519813.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/Leo_wl/archive/2012/05/27/2519813.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
