[原创]C#的JSON数据格式转换方法

mikel阅读(1297)

之前一直用ASP.NET MVC中Json(value)方法转换成JsonResult格式的数据异步返回给JQuery进行处理,很爽,结果最近不用

ASP.NET MVC开发应用了,异步地区调用涉及到Json数据的获取发现居然没有好的Json格式数据转换方法像过去一样简单调用

很是不爽,搜了下找到个利用System.Runtime.Serialization.Json转换数据的方法,可以转换为JQuery可以识别的格式,不过

调用过程中发现它将实体类的属性名称转换成:”<属性名>k__BackingField ”

这种格式,调用起来很不方便,不过还是将这种方法编写的助手类贴出来:
注意需要引用:
首先,当然是项目是3.5的,

只引用一个System.Runtime.Serialization 是不够的,

还要添加 System.ServiceModel

System.ServiceModel.Web

的引用,OK
转载的文章中提到了

代码如下:

    /// <summary>
    /// FileName: JSONHelper.cs
    /// CLRVersion: 2.0.50727.3623
    /// Author: Mikel
    /// Corporation:
    /// Description:JSON格式数据转换助手类
    /// 1.将List<T>类型的数据转换为JSON格式
    /// 2.将T类型对象转换为JSON格式对象
    /// 3.将JSON格式对象转换为T类型对象
    /// DateTime: 2011-09-13 14:11:34
    /// </summary>
    public static class JSONHelper
    {
        /// <summary>
        /// 转换对象为JSON格式数据
        /// </summary>
        /// <typeparam name="T">类</typeparam>
        /// <param name="obj">对象</param>
        /// <returns>字符格式的JSON数据</returns>
        public static string GetJSON<T>(object obj)
        {
            string result = String.Empty;
            try
            {
                System.Runtime.Serialization.Json.DataContractJsonSerializer serializer =
                new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
                using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
                {
                    serializer.WriteObject(ms, obj);
                    result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return result;
        }
        /// <summary>
        /// 转换List<T>的数据为JSON格式
        /// </summary>
        /// <typeparam name="T">类</typeparam>
        /// <param name="vals">列表值</param>
        /// <returns>JSON格式数据</returns>
        public static string JSON<T>(List<T> vals)
        {
            System.Text.StringBuilder st = new System.Text.StringBuilder();
            try
            {
                System.Runtime.Serialization.Json.DataContractJsonSerializer s = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));

                foreach (T city in vals)
                {
                    using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
                    {
                        s.WriteObject(ms, city);
                        st.Append(System.Text.Encoding.UTF8.GetString(ms.ToArray()));
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return st.ToString();
        }
        /// <summary>
        /// JSON格式字符转换为T类型的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="jsonStr"></param>
        /// <returns></returns>
        public static T ParseFormByJson<T>(string jsonStr)
        {
            T obj = Activator.CreateInstance<T>();
            using (System.IO.MemoryStream ms =
            new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(jsonStr)))
            {
                System.Runtime.Serialization.Json.DataContractJsonSerializer serializer =
                new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
                return (T)serializer.ReadObject(ms);
            }
        }
    }

还是觉得ASP.NET MVC的Json转换比较地道,于是翻找其源码,发现是利用的.net Framework 3.5特有的
c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Extensions.dll
实现的Json数据格式转换,实现代码如下:

namespace System.Web.Mvc {
    using System;
    using System.Text;
    using System.Web;
    using System.Web.Script.Serialization;

    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class JsonResult : ActionResult {

        public Encoding ContentEncoding {
            get;
            set;
        }

        public string ContentType {
            get;
            set;
        }

        public object Data {
            get;
            set;
        }

        public override void ExecuteResult(ControllerContext context) {
            if (context == null) {
                throw new ArgumentNullException("context");
            }

            HttpResponseBase response = context.HttpContext.Response;

            if (!String.IsNullOrEmpty(ContentType)) {
                response.ContentType = ContentType;
            }
            else {
                response.ContentType = "application/json";
            }
            if (ContentEncoding != null) {
                response.ContentEncoding = ContentEncoding;
            }
            if (Data != null) {
#pragma warning disable 0618
                JavaScriptSerializer serializer = new JavaScriptSerializer();
                response.Write(serializer.Serialize(Data));
#pragma warning restore 0618
            }
        }
    }
}

看到这大家应该明白了,原来.net Framework3.5已经做好了转换的方法,只需要调用即可,不过提醒大家注意:
一定要先引用c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Extensions.dll
才能使用JavaScriptSerializer serializer = new JavaScriptSerializer();
切记!切记!

[转载]如何引用 System.Runtime.Serialization.Json

mikel阅读(1108)

[转载]如何引用 System.Runtime.Serialization.Json; – Blackie – 博客园.

今天新开的一个项目突然发现引用System.Runtime.Serialization.Json 提示 命名空间 不存在类型或命名空间名称 json

明明前段时间刚开发的WCF是很正常的引用的,认真比照了引用的文件位置是一样的,记得前段时间在CSDN上也有人抱怨看不到System.Runtime.Serialization.Json

后面发现

首先,当然是项目是3.5的,

只引用一个System.Runtime.Serialization 是不够的,

还要添加 System.ServiceModel

System.ServiceModel.Web

的引用,OK

System.Runtime.Serialization.Json 出来了,可以开始对JSON字符串转为实体了。

命名空间 不存在类型或命名空间名称 json

[转载]JSON 数据格式解析

mikel阅读(934)

[转载]JSON 数据格式解析 – chen eric – 博客园.

JSON 数据格式解析

和 XML 一样,JSON 也是基于纯文本的数据格式。由于 JSON 天生是为 JavaScript 准备的,因此,JSON 的数据格式非常简单,您可以用 JSON 传输一个简单的 String,Number,Boolean,也可以传输一个数组,或者一个复杂的 Object 对象。

String,Number 和 Boolean 用 JSON 表示非常简单。例如,用 JSON 表示一个简单的 String “ abc ”,其格式为:

"abc"

除了字符 "\/ 和一些控制符(\b\f\n\r\t)需要编码外,其他 Unicode 字符可以直接输出。下图是一个 String 的完整表示结构:
图 1. String 的完整表示结构
 图 1. String 的完整表示结构
一个 Number 可以根据整型或浮点数表示如下:
图 2. Number 的表示结构
 图 2. Number 的表示结构
这与绝大多数编程语言的表示方法一致,例如:

12345(整数)
            -3.9e10(浮点数)

Boolean 类型表示为 truefalse 。此外,JavaScript 中的 null 被表示为 null,注意,truefalsenull 都没有双引号,否则将被视为一个 String 。

JSON 还可以表示一个数组对象,使用 [] 包含所有元素,每个元素用逗号分隔,元素可以是任意的 Value,例如,以下数组包含了一个 String,Number,Boolean 和一个 null:

["abc",12345,false,null]

Object 对象在 JSON 中是用 {} 包含一系列无序的 Key-Value 键值对表示的,实际上此处的 Object 相当于 Java 中的 Map<String, Object>,而不是 Java 的 Class 。注意 Key 只能用 String 表示。

例如,一个 Address 对象包含如下 Key-Value:

city:Beijing
            street:Chaoyang Road
            postcode:100025(整数)

用 JSON 表示如下:

{"city":"Beijing","street":" Chaoyang Road ","postcode":100025}

其中 Value 也可以是另一个 Object 或者数组,因此,复杂的 Object 可以嵌套表示,例如,一个 Person 对象包含 name 和 address 对象,可以表示如下:

{"name":"Michael","address":
            {"city":"Beijing","street":" Chaoyang Road ","postcode":100025}
            }

JavaScript 处理 JSON 数据

上面介绍了如何用 JSON 表示数据,接下来,我们还要解决如何在服务器端生成 JSON 格式的数据以便发送到客户端,以及客户端如何使用 JavaScript 处理 JSON 格式的数据。

我们先讨论如何在 Web 页面中用 JavaScript 处理 JSON 数据。我们通过一个简单的 JavaScript 方法就能看到客户端如何将 JSON 数据表示给用户:

function handleJson() {
            var j={"name":"Michael","address":
            {"city":"Beijing","street":" Chaoyang Road ","postcode":100025}
            };
            document.write(j.name);
            document.write(j.address.city);
            }

假定服务器返回的 JSON 数据是上文的:

{"name":"Michael","address":
            {"city":"Beijing","street":" Chaoyang Road ","postcode":100025}
            }

只需将其赋值给一个 JavaScript 变量,就可以立刻使用该变量并更新页面中的信息了,相比 XML 需要从 DOM 中读取各种节点而言,JSON 的使用非常容易。我们需要做的仅仅是发送一个 Ajax 请求,然后将服务器返回的 JSON 数据赋值给一个变量即可。有许多 Ajax 框架早已包含了处理 JSON 数据的能力,例如 Prototype(一个流行的 JavaScript 库:http://prototypejs.org)提供了 evalJSON() 方法,能直接将服务器返回的 JSON 文本变成一个 JavaScript 变量:

new Ajax.Request("http://url", {
            method: "get",
            onSuccess: function(transport) {
            var json = transport.responseText.evalJSON();
            // TODO: document.write(json.xxx);
            }
            });

服务器端输出 JSON 格式数据

下面我们讨论如何在服务器端输出 JSON 格式的数据。以 Java 为例,我们将演示将一个 Java 对象编码为 JSON 格式的文本。

将 String 对象编码为 JSON 格式时,只需处理好特殊字符即可。另外,必须用 (") 而非 (') 表示字符串:

            static String string2Json(String s) {
            StringBuilder sb = new StringBuilder(s.length()+20);
            sb.append('\"');
            for (int i=0; i<s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
            case '\"':
            sb.append("\\\"");
            break;
            case '\\':
            sb.append("\\\\");
            break;
            case '/':
            sb.append("\\/");
            break;
            case '\b':
            sb.append("\\b");
            break;
            case '\f':
            sb.append("\\f");
            break;
            case '\n':
            sb.append("\\n");
            break;
            case '\r':
            sb.append("\\r");
            break;
            case '\t':
            sb.append("\\t");
            break;
            default:
            sb.append(c);
            }
            }
            sb.append('\"');
            return sb.toString();
            }

将 Number 表示为 JSON 就容易得多,利用 Java 的多态,我们可以处理 Integer,Long,Float 等多种 Number 格式:

            static String number2Json(Number number) {
            return number.toString();
            }

Boolean 类型也可以直接通过 toString() 方法得到 JSON 的表示:

            static String boolean2Json(Boolean bool) {
            return bool.toString();
            }

要将数组编码为 JSON 格式,可以通过循环将每一个元素编码出来:

            static String array2Json(Object[] array) {
            if (array.length==0)
            return "[]";
            StringBuilder sb = new StringBuilder(array.length << 4);
            sb.append('[');
            for (Object o : array) {
            sb.append(toJson(o));
            sb.append(',');
            }
            // 将最后添加的 ',' 变为 ']':
            sb.setCharAt(sb.length()-1, ']');
            return sb.toString();
            }

最后,我们需要将 Map<String, Object> 编码为 JSON 格式,因为 JavaScript 的 Object 实际上对应的是 Java 的 Map<String, Object> 。该方法如下:

            static String map2Json(Map<String, Object> map) {
            if (map.isEmpty())
            return "{}";
            StringBuilder sb = new StringBuilder(map.size() << 4);
            sb.append('{');
            Set<String> keys = map.keySet();
            for (String key : keys) {
            Object value = map.get(key);
            sb.append('\"');
            sb.append(key);
            sb.append('\"');
            sb.append(':');
            sb.append(toJson(value));
            sb.append(',');
            }
            // 将最后的 ',' 变为 '}':
            sb.setCharAt(sb.length()-1, '}');
            return sb.toString();
            }

为了统一处理任意的 Java 对象,我们编写一个入口方法 toJson(Object),能够将任意的 Java 对象编码为 JSON 格式:

            public static String toJson(Object o) {
            if (o==null)
            return "null";
            if (o instanceof String)
            return string2Json((String)o);
            if (o instanceof Boolean)
            return boolean2Json((Boolean)o);
            if (o instanceof Number)
            return number2Json((Number)o);
            if (o instanceof Map)
            return map2Json((Map<String, Object>)o);
            if (o instanceof Object[])
            return array2Json((Object[])o);
            throw new RuntimeException("Unsupported type: " + o.getClass().getName());
            }

我们并未对 Java 对象作严格的检查。不被支持的对象(例如 List)将直接抛出 RuntimeException 。此外,为了保证输出的 JSON 是有效的,Map<String, Object> 对象的 Key 也不能包含特殊字符。细心的读者可能还会发现循环引用的对象会引发无限递归,例如,精心构造一个循环引用的 Map,就可以检测到 StackOverflowException

            @Test(expected=StackOverflowError.class)
            public void testRecurrsiveMap2Json() {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("key", map);
            JsonUtil.map2Json(map);
            }

好在服务器处理的 JSON 数据最终都应该转化为简单的 JavaScript 对象,因此,递归引用的可能性很小。

最后,通过 Servlet 或 MVC 框架输出 JSON 时,需要设置正确的 MIME 类型(application/json)和字符编码。假定服务器使用 UTF-8 编码,则可以使用以下代码输出编码后的 JSON 文本:

            response.setContentType("application/json;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter pw = response.getWriter();
            pw.write(JsonUtil.toJson(obj));
            pw.flush();

小结

JSON 已经是 JavaScript 标准的一部分。目前,主流的浏览器对 JSON 支持都非常完善。应用 JSON,我们可以从 XML 的解析中摆脱出来,对那些应用 Ajax 的 Web 2.0 网站来说,JSON 确实是目前最灵活的轻量级方案。

[转载]LAMP网站架构方案分析

mikel阅读(818)

[转载]LAMP网站架构方案分析【精辟】 – PHP淮北 – 博客园.

LAMP(Linux- Apache-MySQL-PHP)网站架构是目前国际流行的Web框架,该框架包括:Linux操作系统,Apache网络服务器,MySQL数据 库,Perl、PHP或者Python编程语言,所有组成产品均是开源软件,是国际上成熟的架构框架,很多流行的商业应用都是采取这个架构,和 Java/J2EE架构相比,LAMP具有Web资源丰富、轻量、快速开发等特点,微软的.NET架构相比,LAMP具有通用、跨平台、高性能、低价格的 优势,因此LAMP无论是性能、质量还是价格都是企业搭建网站的首选平台。

对于大流量、大并发量的网站系统架构来说,除了硬件上使用高性能的服务器、负载均衡、CDN等之外,在软件架构上需要重点关注下面几个环节:使用高性能 的操作系统(OS)、高性能的网页服务器(Web Server)、高性能的数据库(Databse)、高效率的编程语言等。下面我将从这几点对其一一讨论。

操作系统

Linux操作系统有很多个不同的发行版,如Red Hat Enterprise Linux、SUSE Linux Enterprice、Debian、Ubuntu、CentOS等,每一个发行版都有自己的特色,比如RHEL的稳定,Ubuntu的易用,基于稳定性 和性能的考虑,操作系统选择CentOS(Community ENTerprise Operating System)是一个理想的方案。

CentOS(Community ENTerprise Operating System)是Linux发行版之一,是RHEL/Red Hat Enterprise Linux的精简免费版,和RHEL为同样的源代码,不过,RHEL和SUSE LE等企业版,提供的升级服务均是收费升级,无法免费在线升级,因此要求免费的高度稳定性的服务器可以用CentOS替代Red Hat Enterprise Linux使用。

LAMP网站架构方案分析

LAMP网站架构图

Web服务器、缓存和PHP加速

Apache是LAMP架构最核心的Web Server,开源、稳定、模块丰富是Apache的优势。但Apache的缺点是有些臃肿,内存和CPU开销大,性能上有损耗,不如一些轻量级的Web 服务器(例如nginx)高效,轻量级的Web服务器对于静态文件的响应能力来说远高于Apache服务器。

Apache做为Web Server是负载PHP的最佳选择,如果流量很大的话,可以采用nginx来负载非PHP的Web请求。nginx是一个高性能的HTTP和反向代理服 务器,Nginx以它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。Nginx不支持PHP和CGI等动态语言,但支持负载均衡和容 错,可和Apache配合使用,是轻量级的HTTP服务器的首选。

Web服务器的缓存也有多种方案,Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高 Apache的访问响应能力。Squid Cache是一个Web缓存服务器,支持高效的缓存,可以作为网页服务器的前置cache服务器缓存相关请求来提高Web服务器的速度,把Squid放在 Apache的前端来缓存Web服务器生成的动态内容,而Web应用程序只需要适当地设置页面实效时间即可。如访问量巨大则可考虑使用memcache作 为分布式缓存。

PHP的加速使用eAccelerator加速器,eAccelerator是一个自由开放源码PHP加速器,优化和动态内容缓存,提高了性能PHP脚 本的缓存性能,使得PHP脚本在编译的状态下,对服务器的开销几乎完全消除。它还有对脚本起优化作用,以加快其执行效率。使PHP程序代码执效率能提高 1-10倍。

具体的解决方案有以下几种:

1、squid + Apache + PHP + eAccelerator

使用Apache负载PHP,使用squid进行缓存,html或图片的请求可以直接由squid返回给用户。很多大型网站都采用这种架构。

2、nginx/Apache + PHP(fastcgi) + eAccelerator

使用nginx或Apache负载PHP,PHP使用fastcgi方式运行,效率较高。

3、nginx + Apache + PHP + eAccelerator

此方案综合了nginx和Apache的优点,使用Apache负载PHP,nginx负责解析其他Web请求,使用nginx的rewrite模块,Apache端口不对外开放。

数据库

开源的数据库中,MySQL在性能、稳定性和功能上是首选,可以达到百万级别的数据存储,网站初期可以将MySQL和Web服务器放在一起,但是当访问 量达到一定规模后,应该将MySQL数据库从Web Server上独立出来,在单独的服务器上运行,同时保持Web Server和MySQL服务器的稳定连接。

当数据库访问量达到更大的级别,可以考虑使用MySQL Cluster等数据库集群或者库表散列等解决方案。

总的来说,LAMP架构的网站性能会远远优于Windows IIS + ASP + Access(例如月光博客)这样的网站,可以负载的访问量也非常大,国内的大量个人网站如果想要支撑大访问量,采用LAMP架构是一个不错的方案。

综上所述,基于LAMP架构设计具有成本低廉、部署灵活、快速开发、安全稳定等特点,是Web网络应用和环境的优秀组合。

[转载]JQuery图表插件——Highcharts

mikel阅读(1058)

[转载]JQuery图表插件——Highcharts – LyIng.Net – 博客园.

因为项目中需要用到图表生成,所有Google了下,找到了这个插件,顺带写了个DEMO。点击跳转官网

先上三个图,分别是曲线、柱状、扇形。

图表中的数据纯属于DEMO的测试数据,没有实际用意。下面讲下大致的实现步骤

第一步,下载并且引用JS包(highcharts.js),theme顾名思义是放皮肤的。可以下载DEMO逐一试试就知道效果怎么样了,上图就 应用了两个样式。download中放的是打印和导出成图片的js文件(貌似是通过js上传到官网,然后再下载到本地),因为项目是放到内网的所以就没有 用了。

第二步,实现,贴代码。

var chart;
        $(document).ready(function() {
            chart = new Highcharts.Chart({
                chart: {
                    renderTo: 'container',          //放置图表的容器
                    plotBackgroundColor: null,
                    plotBorderWidth: null,
                    defaultSeriesType: 'line'   
                },
                title: {
                    text: 'JQuery曲线图演示'
                },
                subtitle: {
                    text: '副标题'
                },
                xAxis: {//X轴数据
                    categories: ['一月份', '二月份', '三月份', '四月份', '五月份', '六月份', '七月份', '八月份', '九月份', '十月份', '十一月份', '十二月份'],
                    labels: {
                        rotation: -45, //字体倾斜
                        align: 'right',
                        style: { font: 'normal 13px 宋体' }
                    }
                },
                yAxis: {//Y轴显示文字
                    title: {
                        text: '产量/百万'
                    }
                },
                tooltip: {
                    enabled: true,
                    formatter: function() {
                        return '<b>' + this.x + '</b><br/>' + this.series.name + ': ' + Highcharts.numberFormat(this.y, 1);
                    }
                },
                plotOptions: {
                    line: {
                        dataLabels: {
                            enabled: true
                        },
                        enableMouseTracking: true//是否显示title
                    }
                },
                series: [{
                    name: '杭州',
                    data: [7.0, 6.9, 9.5, 14.5, 18.4, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]
                }, {
                    name: '江西',
                    data: [4.0, 2.9, 5.5, 24.5, 18.4, 11.5, 35.2, 36.5, 23.3, 38.3, 23.9, 3.6]
                }, {
                    name: '北京',
                    data: [14.0, 12.9, 15.5, 14.5, 28.4, 21.5, 15.2, 16.5, 13.3, 28.3, 13.9, 13.6]
                }, {
                    name: '湖南',
                    data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]
}]
                });
            });

这写都是配置,最重要的就是series里面的数据了,如果需要从数据库中取出来的话,直接生成json然后赋值上去就OK了,效果很炫,还有动态感,感兴趣的朋友可以下载下来跑跑。

注意,DEMO里的download文件夹中的js文件,本来导出及打印的一些提示是英文,我已经修改成中文了,在引用js文件的时候需要 charset=”gb2312″,具体DEMO中我已经写好了注释。好的,到此为止。下载地址

[转载]ASP.NET性能优化之构建自定义文件缓存

mikel阅读(861)

[转载]ASP.NET性能优化之构建自定义文件缓存 – 陆敏技 – 博客园.

SP.NET的输出缓存(即静态HTML)在.NET4.0前一直是基于内存的。这意味着如果我们的站点含有大量的缓存,则很容易消耗掉本机内存。 现在,借助于.NET4.0中的OutputCacheProvider,我们可以有多种选择创建自己的缓存。如,我们可以把HTML输出缓存存储到 memcached分布式集群服务器,或者MongoDB中(一种常用的面向文档数据库,不妨阅读本篇http://msdn.microsoft.com/zh-cn/magazine/gg650661.aspx)。当然,我们也可以把缓存作为文件存储到硬盘上,考虑到可扩展性,这是一种最廉价的做法,本文就是介绍如果构建自定义文件缓存。

1:OutputCacheProvider

OutputCacheProvider是一个抽象基类,我们需要override其中的四个方法,它们分别是:

Add 方法,将指定项插入输出缓存中。

Get 方法,返回对输出缓存中指定项的引用。

Remove 方法,从输出缓存中移除指定项。

Set 方法,将指定项插入输出缓存中,如果该项已缓存,则覆盖该项。

2:创建自己的文件缓存处理类

该类型为FileCacheProvider,代码如下:

public class FileCacheProvider : OutputCacheProvider
{
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

public override void Initialize(string name, NameValueCollection attributes)
{
base.Initialize(name, attributes);
CachePath = HttpContext.Current.Server.MapPath(attributes["cachePath"]);
}

public override object Add(string key, object entry, DateTime utcExpiry)
{
Object obj = Get(key);
if (obj != null)    //这一步很重要
{
return obj;
}
Set(key,entry,utcExpiry);
return entry;
}

public override object Get(string key)
{
string path = ConvertKeyToPath(key);
if (!File.Exists(path))
{
return null;
}
CacheItem item = null;
using (FileStream file = File.OpenRead(path))
{
var formatter = new BinaryFormatter();
item = (CacheItem)formatter.Deserialize(file);
}

if (item.ExpiryDate &lt;= DateTime.Now.ToUniversalTime())
{
log.Info(item.ExpiryDate + "*" + key);
Remove(key);
return null;
}
return item.Item;
}

public override void Set(string key, object entry, DateTime utcExpiry)
{
CacheItem item = new CacheItem(entry, utcExpiry);
string path = ConvertKeyToPath(key);
using (FileStream file = File.OpenWrite(path))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(file, item);
}
}

public override void Remove(string key)
{
string path = ConvertKeyToPath(key);
if (File.Exists(path))
File.Delete(path);
}

public string CachePath
{
get;
set;
}

private string ConvertKeyToPath(string key)
{
string file = key.Replace('/', '-');
file += ".txt";
return Path.Combine(CachePath, file);
}
}

[Serializable]
public class CacheItem
{
public DateTime ExpiryDate;
public object Item;

public CacheItem(object entry, DateTime utcExpiry)
{
Item = entry;
ExpiryDate = utcExpiry;
}
}

有两个地方需要特别说明:

在Add方法中,有一个条件判断,必须做出这样的处理,否则缓存机制将会缓存第一次的结果,过了有效期后缓存讲失效并不再重建;

在示例程序中,我们简单的将缓存放到了Cache目录下,在实际的项目实践中,考虑到缓存的页面将是成千上万的,所以我们必须要做目录分级,否则寻找并读取缓存文件将会成为效率瓶颈,这会耗尽CPU。

3:配置文件

我们需要在Web.config中配置缓存处理程序是自定义的FileCacheProvider,即在  <system.web>下添加节点:


4:缓存的使用

我们假设在MVC的控制中使用(如果要在ASP.NET页面中使用,则在页面中包含<%@OutputCache VaryByParam=”none”  Duration=”10″ %>),可以看到,Index是未进行输出缓存的,而Index2进行了输出缓存,缓存时间为10秒。

public class HomeController : Controller
{
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static string s_conn = "Data Source=192.168.0.77;Initial Catalog=luminjidb;User Id=sa;Password=sa;";
public ActionResult Index()
{
using (DataSet ds = Common.SqlHelper.ExecuteDataset(s_conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
{
ViewBag.Message = ds.Tables[0].Rows[0]["name"].ToString();
}
return View();
}

[OutputCache(Duration = 10, VaryByParam = "none")]
public ActionResult Index2()
{
using (DataSet ds = Common.SqlHelper.ExecuteDataset(s_conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
{
ViewBag.Message = ds.Tables[0].Rows[0]["name"].ToString();
}
return View();
}
}

5:查看下效果

上面的代码,在访问了Index2后,将会在Cache文件夹下产生缓存文件,如下:

image

现在,我们开始评价下有输出缓存和无输出缓存的性能对比,模拟100个用户并发1000次请求如下:

image

可以看到,有输出缓存后,吞吐率明显提高了10倍。

6:代码下载

FileCacheProvider的原始代码来自于网络,我修改了其中的BUG,全部代码下载如下:MvcApplication20110907.rar

[转载]优酷网架构分享

mikel阅读(774)

[转载]优酷网架构分享 – 一场情动,半世流离 – 博客园.

记得以前给大家介绍过视频网站龙头老大YouTube的技术架构,相信大家看了都会有不少的感触,互联网就是这么一个神奇的东西。今天我突然想到,优酷网在国内也算是视频网站的老大了,不知道他的架构相对于YouTube是怎么样的,于是带着这个好奇心去网上找了优酷网架构的各方面资料,虽然谈得没有YouTube那么详细,但多少还是挖掘了一点,现在总结一下,希望对喜欢架构的朋友有所帮助。

一、网站基本数据概览

  • 据2010年统计,优酷网日均独立访问人数(uv)达到了8900万,日均访问量(pv)更是达到了17亿,优酷凭借这一数据成为google榜单中国内视频网站排名最高的厂商。
  • 硬件方面,优酷网引进的戴尔服务器主要以 PowerEdge 1950与PowerEdge 860为主,存储阵列以戴尔MD1000为主,2007的数据表明,优酷网已有1000多台服务器遍布在全国各大省市,现在应该更多了吧。

二、网站前端框架

从一开始,优酷网就自建了一套CMS来解决前端的页面显示,各个模块之间分离得比较恰当,前端可扩展性很好,UI的分离,让开发与维护变得十分简单和灵活,下图是优酷前端的模块调用关系:

这样,就根据module、method及params来确定调用相对独立的模块,显得非常简洁。下面附一张优酷的前端局部架构图:

三、数据库架构

应该说优酷的数据库架构也是经历了许多波折,从一开始的单台MySQL服务器(Just Running)到简单的MySQL主从复制、SSD优化、垂直分库、水平sharding分库,这一系列过程只有经历过才会有更深的体会吧,就像MySpace的架构经历一样,架构也是一步步慢慢成长和成熟的。

1、简单的MySQL主从复制:

MySQL的主从复制解决了数据库的读写分离,并很好的提升了读的性能,其原来图如下:

其主从复制的过程如下图所示:

但是,主从复制也带来其他一系列性能瓶颈问题:

  1. 写入无法扩展
  2. 写入无法缓存
  3. 复制延时
  4. 锁表率上升
  5. 表变大,缓存率下降

那问题产生总得解决的,这就产生下面的优化方案,一起来看看。

2、MySQL垂直分区

如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且万一其中一个业务崩溃了也不会影响其他业务的正常进行,并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。经过垂直分区后的数据库架构图如下:

然而,尽管业务之间已经足够独立了,但是有些业务之间或多或少总会有点联系,如用户,基本上都会和每个业务相关联,况且这种分区方式,也不能解决单张表数据量暴涨的问题,因此为何不试试水平sharding呢?

3、MySQL水平分片(Sharding)

这是一个非常好的思路,将用户按一定规则(按id哈希)分组,并把该组用户的数据存储到一个数据库分片中,即一个sharding,这样随着用户数量的增加,只要简单地配置一台服务器即可,原理图如下:

如何来确定某个用户所在的shard呢,可以建一张用户和shard对应的数据表,每次请求先从这张表找用户的shard id,再从对应shard中查询相关数据,如下图所示:

但是,优酷是如何解决跨shard的查询呢,这个是个难点,据介绍优酷是尽量不跨shard查询,实在不行通过多维分片索引、分布式搜索引擎,下策是分布式数据库查询(这个非常麻烦而且耗性能)

四、缓存策略

貌似大的系统都对“缓存”情有独钟,从http缓存到memcached内存数据缓存,但优酷表示没有用内存缓存,理由如下:

  1. 避免内存拷贝,避免内存锁
  2. 如接到老大哥通知要把某个视频撤下来,如果在缓存里是比较麻烦的

而且Squid 的 write() 用户进程空间有消耗,Lighttpd 1.5 的 AIO(异步I/O) 读取文件到用户内存导致效率也比较低下。

但为何我们访问优酷会如此流畅,与土豆相比优酷的视频加载速度略胜一筹?这个要归功于优酷建立的比较完善的内容分发网络(CDN),它 通过多种方式保证分布在全国各地的用户进行就近访问——用户点击视频请求后,优酷网将根据用户所处地区位置,将离用户最近、服务状况最好的视频服务器地址 传送给用户,从而保证用户可以得到快速的视频体验。这就是CDN带来的优势,就近访问,有关CDN的更多内容,请大家Google一下。

备注:本文转载自http://www.it-ezone.com

[转载]工作多年的.NET程序员,是否建立了自己的开发知识库?分享制作电子书的经验

mikel阅读(836)

[转载]工作多年的.NET程序员,是否建立了自己的开发知识库?分享制作电子书的经验 – James Li – 博客园.

经过多年的编程经验的积累,工作中肯定会遇到很多问题,也都通过各种办法解决了。无论是 上网搜索,或是向同事寻求帮忙,在遇到问题时,都会把这个系列的问题的网页都保存起来,以便以后查找起来方便。这样,随着时间的积累,知识库的内容会越来 越多,如何整理这些文件,方便查找,一直是工作之外努力的方向。

曾经用EverNote建立各种知识库,如下图所示,定期抽出时间,在网上搜索,看到好的文章,都剪贴下来

image

几个月下来,就积累了几十兆的数据,大部分是含有简单格式的文本数据,可设置格式,打印,非常方便。而且EverNote这个软件是绿色版,一共才8M多。每次要重装系统或是搬动数据资料,也连同软件一起拷贝。

与 EverNote同名的是微软的OneNote,一直不喜欢微软的捆绑风格,为了记个笔记,搜集点资料,要装那么大的软件,而且有时候会安装不成功,文件 组织方式也不同于EverNote,EverNote会按照数据库来组织你的笔记内容,你可以把相关的知识内容都放到一个数据库中,OneNote是按照 文件名来组织笔记,相比两者,更喜欢EverNote的风格。

更多的时候,忙于工作,没有时间上网,一直等到空闲的时候,想找个安静的地方学习(图书馆),又没有条件上网,如果你面对这样的情况,推荐下面这种方式给你,依照这个步骤,可以让你达到同样的效果。

1 下载网页文件到本机磁盘中

image

Data Loader是我为了搜集资料而开发的一个软件,如图所示。文本编辑器中是博客园中排名靠前的博主,他们的文章质量高,读起来效果好,项目忙,工作忙,常 常没有时间去一页页的读,然后拷贝到WORD,于是就想到用这个软件,分门别类,把他们的文章,都下载到本机磁盘中。点击Start按钮后,后台程序会下 载他们的文章到本地磁盘的G:\Document中。

image

所采用的格式是mht格式,可以保存图片,mht也是邮件的格式。如果可能,也可以通过互操作类型,把它存成DOC/DOCX格式,PDF格式也行,做这件事,对于程序员来说,不难。

2  Power CHM 登场,制作成CHM格式

博客园中Foundation博主写的工作流系列非常精彩,我用Power CHM导入上图中的目录,效果如下

image

这些是简单的活,不需要复杂的技术,点击File->Import Directory即可。可能有的页面会报脚本错误,没有关系,先不管,到后面再编辑它。点击Tools->CHM Compile ,生成CHM文件。
Power CHM已经内置了编译CHM文件的类型库,不需要安装Help workshop。顺便来看一下它的文件结构,我对这个hh.exe,hha.ddl感觉特别亲切,因为自己也曾经调用它来生成CHM文件,只是没有想到 做得更好,做成像Power CHM这样优秀的软件。这使我想到,做技术的致命缺点,懂一点,但又懂得不精,没有深入的研究,浅尝辙止。

image

等待一段时间,喝杯茶提提神,或是吃个苹果,片刻之后,就可以看到产生出的CHM文件了,如下图所示

image

我们就轻松的获取了CHM格式的电子书。依照这个办法,你可以做很多电子书。

在 我的Data Loader开放下载之前,你可以使用其他的网页下载软件,把网页下载到本地。实在找不到,写一个,调用WebClient类型,也应该是个把钟头的事 情,不会太难。先不要考虑太多问题,比如多线程,比如异步,你只是需要一个小程序来帮忙你把指定的网页下载到本地,记住,先把问题简化,有助于达到最终的 目的。

3  编辑CHM文件 整理成精致的CHM电子书

每位开发人员对技术的定位点不同,不同的开发阶段有不同的知识需求,这样,需要对CHM文件的内容进行取舍,制作需要的电子书。启动CHM Editor,打开刚才生成的Foundation.chm文件,如下图所示

image

CHM Editor专门用来编辑CHM文件,可对它直接进行编辑,即时保存。把不需要的文章删除,不需要的部分去掉,保存之后,就是我们需要的专题电子书了。此外,CHM Editor也是制作CHM文件的好工具,有了这个工具,你可以忽视网页

的存在。直接编辑,即时保存,就是你所需要的电子书文件。

CHM 格式仍然是Windows平台上流行的电子书格式文件,EXE格式容易被报病毒,或是被感染,PDF的格式查找不方便,缺少了CHM文件左边的树型导航 (虽然可以做出书签来,但是大量的免费的书都没有书签,自己做又很麻烦),会逊色很多。不过PDF着重点在于可移植的文档格式,是通用的格式,而CHM只 适用于Windows平台。

[转载]使用Windbg知道程序运行时的命令行参数.

mikel阅读(1067)

[转载]使用Windbg知道程序运行时的命令行参数. – 嗷嗷 – 博客园.

如何才能一个程序运行时的command line arguments是什么?其实这个信息被记录在了进程的process environment block中,我们可以用Windbg很方便的找出这个信息。

比如说,我在cmd上打开一个notepad, 如下

clip_image001

当notepad运行起来以后,使用windbg attach到notepad, 然后使用命令!peb打印出process environment block. 要注意symbol path要正确。

然后就能看到下面这些信息了。

clip_image002

我们能知道CommandLine了,也能知道DllPath和Environment了.

那么有时候这个程序运行的太快了,还来不及attach就跑完了怎么办?

没关系,我们可以设置notepad.exe一运行就被windbg断下来。这要用到windbg下的另外一个非常有用的工具了,gflags.exe. 按如下图设置notepad.exe

clip_image003

其效果是在注册表中添加了下面一些信息:

clip_image004

现在只要notepad以运行,首先就会被windbg断下来了。

总结,其实也不一定使用Windbg,使用VS的Debugger也是可以的,原理一样,就不详述了。

另外,使用Process Monitor来得到程序运行参数更方便

最后嘛,惯例是要配图的。不过最近手上没有好图,下次吧。

[转载]FLEX+Webservice 大附件上传、断点续传实现

mikel阅读(1170)

转载FLEX+Webservice 大附件上传、断点续传实现 – 大侠酷裤马路 – 博客园.

最近项目需要,面临大附件上传的功能,具体研究如下:
实现思路
大附件上传,如何流畅不占用内存,还要支持断点续传,当第一次看到这 些需求的时候还是有所顾虑,传统ASP.NET中利用fileupload可以实现上传,但是webconfig中文件大小受限制,即使设置大小了也将面 临超时的问题。对于上述情况,WINFORM应该能够很好的解决断点续传大文件,当应用到WEB应用中的时候就很难如此轻松了,因此富客户端思想是很好的 选择,决定采用FLEX实现客户端,Webservice实现客户端。
由于ASP.NET默认支持4M大小文件上传,一次需要将需要上传的文件进行分割,客户端分块上传,服务端分块追加。
具体实现
1)、客户端实现

客户端界面设计

<s:Group width=”100%” height=”100%”>
<s:Button id=”btnBrower” x=”390″ y=”25″ label=”浏览…” width=”60″ click=”btnBrower_clickHandler(event)”></s:Button>
<s:TextInput id=”edFile” x=”21″ y=”24″ width=”361″ enabled=”false”/>
<s:Button id=”btnUpload” x=”458″ y=”25″ label=”开始上传” width=”71″ click=”btnUpload_clickHandler(event)”/>
<mx:Canvas width=”508″ height=”25″ backgroundColor=”0Xf1f1f1″ x=”21″ y=”53″ borderStyle=”solid” borderColor=”0Xbbbbbb”>
<mx:Label text=”” fontWeight=”bold” id=”tip_txt” x=”5″ y=”4″/>
</mx:Canvas>
<mx:Canvas id=”totalProcess” borderStyle=”solid” x=”22″ width=”507″ y=”82″ height=”13″ borderColor=”0X124fc0″ backgroundColor=”0xffffff”>
<mx:Canvas backgroundColor=”0X124fc0″ backgroundAlpha=”0.5″ id=”processBar_Total” width=”0″ height=”23″/>
</mx:Canvas>

</s:Group>

定义Webservice方法,对应服务端,各自有自己的返回事件

<s:WebService id=service wsdl=../Service.asmx?wsdl useProxy=false>
<s:operation name=WriteFile result=onResult(event) fault=onFault(event)>
</s:operation>
<s:operation name=CheckFile result=onCheckResult(event) fault=onCheckFault(event)>
</s:operation>
<s:operation name=CopyFile result=onCopyResult(event) fault=onCopyFault(event)>
</s:operation>
</s:WebService>

浏览需要上传的文件

//页面加载完毕,进行相关事件注册
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
// 注册调用js函数
ExternalInterface.addCallback(getFileInfo,GetFileInfo);

//初始化文件浏览事件
file.addEventListener(Event.SELECT,onSelect);//选择文件事件
file.addEventListener(Event.COMPLETE,onComplete);//文件加载完毕
file.addEventListener(Event.OPEN,onOpen);
btnUpload.enabled
=false;
}

//浏览文件
private function onSelect(evt:Event):void
{
this.tip_txt.text=“”;
edFile.text
=file.name;
//浏览完成,开始加载
file.load();
}
//加载文件完毕
private function onComplete(evt:Event):void
{
this.tip_txt.text=加载完毕;
btnUpload.enabled
=true;
}

private function onOpen(evt:Event):void
{
this.tip_txt.text=正在加载…;
}

校验服务端文件是否存在,不存在则创建,存在则判断是否需要断点续传

//校验文件函数
private function CheckFile():void
{
//调用webservice方法,传递文件名进行校验
service.CheckFile.send(file.name);
}
//webervice,校验成功
private function onCheckResult(event:ResultEvent):void
{
//获取返回值
var retArray:Array=new Array();
retArray
=event.result.toString().split(,);
var retMsg:String=retArray[0].toString();//返回文件存在与否消息
var retNum:int=int(retArray[1].toString());//返回文件大小
tip_txt.text=retMsg;

//判断是否存在文件
if(retNum != 0)
{
//若文件已经存在,判断文件是否已经上传完毕
if(retNum == file.data.length)
tip_txt.text
=文件已上传完毕;
else
{
tip_txt.text
=准备断点续传;

//断点续传需要重新计算块数,剩余大小
var Leave:int= file.data.lengthretNum;

//判断剩余情况
if(Leave>blocksize)
{
//剩余部分分块
var BlockNum2:Number=(Leave / blocksize);
BlockNum
=int(BlockNum2);
BlockNumles
=int(BlockNum2);
reBlock
=Leave % blocksize;
tip_txt.text
=正在处理…;
//调用上传函数
uploadFile(retNum,blocksize);
}
else
{
//直接从返回值大小开始,传递剩余部分
BlockNumles=1;
uploadFile(retNum,reBlock);
}
}
}
else
{
//若文件不存在,则创建,从0开始
var BlockNum1:Number=(file.data.length / blocksize);
BlockNum
=int(BlockNum1);
BlockNumles
=int(BlockNum1);
reBlock
=file.data.length % blocksize;

tip_txt.text=正在处理…;
uploadFile(retNum,blocksize);
}
}

private function onCheckFault(event:FaultEvent):void
{
Alert.show(event.toString());
}

开始上传

//上传附件函数
private function uploadFile(begin:int,end:int):void
{
//判断文件大小
if(file.data.length>blocksize)
{
//读取部分文件,分块上传
fileUpload.writeBytes(file.data,begin,end);
service.WriteFile.send(file.name,fileUpload);
}
else
{
//直接上传
service.WriteFile.send(file.name,fileUpload);
}
}

//webservice相关事件函数,上传成功
private function onResult(event:ResultEvent):void
{
//每次上传成功返回值,作为下次传递的开始位置
var begin:int=int(event.result.toString());
BlockNumles
-=1;//递减
BlockNumadd+=1;//递增
//清空历史数据
fileUpload.clear();

//进度条
onProgress(begin,file.data.length);
//判断剩余块多少,进行不同情况的上传
if(BlockNumles>0)
{
uploadFile(begin,blocksize);
}

if(BlockNumles==0)
{
uploadFile(begin,file.data.length
begin);
tip_txt.text
=上传完毕!;
tip_txt.text
=开始扫描文件…;
service.CopyFile.send(file.name);
}
}
//上传失败
private function onFault(event:FaultEvent):void
{
Alert.show(
event.toString());
}

进度监视

//上传进度条
private function onProgress(Loaded:int,Total:int):void
{
processBar_Total.width
=(Loaded/Total)*506;
tip_txt.text
=已上传: + Loaded+/+Total;
if(Loaded==Total)
tip_txt.text
=已上传完毕;
}

上传完毕处理文件,完毕之后需要对文件进行类似处理,在这里是对文件进行重命名。具体在客户端可以体现出来

//复制文件
private function onCopyResult(event:ResultEvent):void
{
tip_txt.text
=扫描完成;
//文件上传结束,调用js函数
var f:String = showButton;
var m:String
= ExternalInterface.call(f);
trace(m);
}

private function onCopyFault(event:FaultEvent):void
{
Alert.show(
event.toString());
}

2)、服务端实现
对应客户端三个方法实现,分别是校验、上传、上传完毕
校验文件,并返回值

#region 校验文件是否存在
[WebMethod]
public string CheckFile(string FileName)
{
string FileSavePath = Server.MapPath(File/) + FileName;
if (!IsExistFile(FileSavePath))
return 文件不存在,0;
else
{
string FileSize = GetFileSize(FileSavePath).ToString();
return 文件已存在, + FileSize;
}
}
#endregion

开始写文件,没有则创建,有则追加

#region 写文件
[WebMethod]
public string WriteFile(string FileName, byte[] filestrem)
{
string FileSavePath = Server.MapPath(File/) + FileName + .temp;
if (!IsExistFile(FileSavePath))
{
FileStream fs
= new FileStream(FileSavePath, FileMode.Create);
//获得字节数组
byte[] data = filestrem;
//开始写入
fs.Write(data, 0, data.Length);
//清空缓冲区、关闭流
fs.Flush();
fs.Close();
}
else
{
//追加文件
using (System.IO.FileStream f = new System.IO.FileStream(FileSavePath, System.IO.FileMode.Append, FileAccess.Write))
{
byte[] b = filestrem;
f.Write(b,
0, b.Length);
}
}

return GetFileSize(FileSavePath).ToString();
}
#endregion

上传完毕

#region
[WebMethod]
public string CopyFile(string FileName)
{
string FileSavePath = Server.MapPath(File/) + FileName+.temp;
string FileConvertPath = Server.MapPath(ConvertFile/) + FileName;

//如果目标中存在同名文件,则删除
if (IsExistFile(FileConvertPath))
{
DeleteFile(FileConvertPath);
}
//将文件复制到指定目录
File.Copy(FileSavePath, FileConvertPath);

//删除原始临时文件
DeleteFile(FileSavePath);

string Path = FileConvertPath;
return Path;
}
#endregion

存在问题
客户端要把文件读取完毕之后,才开始分段上传,如果文件过大,内存玩儿不转那么浏览器将会死掉。需要继续改进,请大家拍 砖!!源代码全部奉上,了解flex的可以看看flex部分源码,不了解的可以直接在项目中使用,已经在.NET项目中配置完毕,可直接运行看到效果。本 例子仅为beta1.0版本,还在继续修改当中。

效果图:

源码:断点续传.rar