WCF学习之:利用Throttling提高服务器性能

    如果WCF服务用到服务器的稀缺资源,该资源的创建和销毁极耗服务器时间和性能,这种情况如果提高服务器性能呢?
    我们知道WCF的实例模式有三种:Per-Call 、Per-Session 、Singleton。我们来分析一下:第一种Per-Call ,每一个Client Call都会一个服务实例和资源对象,这样的性能是无法忍受的;第二种Per-Session ,这种情况稍为改善了服务器性能,但是海量并发请求的时候比前者好不到哪去;第三种Singleton单件模式,保证只有一个服务实例和资源对象,但是并发访问时候会发生线程冲突。
     在这种情况下,资源池派上了用场,把大操作对象(资源对象)放到一个池里面,这个池是启动服务之后提供服务之前创建好的,响应客户端请求时从池里获取一个可用的资源对象,请求响应过后释放该资源,使之响应下一个请求。
     关键点在于:当并发请求数大于资源对象池大小时,如何排队等候。我们来看WCF是怎么处理这种情况的。WCF有三种Service Concurrency Mode:Single、Multiple、Reentrant。默认是Single,每次只能响应一个请求,其它请求被阻塞;Multiple服务实例是多线程的,所有并发请求立刻被响应,无同步保证,但是我们可以用Throttling节流措施来限制并发请求数,使并发数刚好等于资源池的大小,其它的请求排队等候,保证每个请求都有可用资源。看下面的例子:
     以GDI+为例,我们创建Graphic绘图对象池,Client请求服务器给它绘制一幅图。
1、先设计一个服务契约:

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Runtime.Serialization;
using  System.ServiceModel;
using  System.Text;

namespace  Tloner.Throttling.WcfServiceLibrary1
{
    
// 注意: 如果更改此处的接口名称“IService1”,也必须更新 App.config 中对“IService1”的引用。
    [ServiceContract]
    
public interface IService1
    
{
        [OperationContract]
        
byte[] DrawImg(string str);
    }

}

2、设计一个资源类和资源池
     public   class  MyResource
    
{
        
public Bitmap bitmap;
        
public Graphics graphics;
        
public bool usable;
        
public int resourceIndex;

        
public MyResource(int index)
        
{
            bitmap 
= new Bitmap(500500);
            graphics 
= Graphics.FromImage(bitmap);
            usable 
= true;
            resourceIndex 
= index;
        }

    }


    
public   class  ResourceCollection : List < MyResource >
    
{
        
public ResourceCollection(int capacity)
        
{
            
for (int i = 0; i < capacity; i++)
            
{
                
this.Add(new MyResource(i));
            }

        }

        
public MyResource CatchResource()
        
{
            
lock (this)
            
{
                
for (int i = 0; i < this.Count; i++)
                
{
                    
if (this[i].usable == true)
                    
{
                        
this[i].usable = false;
                        Console.WriteLine(
"catch resource index:{0}", i);
                        
return this[i];
                    }

                }

            }

            
return null;
        }


        
public void ReleaseResource(int index)
        
{
            
lock (this)
            
{
                Console.WriteLine(
"release resource index:{0}", index);
                
this[index].usable = true;
            }

        }

    }
ResourceCollection资源池的构建函数里按照容量添加资源对象
CatchResource()方法用于获取一个可用资源来响应请求
ReleaseResource(int index)方法释放一个资源,使它为其它请求服务

3、服务实现
    [ServiceBehavior(InstanceContextMode  =  InstanceContextMode.Single, ConcurrencyMode  =  ConcurrencyMode.Multiple)]
    
public   class  Service1 : IService1
    
{
        ResourceCollection resourceCollection;

        
public Service1()
        
{
            resourceCollection 
= new ResourceCollection(3);
            Console.WriteLine(
"create a ResourceCollection");
            Console.WriteLine(
"create a service instance");
        }


        
public byte[] DrawImg(string str)
        
{
            MyResource resource 
= null;
            MemoryStream stream 
= new MemoryStream(); ;
            
try
            
{
                resource 
= resourceCollection.CatchResource();
                System.Diagnostics.Debug.Assert(resource 
!= null"no free resource");

                Console.WriteLine(
"using resource index:{0}", resource.resourceIndex);

                resource.graphics.Clear(Color.White);
                resource.graphics.DrawRectangle(
new Pen(Color.Red), 00498498);
                Font drawFont 
= new Font("Arial"30);
                SolidBrush drawBrush 
= new SolidBrush(Color.Red);
                resource.graphics.DrawString(str, drawFont, drawBrush, 
100100);

                resource.bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);

                Thread.Sleep(
3000);
            }

            
catch (Exception ex)
            
{
                Console.WriteLine(ex.Message);
            }

            
finally
            
{
                resourceCollection.ReleaseResource(resource.resourceIndex);
            }


            
return stream.GetBuffer();
        }


        
~Service1()
        
{
            Console.WriteLine(
"destroy a service instance");
        }

    }
实例模式设为 InstanceContextMode = InstanceContextMode.Single,保证只有一个服务实例,该实例是在服务Host的时候创建的。响应DrawImg(string str)请求的时候catch一个可用Graphic对象资源用来绘制,绘制完了把它Release释放掉。为了更好展现请求状态,在每次DrawImg时让线程等待3秒。

4、Host服务
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.ServiceModel;
using  Tloner.Throttling.WcfServiceLibrary1;

namespace  host
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            
using (ServiceHost serivceHost = new ServiceHost(typeof(Service1)))
            
{
                serivceHost.Opened 
+= delegate
                
{
                    Console.WriteLine(
"Service has begun to listen  ");
                }
;

                serivceHost.Open();

                Console.Read();
            }

        }

    }

}
Configuration:
<? xml version = " 1.0 "  encoding = " utf-8 "   ?>
< configuration >
    
< system.web >
        
< compilation debug = " true "   />
    
</ system.web >
    
< system.serviceModel >
        
< services >
            
< service name = " Tloner.Throttling.WcfServiceLibrary1.Service1 "  behaviorConfiguration = " WcfServiceLibrary1.Service1Behavior " >
                
< host >
                    
< baseAddresses >
                        
< add baseAddress  =   " http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/ " />
                    
</ baseAddresses >
                
</ host >
                
< endpoint address  = ""  binding = " basicHttpBinding "  contract = " Tloner.Throttling.WcfServiceLibrary1.IService1 " >
                    
< identity >
                        
< dns value = " localhost " />
                    
</ identity >
                
</ endpoint >
                
< endpoint address = " mex "  binding = " mexHttpBinding "  contract = " IMetadataExchange " />
            
</ service >
        
</ services >
        
< behaviors >
            
< serviceBehaviors >
                
< behavior name = " WcfServiceLibrary1.Service1Behavior " >
                    
< serviceThrottling maxConcurrentCalls = " 3 " />
                    
< serviceMetadata httpGetEnabled = " True " />
                    
< serviceDebug includeExceptionDetailInFaults = " False "   />
                
</ behavior >
            
</ serviceBehaviors >
        
</ behaviors >
    
</ system.serviceModel >
</ configuration >
注意,这里的 <serviceThrottling maxConcurrentCalls="3"/>设置为跟服务实现里的resourceCollection = new ResourceCollection(3)的值一致,并发数和池大小相等才能保证每个请求都有可用的资源。
5、client调用服务
using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;
using  System.Threading;
using  System.ServiceModel;
using  Tloner.Throttling.WcfServiceLibrary1;
using  System.ServiceModel.Channels;
using  System.Drawing;
using  System.IO;

namespace  client
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            
try
            
{
                
for (int i = 0; i < 10; i++)
                
{
                    ThreadStart ts 
= new ThreadStart(ClientCall);
                    Thread th 
= new Thread(ts);
                    th.Start();
                }

            }

            
catch (Exception ex)
            
{
                Console.WriteLine(ex.Message);
            }


            Console.Read();
        }


        
static void ClientCall()
        
{
            
using (ThrottlingClient client = new ThrottlingClient("throttling"))
            
{
                
byte[] data = client.DrawImg(Thread.CurrentThread.ManagedThreadId.ToString());
                MemoryStream stream 
= new MemoryStream(data);
                Bitmap bitmap 
= new Bitmap(stream);
                bitmap.Save(
@"c:\" + Thread.CurrentThread.ManagedThreadId.ToString() + ".png", System.Drawing.Imaging.ImageFormat.Png);                
                Console.WriteLine(
"CurrentThreadID: {0}", Thread.CurrentThread.ManagedThreadId.ToString());
            }

        }

    }


    
class ThrottlingClient : ClientBase<IService1>, IService1
    
{
        
public ThrottlingClient()
            : 
base()
        
{ }

        
public ThrottlingClient(string endpointConfigurationName)
            : 
base(endpointConfigurationName)
        
{ }

        
public ThrottlingClient(Binding binding, EndpointAddress address)
            : 
base(binding, address)
        
{ }

        
public byte[] DrawImg(string str)
        
{
            
return this.Channel.DrawImg(str);
        }

    }

}

创建10个线程模拟10个并发请求,把服务器绘制返回的图保存到本地。
Configuration:
<? xml version = " 1.0 "  encoding = " utf-8 "   ?>
< configuration >
    
< system.serviceModel >
        
< bindings >
            
< basicHttpBinding >
                
< binding name = " throttlingBinding "
                 sendTimeout
= " 00:10:00 "
                 transferMode
= " Streamed "
                 maxReceivedMessageSize
= " 92233720368 " >
                    
< readerQuotas maxDepth = " 32 "  maxStringContentLength = " 8192 "  maxArrayLength = " 963840000 "
                        maxBytesPerRead
= " 4096 "  maxNameTableCharCount = " 16384 "   />
                
</ binding >
            
</ basicHttpBinding >
        
</ bindings >
        
< client >
            
< endpoint address = " http://localhost:8732/Design_Time_Addresses/WcfServiceLibrary1/Service1/ "  binding = " basicHttpBinding "  bindingConfiguration = " throttlingBinding "
              contract
= " Tloner.Throttling.WcfServiceLibrary1.IService1 "  name = " throttling "   />
        
</ client >
    
</ system.serviceModel >
</ configuration >

我们来看看运行结果:

我们可以看到每个资源都是catch-->using-->release,资源释放之前不会被cactht和using.

也许有考虑欠周到的地方,欢迎大家给点意见,共同研究

源码下载: download

转载于:https://www.cnblogs.com/tloner/archive/2008/04/17/1158437.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值