We have an architecture built around service bus, which is working well. We are currently also using the same name-spaced service bus to do our logging via a bespoke Serilog sink (which is going to be replaced with datadog).
This has caused us in our test environment, to occasionally be throttled via service bus (we are on standard and not premium). We know that its a problem with our side and are working to resolve it.
However, I want to update our code to handle the throttling exception more gracefully and potentially add a delay before it tries again.
What is not clear from the documentation is the exception details:
https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-throttling
We currently catch MessagingException - its not clear if this message will be thrown.
Has anyone handled throttled exceptions in this way?
At the moment the resolution seems to be catch the error, check the exception message against the exception message (I think looking at the errorcode is the least tightly coupled implementation) and put it dedicated code to react to it.
We have few exceptions in service bus such as QuotaExceededExceptions, ServerBusyExceptions, MessageSizeExceededExceptions, TransactionSizeExceededExceptions.
And in some throttling conditions there are several retries to ensure to deliver the messages at the end.
With Azure Monitor we can handle the throttles from metrics:
Below are few steps which I went through:
Can check throttle requests
We can select ServiceBusThrottling
You can check this blog to understand about handling throttle calls with Azure functions for Service bus.
Below is the sample code:
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.ServiceBus.Messaging;
using System.Data.SqlClient;
using System.Text;
using System;
namespace BallardChalmers.BackgroundFunctions
{
public static class BallardChalmersBackgroundTaskCreated
{
[FunctionName("BallardChalmersBackgroundTaskCreated")]
public static void
Run([ServiceBusTrigger("BallardChalmersBackgroundTaskCreated", AccessRights.Send, Connection = "ServiceBusConnection")]string queueMessage, TraceWriter log)
{
log.Info($"Service_TaskManager task added to queue with ID: {queueMessage}");
try
{
string connectionString = System.Configuration.ConfigurationManager.AppSettings["SQLConnectionString"]; ;
using (SqlConnection connection = new SqlConnection(connectionString))
{
// Create wait period between 1 and 60 seconds
System.Random random = new System.Random();
int waitPeriod = random.Next(1, 60);
log.Info($"Wait period: {waitPeriod.ToString()}");
connection.Open();
StringBuilder sb = new StringBuilder();
sb.Append("INSERT FunctionsLog ");
sb.Append("SELECT '" + queueMessage + "','Started','" + DateTime.Now.ToString() + "','" + DateTime.Now.ToString() + "'," + waitPeriod.ToString());
String sql = sb.ToString();
log.Info($"Insert command: {sql}");
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.ExecuteNonQuery();
}
log.Info($"Inserted to FunctionsLog");
System.Threading.Thread.Sleep(1000 * waitPeriod);
log.Info($"Wait completed");
sb = new StringBuilder();
sb.Append("UPDATE FunctionsLog ");
sb.Append("SET [Status]='Completed', Updated='" + DateTime.Now.ToString() + "'");
sb.Append("WHERE ID='" + queueMessage + "'");
sql = sb.ToString();
log.Info($"Update command: {sql}");
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.ExecuteNonQuery();
}
log.Info($"Updated to FunctionsLog");
}
}
catch (SqlException e)
{
log.Error($"Error writing to database: {e.Message + ":::" + e.StackTrace}");
}
}
}
}
Related
I created a .NET core 6 project. I added Azure.Messaging.ServiceBus as the dependency. I am using below code to send message to service bus topic.
// See https://aka.ms/new-console-template for more information
using Azure.Messaging.ServiceBus;
using System.Dynamic;
using System.Net;
using System.Text;
using System.Text.Json;
Console.WriteLine("Hello, World!");
Sender t = new Sender();
Sender.Send();
class Sender
{
public static async Task Send()
{
string connectionString = "Endpoint=sb://sb-test-one.servicebus.windows.net/;SharedAccessKeyName=manage;SharedAccessKey=8e+6SWp3skB3AeDlwH6ufGEainEs45353435JzDywz5DU=;";
string topicName = "topicone";
string subscriptionName = "subone";
// The Service Bus client types are safe to cache and use as a singleton for the lifetime
try
{
await using var client = new ServiceBusClient(connectionString, new ServiceBusClientOptions
{
TransportType = ServiceBusTransportType.AmqpWebSockets
});
// create the sender
ServiceBusSender sender = client.CreateSender(topicName);
dynamic data = new ExpandoObject();
data.name = "Abc";
data.age = 6;
// create a message that we can send. UTF-8 encoding is used when providing a string.
var messageBody = JsonSerializer.Serialize(data);
ServiceBusMessage message = new ServiceBusMessage(messageBody);
// send the message
await sender.SendMessageAsync(message);
var s = 10;
}
catch (Exception e)
{
var v = 10;
}
//// create a receiver for our subscription that we can use to receive the message
//ServiceBusReceiver receiver = client.CreateReceiver(topicName, subscriptionName);
//// the received message is a different type as it contains some service set properties
//ServiceBusReceivedMessage receivedMessage = await receiver.ReceiveMessageAsync();
//// get the message body as a string
//string body = receivedMessage.Body.ToString();
//Console.WriteLine(body);
Console.WriteLine("Press any key to end the application");
Console.ReadKey();
}
}
Issue: When I call await sender.SendMessageAsync(message); after this line get executed, the program is actually terminating. It not awating. The whole execution stops after this line.
System is not throwing any exception and service bus is not receiving any message.
I just noticed that all other samples I saw had a default SharedAccessPolicy called RootManageSharedAccessKey policy available by default in the azure portal. For me, I had to create this policy. To my policy I have given Manage, Send, ReceiveAccess.
Needed to change Sender.Send(); to Sender.Send().GetAwaiter().GetResult();
I just follow the instructions as below:
https://github.com/Azure/azure-functions-vs-build-sdk
tried to add new version of newtonsoft.json and then installed the latest SharePointPnPCoreOnline.
It works well in my project and also I could do a unit test for my event-grid trigger locally.
But after I deploy to azure,an error will happen.It seems the function did not load the proper DLL
Method not found: 'Newtonsoft.Json.Linq.JObject Microsoft.Azure.WebJobs.Extensions.EventGrid.EventGridEvent.get_Data()'.
and executed this code when error
[FunctionName("ProcessRequest")]
[Obsolete]
public static void Run([EventGridTrigger] string eventGridEvent, TraceWriter log)
{
EventGridEvent eventGridEventData = JsonConvert.DeserializeObject<EventGridEvent>(eventGridEvent);
var siteCreationInfo = eventGridEventData.Data.ToObject<SiteRequest>();
}
very confused about the issue and made all my solutions but could not find the way.
Under this condition and if we have to use both of these libraries,it seems we could not to convert the object to eventgrid object directly.
EventGridEvent eventGridEventData = eventGridEvent.ToObject<EventGridEvent>();
because of the libraries conflict,we can not use this function directly.
We should get the key and value separately:
JObject eventGridData = JObject.Parse(eventGridEvent);
var eventId = eventGridData["id"];
var siteData = eventGridData["data"];
we should do data conversion in the simple way
The solution to overcoming this issue is by first manually installing a newer version of Newtonsoft.Json via NuGet.
Check the references.
My test project, it has warnings, but code runs successfully.
string webTitle = string.Empty;
JObject jObject = JObject.Parse(#"{
'CPU': 'Intel',
'Drives': [
'DVD read/writer',
'500 gigabyte hard drive'
]
}");
try
{
//Create the client context
using (var clientContext = authenticationManager.GetSharePointOnlineAuthenticatedContextTenant(authArray[0], authArray[1], authArray[2]))
{
var web = clientContext.Web;
clientContext.Load(web);
clientContext.ExecuteQuery();
Console.WriteLine(web.Title);
webTitle = web.Title;
}
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
webTitle = ex.Message;
}
return req.CreateResponse(HttpStatusCode.OK, "Hello " + webTitle+ jObject["CPU"]);
We have a SQL server running on a development laptop and would like to we'd like to deploy multiple laptops and use Azure Sync to distribute changes to each user. Performance is too slow to have a remote SQL database with our current application -- that's a separate issue. Speed isn't critical and I don't anticipate collisions between users. I set the update to 12 hours and conflict resolution to member win.
Everything seems to be working as intended except the Microsoft SQL Data Sync 2.0 Windows service process is continuously consuming 2%-5% of the CPU and streaming data at about 80 kbps continuously. I'm worried this will drain the batteries when the laptops are in the field. Is there a better way to do this?
Here's the resource utilization from the Azure database. I stopped the sync at some point to see if the frequency setting would automatically restart it (it does not).
You can write your own sync application based on your needs. This and this resources provides you a good guidance. Below you will see a sample application.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using Microsoft.Synchronization.Data.SqlServer;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization;
namespace SQLAzureDataSync
{
class Program
{
public static string sqlazureConnectionString = "Server=[Your SQL Azure Server].database.windows.net;Database=AdventureWorksLTSQLAzure;User ID=[Your SQL Azure User Name]#[Your SQL Azure Server];Password=[Your SQL Azure Password];Trusted_Connection=False;Encrypt=True;";
public static string sqllocalConnectionString = "Server=(local);Database=AdventureWorksLT2008;Trusted_Connection=True";
public static readonly string scopeName = "alltablesyncgroup";
static void Main(string[] args)
{
// Test if input arguments were supplied:
if (args.Length == 0)
{
System.Console.WriteLine("Please enter an argument.");
System.Console.WriteLine("Usage: SyncTest.exe -setup");
System.Console.WriteLine(" SyncTest.exe -sync");
}
else if (args[0] == "-setup")
Setup();
else if (args[0] == "-sync")
Sync();
}
public static void Setup()
{
try
{
SqlConnection sqlServerConn = new SqlConnection(sqllocalConnectionString);
SqlConnection sqlAzureConn = new SqlConnection(sqlazureConnectionString);
DbSyncScopeDescription myScope = new DbSyncScopeDescription(scopeName);
DbSyncTableDescription Customer = SqlSyncDescriptionBuilder.GetDescriptionForTable("Customer", sqlServerConn);
DbSyncTableDescription Product = SqlSyncDescriptionBuilder.GetDescriptionForTable("Product", sqlServerConn);
// Add the tables from above to the scope
myScope.Tables.Add(Customer);
myScope.Tables.Add(Product);
// Setup SQL Server for sync
SqlSyncScopeProvisioning sqlServerProv = new SqlSyncScopeProvisioning(sqlServerConn, myScope);
if (!sqlServerProv.ScopeExists(scopeName))
{
// Apply the scope provisioning.
Console.WriteLine("Provisioning SQL Server for sync " + DateTime.Now);
sqlServerProv.Apply();
Console.WriteLine("Done Provisioning SQL Server for sync " + DateTime.Now);
}
else
Console.WriteLine("SQL Server Database server already provisioned for sync " + DateTime.Now);
// Setup SQL Azure for sync
SqlSyncScopeProvisioning sqlAzureProv = new SqlSyncScopeProvisioning(sqlAzureConn, myScope);
if (!sqlAzureProv.ScopeExists(scopeName))
{
// Apply the scope provisioning.
Console.WriteLine("Provisioning SQL Azure for sync " + DateTime.Now);
sqlAzureProv.Apply();
Console.WriteLine("Done Provisioning SQL Azure for sync " + DateTime.Now);
}
else
Console.WriteLine("SQL Azure Database server already provisioned for sync " + DateTime.Now);
sqlAzureConn.Close();
sqlServerConn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public static void Sync()
{
try
{
SqlConnection sqlServerConn = new SqlConnection(sqllocalConnectionString);
SqlConnection sqlAzureConn = new SqlConnection(sqlazureConnectionString);
SyncOrchestrator orch = new SyncOrchestrator
{
LocalProvider = new SqlSyncProvider(scopeName, sqlAzureConn),
RemoteProvider = new SqlSyncProvider(scopeName, sqlServerConn),
Direction = SyncDirectionOrder.UploadAndDownload
};
Console.WriteLine("ScopeName={0} ", scopeName.ToUpper());
Console.WriteLine("Starting Sync " + DateTime.Now);
ShowStatistics(orch.Synchronize());
sqlAzureConn.Close();
sqlServerConn.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public static void ShowStatistics(SyncOperationStatistics syncStats)
{
string message;
message = "\tSync Start Time :" + syncStats.SyncStartTime.ToString();
Console.WriteLine(message);
message = "\tSync End Time :" + syncStats.SyncEndTime.ToString();
Console.WriteLine(message);
message = "\tUpload Changes Applied :" + syncStats.UploadChangesApplied.ToString();
Console.WriteLine(message);
message = "\tUpload Changes Failed :" + syncStats.UploadChangesFailed.ToString();
Console.WriteLine(message);
message = "\tUpload Changes Total :" + syncStats.UploadChangesTotal.ToString();
Console.WriteLine(message);
message = "\tDownload Changes Applied :" + syncStats.DownloadChangesApplied.ToString();
Console.WriteLine(message);
message = "\tDownload Changes Failed :" + syncStats.DownloadChangesFailed.ToString();
Console.WriteLine(message);
message = "\tDownload Changes Total :" + syncStats.DownloadChangesTotal.ToString();
Console.WriteLine(message);
}
}
}
I wrote a simply java program (jdk 1.7) that lists all my service bus topics and prints out the name of each topic to stdout:
try {
String namespace = "myservicebus"; // from azure portal
String issuer = "owner"; // from azure portal
String key = "asdjklasdjklasdjklasdjklasdjk"; // from azure portal
Configuration config = ServiceBusConfiguration.configureWithWrapAuthentication(
namespace,
issuer,
key,
".servicebus.windows.net",
"-sb.accesscontrol.windows.net/WRAPv0.9");
ServiceBusContract service = ServiceBusService.create(config);
ListTopicsResult result = service.listTopics();
List<TopicInfo> infoList = result.getItems();
for(TopicInfo info : infoList) {
System.out.println( info.getPath());
}
} catch (Exception e) {
e.printStackTrace();
}
Now, i am trying to run this example in a simple android project (Android 4.2) but it wont work.
The runtime always throws following error:
java.lang.RuntimeException: Service or property not registered: com.microsoft.windowsazure.services.serviceBus.ServiceBusContract
Has anyone successfully established a connection from an android device (or emulator) to azure service bus?
Does the Microsoft Azure-Java-SDK not support android projects?
Thanks in advance
This error is due to the fact that apks generated do not include (remove) the ServiceLoader information (under META-INF/services). You can test yourself deleting it from the jar generated and see that the same error appears. In the documentation it is said that it is now supported, but i found problems to use it.
http://developer.android.com/reference/java/util/ServiceLoader.html
You can include manually the data in the apk using ant
Keep 'META-INF/services'-files in apk
After 10h debugging, removing classes manually, including META-INF/services, etc, I found that the Azure SDK uses some classes not supported by Android (javax.ws.*) and any woraround works for me.
So I would recommend using the REST API in Android environments, find below the source code i used to sebd messages to the topic.
private static String generateSasToken(URI uri) {
String targetUri;
try {
targetUri = URLEncoder
.encode(uri.toString().toLowerCase(), "UTF-8")
.toLowerCase();
long expiresOnDate = System.currentTimeMillis();
int expiresInMins = 20; // 1 hour
expiresOnDate += expiresInMins * 60 * 1000;
long expires = expiresOnDate / 1000;
String toSign = targetUri + "\n" + expires;
// Get an hmac_sha1 key from the raw key bytes
byte[] keyBytes = sasKey.getBytes("UTF-8");
SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");
// Get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
// Compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(toSign.getBytes("UTF-8"));
// using Apache commons codec for base64
// String signature = URLEncoder.encode(
// Base64.encodeBase64String(rawHmac), "UTF-8");
String rawHmacStr = new String(Base64.encodeBase64(rawHmac, false),"UTF-8");
String signature = URLEncoder.encode(rawHmacStr, "UTF-8");
// construct authorization string
String token = "SharedAccessSignature sr=" + targetUri + "&sig="
+ signature + "&se=" + expires + "&skn=" + sasKeyName;
return token;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void Send(String topic, String subscription, String msgToSend) throws Exception {
String url = uri+topic+"/messages";
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
// Add header
String token = generateSasToken(new URI(uri));
post.setHeader("Authorization", token);
post.setHeader("Content-Type", "text/plain");
post.setHeader(subscription, subscription);
StringEntity input = new StringEntity(msgToSend);
post.setEntity(input);
System.out.println("Llamando al post");
HttpResponse response = client.execute(post);
System.out.println("Response Code : "
+ response.getStatusLine().getStatusCode());
if (response.getStatusLine().getStatusCode() != 201)
throw new Exception(response.getStatusLine().getReasonPhrase());
}
Further info at REST API Azure information.
I always thought that setting InstanceContextMode to PerCall makes concurrency mode irrelevant even if using a session aware binding like net.tcp. This is what MSDN says
http://msdn.microsoft.com/en-us/library/ms731193.aspx
"In PerCallinstancing, concurrency is not relevant, because each message is processed by a new InstanceContext and, therefore, never more than one thread is active in the InstanceContext."
But today I was going through Juval Lowy's book Programming WCF Services and he writes in Chapter 8
If the per-call service has a transport-level session, whether
concurrent processing of calls is allowed is a product of the service
concurrency mode. If the service is configured with
ConcurrencyMode.Single, concurrent processing of the pending
calls is not al lowed, and the calls are dispatched one at a time.
[...] I consider this to be a flawed design. If the service is
configured with ConcurrencyMode.Multiple, concurrent pro- cessing is
allowed. Calls are dispatched as they arrive, each to a new instance,
and execute concurrently. An interesting observation here is that in
the interest of through- put, it is a good idea to configure a
per-call service with ConcurrencyMode.Multiple— the instance itself
will still be thread-safe (so you will not incur the synchronization
liability), yet you will allow concurrent calls from the same client.
This is contradicting my understanding and what MSDN says. Which is correct ?
In my case I have a WCF Net.Tcp service used my many client applications that creates a new proxy object, makes the call and then immediately closes the proxy. The service has PerCall InstanceContextMode. Will I get improved throughput if I change the InstanceContextMode to Multiple with no worse thread safety behaviour than percall ?
The key phrase in reading Lowy’s statement is “in the interest of throughput”. Lowy is pointing out that when using ConcurrencyMode.Single WCF will blindly implement a lock to enforce serialization to the service instance. Locks are expensive and this one isn’t necessary because PerCall already guarantees that a second thread will never try to call the same service instance.
In terms of behavior:
ConcurrencyMode does not matter for a PerCall service instance.
In terms of performance:
A PerCall service that is ConcurrencyMode.Multiple should be slightly faster because its not creating and acquiring the (unneeded) thread lock that ConcurrencyMode.Single is using.
I wrote a quick benchmark program to see if I could measure the performance impact of Single vs Multiple for a PerCall service: The benchmark showed no meaningful difference.
I pasted in the code below if you want to try running it yourself.
Test cases I tried:
600 threads calling a service 500 times
200 threads calling a service 1000 times
8 threads calling a service 10000 times
1 thread calling a service 10000 times
I ran this on a 4 CPU VM running Service 2008 R2. All but the 1 thread case was CPU constrained.
Results:
All the runs were within about 5% of eachother.
Sometimes ConcurrencyMode.Multiple was faster. Sometimes ConcurrencyMode.Single was faster. Maybe a proper statistical analysis could pick a winner. In my opinion they are close enough to not matter.
Here’s a typical output:
Starting Single Service on net.pipe://localhost/base...
Type=SingleService ThreadCount=600 ThreadCallCount=500
runtime: 45156759 ticks 12615 msec
Starting Multiple Service on net.pipe://localhost/base...
Type=MultipleService ThreadCount=600 ThreadCallCount=500
runtime: 48731273 ticks 13613 msec
Starting Single Service on net.pipe://localhost/base...
Type=SingleService ThreadCount=600 ThreadCallCount=500
runtime: 48701509 ticks 13605 msec
Starting Multiple Service on net.pipe://localhost/base...
Type=MultipleService ThreadCount=600 ThreadCallCount=500
runtime: 48590336 ticks 13574 msec
Benchmark Code:
Usual caveat: This is benchmark code that takes short cuts that aren’t appropriate for production use.
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WCFTest
{
[ServiceContract]
public interface ISimple
{
[OperationContract()]
void Put();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class SingleService : ISimple
{
public void Put()
{
//Console.WriteLine("put got " + i);
return;
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MultipleService : ISimple
{
public void Put()
{
//Console.WriteLine("put got " + i);
return;
}
}
public class ThreadParms
{
public int ManagedThreadId { get; set; }
public ServiceEndpoint ServiceEndpoint { get; set; }
}
public class BenchmarkService
{
public readonly int ThreadCount;
public readonly int ThreadCallCount;
public readonly Type ServiceType;
int _completed = 0;
System.Diagnostics.Stopwatch _stopWatch;
EventWaitHandle _waitHandle;
bool _done;
public BenchmarkService(Type serviceType, int threadCount, int threadCallCount)
{
this.ServiceType = serviceType;
this.ThreadCount = threadCount;
this.ThreadCallCount = threadCallCount;
_done = false;
}
public void Run(string baseAddress)
{
if (_done)
throw new InvalidOperationException("Can't run twice");
ServiceHost host = new ServiceHost(ServiceType, new Uri(baseAddress));
host.Open();
Console.WriteLine("Starting " + ServiceType.Name + " on " + baseAddress + "...");
_waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
_completed = 0;
_stopWatch = System.Diagnostics.Stopwatch.StartNew();
ServiceEndpoint endpoint = host.Description.Endpoints.Find(typeof(ISimple));
for (int i = 1; i <= ThreadCount; i++)
{
// ServiceEndpoint is NOT thread safe. Make a copy for each thread.
ServiceEndpoint temp = new ServiceEndpoint(endpoint.Contract, endpoint.Binding, endpoint.Address);
ThreadPool.QueueUserWorkItem(new WaitCallback(CallServiceManyTimes),
new ThreadParms() { ManagedThreadId = i, ServiceEndpoint = temp });
}
_waitHandle.WaitOne();
host.Shutdown();
_done = true;
//Console.WriteLine("All DONE.");
Console.WriteLine(" Type=" + ServiceType.Name + " ThreadCount=" + ThreadCount + " ThreadCallCount=" + ThreadCallCount);
Console.WriteLine(" runtime: " + _stopWatch.ElapsedTicks + " ticks " + _stopWatch.ElapsedMilliseconds + " msec");
}
public void CallServiceManyTimes(object threadParams)
{
ThreadParms p = (ThreadParms)threadParams;
ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(p.ServiceEndpoint);
ISimple proxy = factory.CreateChannel();
for (int i = 1; i < ThreadCallCount; i++)
{
proxy.Put();
}
((ICommunicationObject)proxy).Shutdown();
factory.Shutdown();
int currentCompleted = Interlocked.Increment(ref _completed);
if (currentCompleted == ThreadCount)
{
_stopWatch.Stop();
_waitHandle.Set();
}
}
}
class Program
{
static void Main(string[] args)
{
BenchmarkService benchmark;
int threadCount = 600;
int threadCalls = 500;
string baseAddress = "net.pipe://localhost/base";
for (int i = 0; i <= 4; i++)
{
benchmark = new BenchmarkService(typeof(SingleService), threadCount, threadCalls);
benchmark.Run(baseAddress);
benchmark = new BenchmarkService(typeof(MultipleService), threadCount, threadCalls);
benchmark.Run(baseAddress);
}
baseAddress = "http://localhost/base";
for (int i = 0; i <= 4; i++)
{
benchmark = new BenchmarkService(typeof(SingleService), threadCount, threadCalls);
benchmark.Run(baseAddress);
benchmark = new BenchmarkService(typeof(MultipleService), threadCount, threadCalls);
benchmark.Run(baseAddress);
}
Console.WriteLine("Press ENTER to close.");
Console.ReadLine();
}
}
public static class Extensions
{
static public void Shutdown(this ICommunicationObject obj)
{
try
{
if (obj != null)
obj.Close();
}
catch (Exception ex)
{
Console.WriteLine("Shutdown exception: {0}", ex.Message);
obj.Abort();
}
}
}
}