This closes #2154
This commit is contained in:
commit
faf99cd68f
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Amqp.Framing;
|
||||||
|
using Amqp;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.Perf
|
||||||
|
{
|
||||||
|
class App
|
||||||
|
{
|
||||||
|
static long ReceivedMessages = 0;
|
||||||
|
|
||||||
|
static void TheCallback(int id, Session session, ReceiverLink link, Message message)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref ReceivedMessages);
|
||||||
|
link.Accept(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args) {
|
||||||
|
|
||||||
|
if (args.Length == 0) {
|
||||||
|
args = new string[1];
|
||||||
|
args[0] = "amqp://127.0.0.1:5672";
|
||||||
|
}
|
||||||
|
|
||||||
|
// it will start one client towards each server
|
||||||
|
for (int i = 0; i < args.Length; i++) {
|
||||||
|
string addr0 = args.Length >= 1 ? args[0] : "amqp://127.0.0.1:5672";
|
||||||
|
processOn(addr0, "orders", 100000000, 25000, "p1");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
long previousRead = Interlocked.Read(ref ReceivedMessages);
|
||||||
|
long previousSent = Interlocked.Read(ref Producer.totalSent);
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
long currentRead = Interlocked.Read(ref ReceivedMessages);
|
||||||
|
long currentSent = Interlocked.Read(ref Producer.totalSent);
|
||||||
|
Console.WriteLine("Received: " + currentRead + " TotalSent: " +
|
||||||
|
currentSent);
|
||||||
|
Console.WriteLine("Rate reading: " + (currentRead - previousRead) + ", Rate sending: " + (currentSent - previousSent));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
static void processOn(string addr, string queue, int totalSend, int maxRateSend, String processName) {
|
||||||
|
Address address = new Address(addr);
|
||||||
|
|
||||||
|
Connection connection = new Connection(address);
|
||||||
|
|
||||||
|
ReceiverPool pool = new ReceiverPool(connection, 1, queue, 200, TheCallback);
|
||||||
|
pool.start();
|
||||||
|
|
||||||
|
Producer Producer = new Producer(processName, addr, queue, totalSend, maxRateSend);
|
||||||
|
Producer.produce(); // this will start an asynchronous producer
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AMQPNetLite" Version="2.1.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Amqp.Framing;
|
||||||
|
using Amqp;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.Perf
|
||||||
|
{
|
||||||
|
public class Producer
|
||||||
|
{
|
||||||
|
string name;
|
||||||
|
|
||||||
|
string addr;
|
||||||
|
string queue;
|
||||||
|
int numberOfMessages;
|
||||||
|
int messagesPerSecond;
|
||||||
|
long messagesSent;
|
||||||
|
|
||||||
|
public static long totalSent;
|
||||||
|
|
||||||
|
public Producer(string name, string addr, string queue, int numberOfMessages, int messagesPerSecond) {
|
||||||
|
this.name = name;
|
||||||
|
this.addr = addr;
|
||||||
|
this.queue = queue;
|
||||||
|
this.numberOfMessages = numberOfMessages;
|
||||||
|
this.messagesPerSecond = messagesPerSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void produce() {
|
||||||
|
Address address = new Address(addr);
|
||||||
|
|
||||||
|
Connection connection = new Connection(address);
|
||||||
|
|
||||||
|
|
||||||
|
Session session = new Session(connection);
|
||||||
|
SenderLink sender = new SenderLink(session, "sender", queue);
|
||||||
|
|
||||||
|
OutcomeCallback callback = (l, msg, o, s) => {
|
||||||
|
Interlocked.Increment(ref messagesSent);
|
||||||
|
Interlocked.Increment(ref totalSent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is just to limit the number of messages per second we are sending
|
||||||
|
TokenBucketLimiterImpl tokens = new TokenBucketLimiterImpl(messagesPerSecond);
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => {
|
||||||
|
Console.WriteLine("Sending {0} messages...", numberOfMessages);
|
||||||
|
for (var i = 0; i < numberOfMessages; i++)
|
||||||
|
{
|
||||||
|
tokens.limit();
|
||||||
|
Message message = new Message("a message!" + i);
|
||||||
|
message.Header = new Header();
|
||||||
|
message.Header.Durable = true;
|
||||||
|
|
||||||
|
// The callback here is to make the sending to happen as fast as possible
|
||||||
|
sender.Send(message, callback, null);
|
||||||
|
}
|
||||||
|
Console.WriteLine(".... Done sending");
|
||||||
|
}, TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
|
// Trace.TraceLevel = TraceLevel.Verbose | TraceLevel.Error |
|
||||||
|
// TraceLevel.Frame | TraceLevel.Information | TraceLevel.Warning;
|
||||||
|
// Trace.TraceListener = (l, f, o) => Console.WriteLine(DateTime.Now.ToString("[hh:mm:ss.fff]") + " " + string.Format(f, o));
|
||||||
|
|
||||||
|
// sender.Close();
|
||||||
|
|
||||||
|
// Task.Factory.StartNew(() => {
|
||||||
|
// while (true) {
|
||||||
|
// Console.WriteLine("Sent " + Interlocked.Read(ref messagesSent) + " on queue " + queue + " producer " + this.name);
|
||||||
|
// Thread.Sleep(1000);
|
||||||
|
// }
|
||||||
|
// }, TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Amqp.Framing;
|
||||||
|
using Amqp;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.Perf
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class will start many consumers underneath it to satisfy a pool of consumers
|
||||||
|
* While calling a single callback for when messages are received.
|
||||||
|
*/
|
||||||
|
public class ReceiverPool
|
||||||
|
{
|
||||||
|
public delegate void MessageReceived(int id, Session session, ReceiverLink link, Message msg);
|
||||||
|
|
||||||
|
public int MessagesReceived;
|
||||||
|
|
||||||
|
MessageReceived _callback;
|
||||||
|
int _Workers;
|
||||||
|
private Object receiverLock = new Object();
|
||||||
|
private Boolean running = true;
|
||||||
|
|
||||||
|
private ReceiverLink[] Receivers;
|
||||||
|
private Session[] Sessions;
|
||||||
|
|
||||||
|
private Connection _Connection;
|
||||||
|
|
||||||
|
private int Credits;
|
||||||
|
|
||||||
|
public ReceiverPool(Connection Connection, int Workers, String queue, int Credits, MessageReceived callback)
|
||||||
|
{
|
||||||
|
this._Connection = Connection;
|
||||||
|
this.Receivers = new ReceiverLink[Workers];
|
||||||
|
this.Sessions = new Session[Workers];
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < Workers; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
// I was playing with using a single session versus multiple sessions
|
||||||
|
if (i == 0) {
|
||||||
|
Sessions[i] = new Session(Connection);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Sessions[i] = Sessions[0];
|
||||||
|
}
|
||||||
|
Receivers[i] = new ReceiverLink(Sessions[i], "receiver " + queue + " " + i, queue);
|
||||||
|
}
|
||||||
|
this._Workers = Workers;
|
||||||
|
this._callback = callback;
|
||||||
|
this.Credits = Credits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
for (int i = 0; i < _Workers; i++) {
|
||||||
|
Receivers[i].Close();
|
||||||
|
Sessions[i].Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
for (int i = 0; i < _Workers; i++) {
|
||||||
|
{
|
||||||
|
// This variable exists otherwise we would get an olderValue of i
|
||||||
|
int value = i;
|
||||||
|
Task.Factory.StartNew(() => WorkerRun(value), TaskCreationOptions.LongRunning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkerRun(int i) {
|
||||||
|
try {
|
||||||
|
Receivers[i].SetCredit(Credits);
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
Message theMessage = Receivers[i].Receive(TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
|
if (theMessage != null)
|
||||||
|
{
|
||||||
|
_callback(i, Sessions[i], Receivers[i], theMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Amqp.Framing;
|
||||||
|
using Amqp;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Artemis.Perf
|
||||||
|
{
|
||||||
|
|
||||||
|
// this has been copied from Artemis' TokenBucketLimiter with some modifications
|
||||||
|
public class TokenBucketLimiterImpl {
|
||||||
|
|
||||||
|
private int rate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Even thought we don't use TokenBucket in multiThread
|
||||||
|
* the implementation should keep this volatile for correctness
|
||||||
|
*/
|
||||||
|
private long last;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Even thought we don't use TokenBucket in multiThread
|
||||||
|
* the implementation should keep this volatile for correctness
|
||||||
|
*/
|
||||||
|
private int tokens;
|
||||||
|
|
||||||
|
public TokenBucketLimiterImpl(int rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
}
|
||||||
|
private bool checkRate() {
|
||||||
|
|
||||||
|
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
|
||||||
|
if (last == 0) {
|
||||||
|
last = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
long diff = now - last;
|
||||||
|
|
||||||
|
if (diff >= 1000) {
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
tokens = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens > 0) {
|
||||||
|
tokens--;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void limit() {
|
||||||
|
if (!checkRate()) {
|
||||||
|
// Console.WriteLine("Limiting messages per max rate");
|
||||||
|
do {
|
||||||
|
Thread.Sleep(1);
|
||||||
|
} while (!checkRate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Running the .NET AMQP example
|
||||||
|
|
||||||
|
|
||||||
|
# Pre-requisites:
|
||||||
|
|
||||||
|
All of this can be done on Linux, Mac and... Windows
|
||||||
|
|
||||||
|
- Install .NET
|
||||||
|
|
||||||
|
https://www.microsoft.com/net/core
|
||||||
|
|
||||||
|
|
||||||
|
- Visual Studio Code is free and may be useful:
|
||||||
|
|
||||||
|
https://code.visualstudio.com
|
||||||
|
|
||||||
|
|
||||||
|
- Powershell might be also useful:
|
||||||
|
|
||||||
|
https://github.com/PowerShell/PowerShell/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# running the example
|
||||||
|
|
||||||
|
- Create and start the broker, by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./start-server.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This broker is created by simply using the CLI. you may do it manually if you like:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
artemis create ./server1 --user a --password a --role a --allow-anonymous --force
|
||||||
|
cd server1/bin
|
||||||
|
./artemis run
|
||||||
|
```
|
||||||
|
|
||||||
|
- Compile the code
|
||||||
|
|
||||||
|
You need call restore to download AMQP Library and build it.
|
||||||
|
Restore is part of NuGET which is sort of the Maven Repo for Java devs.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
dotnet restore
|
||||||
|
dotnet build
|
||||||
|
dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
Or simply use the run-example.sh script on this directory
|
||||||
|
|
||||||
|
- Debugging
|
||||||
|
|
||||||
|
Visual Studio Code will make it fairly easy to do it
|
||||||
|
|
||||||
|
|
||||||
|
# About this example
|
||||||
|
|
||||||
|
This is sending messages, limited to 25K messages a second.
|
||||||
|
The consumer will have a pool of consumers, which will synchronously acknowledge messages.
|
||||||
|
.NET threading model is expensive, this example shows how to make most of your resources by a pool of consumers.
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
# Setting the script to fail if anything goes wrong
|
||||||
|
set -e
|
||||||
|
|
||||||
|
rm -rf ./server1
|
||||||
|
../../../../../bin/artemis create ./server1 --user a --password a --role a --allow-anonymous --force
|
||||||
|
./target/server0/bin/artemis run
|
||||||
|
#sleep 1
|
||||||
|
#../../../../target/clustered-static-node1/bin/artemis run | tee server2.log &
|
||||||
|
#sleep 1
|
||||||
|
#../../../../target/clustered-static-node2/bin/artemis run | tee server3.log &
|
||||||
|
#sleep 1
|
Loading…
Reference in New Issue