Wcf uses IDispatchMessageInspector to monitor log records and throttling concurrency.

Source: Internet
Author: User

Wcf uses IDispatchMessageInspector to monitor log records and throttling concurrency.

Generally, although you know the business scenarios in which the provided interfaces will be called, you do not know when the interfaces will be called, the frequency of calls, and the interface performance, when a problem occurs, it is not easy to reproduce the request. To trace the content, you need to record the call information of each interface completely, that is, record the log. Logs can record the caller's ip address, Server ip address, call time point, duration, and input/output, you can find the starting point accurately for troubleshooting, recreating exceptions, and performance bottlenecks.

Of course, no one wants to insert a piece of code in each Operation. It would be better if there is something like AOP.

In wcf, there is an IDispatchMessageInspector,

1 namespace System. ServiceModel. Dispatcher 2 {3 using System; 4 using System. ServiceModel; 5 using System. ServiceModel. Channels; 6 7 public interface IDispatchMessageInspector 8 {
// Triggered after receiving the request. The returned value of this method is passed into 9 object AfterReceiveRequest (ref MessageRequest, IClientChannel channel, InstanceContext instanceContext );
// 10 void BeforeSendReply (ref MessageReply, Object correlationState); 11} 12}

The following is an English explanation:

         

That is, we can check and modify the incoming and outgoing messages of the service, which looks a bit like the mvc filter.

Execution Process: AfterReceiveRequest->Wcf operations->BeforeSendReply

 

The entry point has been found. What we need to do is to implement this interface method, record the log, and calculate the number of visits within a period of time and set the corresponding threshold value, that is, concurrent traffic limiting can be realized, the purpose of Throttling is to prevent malicious calls to maintain the stability of your servers.

Implementation ideas:

1 public class ThrottleDispatchMessageInspector: IDispatchMessageInspector 2 {3 public object AfterReceiveRequest (ref Message request, IClientChannel channel, InstanceContext instanceContext) 4 {5 // [concurrent throttling] 6 Use ContractName + OperationName as the key for MemoryCache. The value is the number of calls. The absolute expiration time is set to 7 if (times> set threshold value) 8 request. close (); directly Close request 9 10 // [log] records the request input message, start time, Server ip address, Client ip address, and access wcf contract and method... 11 LogVO log12 // [log] uses the log entity as the return value 13 return log; 14 15} 16 public void BeforeSendReply (ref Message reply, object correlationState) {17 // [log] adds the complete log attribute: Request end time, call duration... then drop the log into the Queue (do not directly Insert the database to prevent the log record from affecting the interface performance) and gradually implement 18 var log = correlationState as LogVO; 19 log => Queue 20} 21} In rds}

The complete code is as follows:

1 // custom sending and distributing message checker 2 public class ThrottleDispatchMessageInspector: IDispatchMessageInspector 3 {4 // TODO these two parameters are stored according to the system configuration and processing method, 5 public static int throttleNum = 10; // Number of Throttling requests 6 public static int throttleUnit = 4; // s 7 8 CacheItemPolicy policy = new CacheItemPolicy (); //! Expiration Policy, ensure that the absolute expiration time of the first set and the subsequent set is consistent 9 10 # region implement IDispatchMessageInspector11 12 // the return value of this method will be passed as the second parameter object correlationState of the BeforeSendReply method to 13 public object afterReceiveRequest (ref Message request, IClientChannel channel, InstanceContext instanceContext) 14 {15 16 17 // obtain the ContractName and OperationName as the cache key 18 var context = OperationContext. current; 19 string contractName = context. endpointDis Patcher. contractName; 20 string operationName = string. empty; 21 if (context. incomingMessageHeaders. action = null) 22 {23 operationName = request. properties. values. lastOrDefault (). toString (); 24} 25 else26 {27 if (context. incomingMessageHeaders. action. contains ("/") 28 {29 operationName = context. incomingMessageHeaders. action. split ('/'). lastOrDefault (); 30} 31} 32 string throttleCacheKey = contractName + "_" + OperationName; 33 // cache the current request frequency to cache the System in memory. runtime. caching. memoryCache (. net4.0 +) 34 ObjectCache cache = MemoryCache. default; 35 var requestCount = cache. get (throttleCacheKey); 36 int currRequestCount = 1; 37 if (requestCount! = Null & int. tryParse (requestCount. toString (), out currRequestCount) 38 {39 // number of visits + 140 currRequestCount ++; 41 cache. set (throttleCacheKey, currRequestCount, policy); // The expiration policy must be consistent with that of the first set. Otherwise, there may be a problem with the expiration time 42} 43 else44 {45 policy. absoluteExpiration = DateTime. now. addSeconds (throttleUnit); 46 cache. set (throttleCacheKey, currRequestCount, policy); 47} 48 49 // if the current number of requests exceeds the threshold, close 50 if (currRequestCount> thrott LeNum) 51 {52 request. close (); 53} 54 55 // send it as the return value to BeforeSendReply 56 LogVO log = new LogVO57 {58 BeginTime = DateTime. now, 59 ContractName = contractName, 60 OperationName = operationName, 61 Request = this. messageToString (ref request), 62 Response = string. empty63}; 64 return log; 65} 66 67 public void BeforeSendReply (ref Message reply, object correlationState) 68 {69 70 // supplement the logs transmitted by AfterReceiveRequest Object Attributes, record 71 LogVO log = correlationState as LogVO; 72 log. endTime = DateTime. now; 73 log. response = this. messageToString (ref reply); 74 log. duration = (log. endTime-log. beginTime ). totalMilliseconds; 75 76 // attention does not affect the interface performance. The log entity is pushed to the queue (redis. etc), and then write the text here ~ 78 try79 {80 string logPath = "D :\\ WcfLog.txt"; 81 if (! File. exists (logPath) 82 {83 File. create (logPath); 84} 85 StreamWriter writer = new StreamWriter (logPath, true); 86 writer. write (string. format ("at {0}, {1} is called, duration: {2} \ r \ n", log. beginTime, log. contractName + ". "+ log. operationName, log. duration); 87 writer. close (); 88} 89 catch (Exception ex) {} 90} 91 # endregion92}View Code

Note: 1. similar to HttpContext on the Web. current. the Cache uses the corresponding memory Cache for MemoryCache. When the Cache value is updated, the expiration policy must be consistent with that set for the first time. If no value is input, the Cache will not expire;

2. Configure concurrency restrictions based on your system framework. Do not overwrite them.

3. log records should not be directly stored in rds. Otherwise, the number of concurrent connections in rds is high, which may affect the api processing speed (you can push to redis, job/service land data)

4. Read the Message data of the Message carrier of wcf and write it again (this method is directly used by foreigners)

Then, as long as the user-defined service behavior is injected into the distribution runtime by the user-defined sending message checker in the ApplyDispatchBehavior method, you can directly paste the Code:

1 // method of customizing service behavior of an Application 2: 2 // 1. inherit Attribute as a feature service marked 3 // 2. inherit BehaviorExtensionElement and modify the configuration file 4 public class ThrottleServiceBehaviorAttribute: Attribute, IServiceBehavior 5 {6 # region implement IServiceBehavior 7 public void AddBindingParameters (ServiceDescription serviceDescription, System. serviceModel. serviceHostBase serviceHostBase, System. collections. objectModel. collection <ServiceEndpoint> endpoints, System. serviceModel. channels. bindingParameterCollection bindingParameters) 8 {9 10} 11 12 public void ApplyDispatchBehavior (ServiceDescription serviceDescription, System. serviceModel. serviceHostBase serviceHostBase) 13 {14 foreach (ChannelDispatcher channelDispather in serviceHostBase. channelDispatchers) 15 {16 foreach (var endpoint in channelDispather. endpoints) 17 {18 // holyshit DispatchRuntime 19 endpoint. dispatchRuntime. messageInspectors. add (new ThrottleDispatchMessageInspector (); 20} 21} 22} 23 24 public void Validate (ServiceDescription serviceDescription, System. serviceModel. serviceHostBase serviceHostBase) 25 {26 27} 28 # endregion29 30 # region override BehaviorExtensionElement31 // public override Type BehaviorType32 // {33 // get {return typeof (ThrottleServiceBehavior );} 34 //} 35 36 // protected override object CreateBehavior () 37 // {38 // return new ThrottleServiceBehavior (); 39 //} 40 # endregion41}

Here, because I am relatively lazy, I will directly inherit the Attribute and paste the service behavior on the service; a better way is to inheritBehaviorExtensionElementThen, register the custom behavior in the configuration file to let all interfaces go through the logic of the custom checker.

 

Test: threshold value 10 times per 4 seconds

Get a Service

 1  [ThrottleServiceBehavior] 2     public class Service1 : IService1 3     { 4         public string GetData() 5         { 6             object num = MemoryCache.Default.Get("IService1_GetData") ?? "0"; 7  8             return string.Format("already request {0} times, Throttle is : {1} per {2} seconds", num, WcfDispatchMessageInspector.ThrottleDispatchMessageInspector.throttleNum, WcfDispatchMessageInspector.ThrottleDispatchMessageInspector.throttleUnit); 9         }10     }

 

1. Brush 5 times in four seconds:

2. More than 10 times:

Disconnected directly ~

 

Complete code

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.