藉由GZipStream的压缩,来减少Web Service的传输量

转帖地址:http://blog.csdn.net/cnming/archive/2008/12/20/3561125.aspx

 

不知道有多少人有遇到跟我一样的问题,就是Web Service的资料回传量太大了,如果都是走区域网路的话,除非量很大,不然还感觉不太出来,可是,如果是透过ADSL的频宽的话,那就很惊人了,以 30MB的资料量来说,透过2M/512K的网路上传,加上网路品质的损耗以65%来看,也大概要花738秒左右来上传. (30*1024)/((512*0.65)/8)=738.46. 这样的时间,看起来很吓人,何况在上传满载时,下载的频宽可能剩40%不到,如果今天能压缩这资料量,让资料量剩25%左右,那上传时间就只要185秒左右,这样的差异就很大了,如果今天我们採用的是n-tier架构,Client是透过Web Service在要资料,那要怎麽去压缩这传输过程中的资料? 这时就可以拿GZipStream出来用了.


  起初,GZipStream只是被我拿来当档桉压缩用的,刚好这段时间遇到公司ERP系统效能调校,突然产生了一个想法,如果我能拿它来压缩资料,那传输的资料量不就小很多,尤其是XML的文字档,效果一定更明显,就在这个念头下,着手开始写了测试的程式,在这裡,我做了两种不同的做法,一个是纯压缩,不Serialize,另一种做法是压缩与Serialize,而这两种做法,是会产生些微的资料量差异.

在提到程式內容之前,先說明這做法所造成的資料量差異,詳見下表 :

正常資料量

壓縮後

壓縮+Serialize

18,368,067 Bytes

4,755,570 Bytes

4,445,278 Bytes

17,937.57 Kb

4,644.11 Kb

4,341.09 Kb




接下来,就是程式码部份,分两个部份说明:

1 .纯压缩 :

WebService Side :

using System.IO;
using System.IO.Compression;



        [WebMethod]
        public byte[] getZipData()
        {
            DataSet ds = LoadData().Copy();
            MemoryStream unMS = new MemoryStream();
            ds.WriteXml(unMS);

            byte[] bytes = unMS.ToArray();
            int lenbyte = bytes.Length;

            MemoryStream compMs = new MemoryStream();
            GZipStream compStream = new GZipStream(compMs, CompressionMode.Compress, true);
            compStream.Write(bytes, 0, lenbyte);

            compStream.Close();
            unMS.Close();
            compMs.Close();
            byte[] zipData = compMs.ToArray();
            return zipData;
        }

        private DataSet LoadData()
        {//产生测试资料用
            DataSet ds = new DataSet();
            DataTable dt = new DataTable("Test");
            dt.Columns.Add("ProID",typeof(int));
            dt.Columns.Add("ProName", typeof(string));
            dt.Columns.Add("CreateTime", typeof(DateTime));
            dt.Columns["ProID"].AutoIncrement = true;
            for (int i = 0; i < 100000; i++)
            {
                DataRow dr = dt.NewRow();
                dr["ProName"] = Guid.NewGuid().ToString();
                dr["CreateTime"] = DateTime.Now.ToString();
                dt.Rows.Add(dr);
            }
            ds.Tables.Add(dt);
            ds.AcceptChanges();
            return ds;
        }


Client Side :

using System.IO.Compression;
using System.IO;

       private void btn_ZipGet_Click(object sender, EventArgs e)
       {
           try
           {
               WS.Service1 wss = new WSZipDemo.WS.Service1();//WebReference
               byte[] da = wss.getZipData();

               MemoryStream input = new MemoryStream();
               input.Write(da, 0, da.Length);
               input.Position = 0;
               GZipStream gzip = new GZipStream(input, CompressionMode.Decompress, true);

               MemoryStream output = new MemoryStream();
               byte[] buff = new byte[4096];
               int read = -1;
               read = gzip.Read(buff, 0, buff.Length);
               while (read > 0)
               {
                   output.Write(buff, 0, read);
                   read = gzip.Read(buff, 0, buff.Length);
               }
               gzip.Close();
               byte[] rebytes = output.ToArray();
               output.Close();
               input.Close();

               MemoryStream ms = new MemoryStream(rebytes);
               DataSet ds = new DataSet();
               ds.ReadXml(ms);
               dataGridView1.DataSource = ds.Tables[0];
           }
           catch (Exception ex)
           {
               MessageBox.Show(ex.Message);
           }
       }


2. 压缩+Serialize

Web Service Side :

using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;



[WebMethod]
public byte[] getZipData()
{
    DataSet ds = LoadData().Copy();
    ds.RemotingFormat = SerializationFormat.Binary;
    BinaryFormatter ser = new BinaryFormatter();
    MemoryStream unMS = new MemoryStream();
    ser.Serialize(unMS, ds);

    byte[] bytes = unMS.ToArray();
    int lenbyte = bytes.Length;

    MemoryStream compMs = new MemoryStream();
    GZipStream compStream = new GZipStream(compMs, CompressionMode.Compress, true);
    compStream.Write(bytes, 0, lenbyte);

    compStream.Close();
    unMS.Close();
    compMs.Close();
    byte[] zipData = compMs.ToArray();
    return zipData;
}

private DataSet LoadData()
{//产生测试资料用
    DataSet ds = new DataSet();
    DataTable dt = new DataTable("Test");
    dt.Columns.Add("ProID",typeof(int));
    dt.Columns.Add("ProName", typeof(string));
    dt.Columns.Add("CreateTime", typeof(DateTime));
    dt.Columns["ProID"].AutoIncrement = true;
    for (int i = 0; i < 100000; i++)
    {
        DataRow dr = dt.NewRow();
        dr["ProName"] = Guid.NewGuid().ToString();
        dr["CreateTime"] = DateTime.Now.ToString();
        dt.Rows.Add(dr);
    }
    ds.Tables.Add(dt);
    ds.AcceptChanges();
    return ds;
}


Client Side :

using System.IO.Compression;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;



private void btn_ZipGet_Click(object sender, EventArgs e)
{
    try
    {
        WS.Service1 wss = new WSZipDemo.WS.Service1();//WebReference
        byte[] da = wss.getZipData();

        MemoryStream input = new MemoryStream();
        input.Write(da, 0, da.Length);
        input.Position = 0;
        GZipStream gzip = new GZipStream(input, CompressionMode.Decompress, true);

        MemoryStream output = new MemoryStream();
        byte[] buff = new byte[4096];
        int read = -1;
        read = gzip.Read(buff, 0, buff.Length);
        while (read > 0)
        {
            output.Write(buff, 0, read);
            read = gzip.Read(buff, 0, buff.Length);
        }
        gzip.Close();
        byte[] rebytes = output.ToArray();
        output.Close();
        input.Close();

        MemoryStream ms = new MemoryStream(rebytes);
        BinaryFormatter bf = new BinaryFormatter();
        object obj = bf.Deserialize(ms);
        DataSet ds = (DataSet)obj;
        dataGridView1.DataSource = ds.Tables[0];
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

 


  这两种做法只是在部份的程式码不一样,但其它大多相同,如果是方法一,在WebService压缩后Return,Client收到资料后,解压缩即可,方法二则多了Serialize这部份,所以在WebService这边,先Serialize,再压缩,Client端收到后,先解压缩再 Deserialize. 不过,这个压缩后的量,是很让人满意,如果在频宽有限,又需要传输大量资料时,这个方法可以考虑看看. 因为这个是.Net 2.0以后才有的东西,公司现在的ERP还在.Net 1.0,所以.........残念~

 

2008/04/29 补充 :

  最近有网友看到这篇文章,有些问题问我朋友,而朋友再来转问我这个问题(真巧,网友问我朋友,我朋友再来问我),而提出的问题是,压缩跟解压是否很耗用CPU的效能? 答桉是,不可能不用到CPU的效能,但它也不致于到"很耗用"的地步. 而这耗用率只能视设备状况来判断,以我的环境来说,Client端的电脑是P4 3GHT, 上面的这个例子跑的时候有个瞬间最高47%,不到一秒的时间,如果是未压缩版的,瞬间最高为37%左右,所以大概多个10%吧.

  或许有人还是有疑虑怎麽可以增加CPU的Loading呢,这样就不好了,这时我们就要换个角度来思考这个问题了,"效能成本"所在为何,我们的Bottleneck在那.CPU在科技的进步下,双核四核的都推出了,CPU的效能是快速的在倍增中,而我们的网路呢? 绝大多数区域还在100MB,部份Server与Server是1G在连,而ADSL呢? 10M/1M或者是只能2M/512K,因为申请不到更快的频宽,如果要更快,每月的费用就更高, 注意囉,上传是1MB或512K哦,而且是多人共用的,所以就目前国内的网路环境来看,ADSL要到100M/100M,似乎还要很长的一段时间,而这段时间的CPU,也不知道已经成长到几核心了,两者之间怎麽取捨,就看大家的看法囉.

 

2008/5/30 补充

  因为一些朋友对这个方式感到有兴趣,也在网路上找了一些文章,可是却又发现了一些疑问,到底这个压缩技术能用在什麽样的情况下. 其中一位朋友传给了我一个网址.Net DataTable 大量资料压缩加密实测, 也是说明用压缩的方式来减少流量,一些测试结果也很详尽,有兴趣的人建议参考,只是朋友在他的文章开头的地方看到 [开发 Web 或分散式系统],这是否代表Web网站也可以用? 这个答桉当然是没用的,试想,我们在Web Server压缩,Client端用IE或Firefox怎麽解压缩,网页是无法用Gzipstream的方式来解压缩来减少Web Server到Client的资料量,能压一定要能解才有用,所以就不用再去列出那些可以及那些不行了,毕竟这还关係的架构上的问题,所以应用的关键就是"能压要能解才能用".

  另一个问题就是选择性的使用,压缩的动作势必用到系统的效能,如果全面性100%的採用压缩,当使用者多,或操作频率高时,资料量大,系统效能也会变的更加吃重,所以必需挑选几个关键的传输来压缩即可,不用连1K不到的资料量也在压缩,除非Server很勐的,那就另当别论. 依我的情况,我会挑选几个使用者的查询作业来压,依目前手头上的分析资料来看,有个查询作业使用频率较高,资料量也是惊人,最高的资料量就一次高达 33MB,平均起来跟其它作业比较,这作业的资料量佔了不小的比率,光这作业一天平均传输量650MB,压缩后只剩162.5MB在传,所以只压缩这个作业的查询动作,就可明显的改善ADSL的频宽瓶颈. 所以要视情况去选择要压缩的作业.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值