I want to do black-box testing of a messaging client library that uses the WindowsAzure.ServiceBus nuget package nuget package (docs) to send and receive messages to/from Microsoft ServiceBus queues.
My goal is to create integration tests for the messaging client library, but not communicate with an actual Microsoft Service Bus server.
Looking at the amqplite library, I was hoping that I could create an AMQP host using amqplite, and configure the messaging client library to communicate with this host, instead of an actual Microsoft Service Bus.
Here is my custom AMQP host, using amqplite:
public class LinkProcessor : ILinkProcessor
{
public void Process(AttachContext attachContext)
{
Console.WriteLine("LinkProcessor.Attach!");
}
}
var containerHost = new ContainerHost(new Uri("amqp://localhost:9876/"));
containerHost.RegisterLinkProcessor(new LinkProcessor());
containerHost.Open();
I register a ILinkProcessor on the containerHost so I can see on the console any connection attempts (based on my limited understanding of AMQP/AMQPlite this is what I believe a ILinkProcessor does).
The AMQP client below can connect, the server process outputs "LinkProcessor.Attach!" on the console.
async Task Main()
{
await SendMessage("q2", "hello world");
}
async Task SendMessage(string queue, string message)
{
var connection = await Connection.Factory.CreateAsync(new Address("amqp://localhost:9876/"));
var session = new Session(connection);
var msg = new Message(message);
var sender = new SenderLink(session, "sender-link", queue);
await sender.SendAsync(msg);
await sender.CloseAsync();
await session.CloseAsync();
await connection.CloseAsync();
}
Back to the messaging client library that I want to test:
The library is configured using a connection string, a working connection string looks like this (I assume the connection string format is one defined by WindowsAzure.ServiceBus):
Endpoint=sb://xxx/yyy;StsEndpoint=https://nnn:NNNN/xxx;RuntimePort=NNNN;ManagementPort=NNNN;OAuthUsername=xxx;OAuthPassword=yyy
I changed the config string to the following:
Endpoint=amqp://localhost:9876/q2
When I run the test client, the following happens:
The client throws an exception: You have tried to create a channel to a service that does not support .Net Framing.
I see no activity on the AMQPlite host end, i.e. no "LinkProcessor.Attach!" message on the console.
Question:
Is what I'm trying to accomplish possible? I.e. is there a way to configure a AMQPlite host so that it can accept connections from a WindowsAzure.ServiceBus client ?
Related
I am trying to make request to my server by MagicOnion protocol (it uses transport from gRPC, but deffrent serialization protocol, message pack instead of protobuf).
An simple test client app, working under net5.0 is executing code like this:
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
var address = $"http://{ServerUrl.Host}:5002";
using var channel = GrpcChannel.ForAddress(address);
var myServiceClient = MagicOnionClient.Create<IMyService>(channel);
var result = await myServiceClient.GetMyData();
...and recieves response succesfully. But if I try to execute the same code on Android app, I am seeing this exception message on server logs:
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2ConnectionErrorException: HTTP/2 connection error (PROTOCOL_ERROR): Invalid HTTP/2 connection preface.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.ParsePreface(ReadOnlySequence`1& buffer, SequencePosition& consumed, SequencePosition& examined)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.TryReadPrefaceAsync()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2Connection.ProcessRequestsAsync[TContext](IHttpApplication`1 application)
With logs and traffic dump I can see that the client on .Net 5 uses HTTP/2, but on Android - HTTP/1.1. As I can see, this is the only deifference between requests.
So, how can I make Android (API30, monoandroid10.0) client use HTTP/2?
The resolution is to use another gRPCC implementation lib - Grpc.Core. It provides GrpcChannel class wich is compatible with MagicOnion.
In my case, the library didn't work immediately, throwing the error about libgrpc_csharp_ext loading trouble. To solve this, you also have to add pacakge Grpc.Core.Xamarin.
The usage example:
var channel = new Grpc.Core.Channel(ServerUrl.Host, 5002, Grpc.Core.ChannelCredentials.Insecure);
var myServiceClient = MagicOnionClient.Create<IMyService>(channel);
var result = await myServiceClient.GetMyData();
I have read an example about subscribing and publishing in StackExchange.Redis documents but I don't understand it.
Why the example doesn't talk about publishing?
var channel = multiplexer.GetSubscriber().Subscribe("messages");
channel.OnMessage(message =>
{
Console.WriteLine((string)message.Message);
});
How to implement subscribe and publish in Dotnet Core project?
What is the RedisChannel, Anyone can explain it?
I Do like this:
RedisChannel channelWithLiteral = new RedisChannel("messages", RedisChannel.PatternMode.Literal);
And
RedisChannel channelWithLiteral = new RedisChannel("messages", RedisChannel.PatternMode.Literal);
var sub = connectionPoolManager.GetConnection().GetSubscriber();
sub.PublishAsync(channelWithLiteral , Serializer.Serialize(message));
both Sub and Pub project. Is this correct?
After adding Redis package to your project (via StackExchange.Redis NuGet package), you can connect to your Redis server (in this case, local):
using StackExchange.Redis;
ConnectionMultiplexer connection = ConnectionMultiplexer.Connect("localhost");
Next, you should subscribe to a channel using the ISubscriber.Subscribe:
var subscriber = connection.GetSubscriber();
Publishing messages to any channel can be done with:
subscriber.Publish("channle-name", "This is a message");
And finally, subscribing to the same channel (from another client maybe) to receive messages sent over your desired channel:
subscriber.Subscribe("channle-name", (channel, message) => {
Console.WriteLine((string)message);
});
I'm using a very basic example for testing my MassTransit connection to rabbitMq through C#. Whenever I run this code to connect to my rabbitMq endpoint, it works fine whenever I have a wildcard set as permission. However, when I add the permissions in the rabbitMq admin to only allow this user to access the test event, this code will fail.
For some reason it will first try to connect to an exchange name that I guess is generated (by MassTransit?):
RabbitMQ.Client.Exceptions.OperationInterruptedException: 'The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=403, text="ACCESS_REFUSED - access to exchange '*ComputerName*_dotnet_bus_73451gfsgerqwrefxfddgf' in vhost '/' refused for user 'user'", classId=40, methodId=10, cause='
So the exchange *ComputerName*_dotnet_bus_73451gfsgerqwrefxfddgf, after that it will try to connect to the test exchange. Of course I can add the ComputerName.... exchange to the permissions but then this would need to be done for each computer trying to run this code. Why is MassTransit trying to connect to this exchange? Is the code incorrect or is this just how MassTransit works?
This is the code for the test application (I altered this a bit so it might not run right of the bat, but, in general the code runs fine):
using System;
using MassTransit;
namespace Test
{
public class Testing
{
public string Id { get; set; }
}
}
namespace Consumer
{
class Program
{
static void Main(string[] args)
{
var bus = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri("rabbitmq://servername"), h =>
{
h.Username("user");
h.Password("user");
});
sbc.ReceiveEndpoint(host, "test", ep =>
{
ep.Handler<Testing>(context =>
{
return Console.Out.WriteLineAsync($"Received: {context.Message.Id}");
});
});
});
bus.Start();
// For testing purposes, we send a message ourselves.
bus.Publish(new Testing { Id = "X" });
Console.WriteLine("Waiting for messages. Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
Image of the rabbitMq admin user permission:
Is the code incorrect or is this just how MassTransit works?
More than likely this is just how MassTransit works. See this other question: Prevent MassTransit from creating a RabbitMQ exchange for a consumer host
Strangely enough I could not find this information anywhere in the MassTransit docs.
You'll have to grant the configure permission for exchanges containing the string dotnet_bus. More than likely other permissions like read and write will be required.
NOTE: the RabbitMQ team monitors the rabbitmq-users mailing list and only sometimes answers questions on StackOverflow.
I have created a very simple console application that connects to Azure ServiceBus and sends one message. I tried the latest library from Microsoft (Microsoft.Azure.ServiceBus) but no matter what I do I just get this error:
No connection could be made because the target machine actively
refused it ErrorCode: ConnectionRefused
I have tried exactly the same connection string in Service Bus Explorer and it does work just fine. Moreover I connected without problems using the older library from Microsoft (WindowsAzure.ServiceBus).
var sender = new MessageSender("endpoint", "topicName");
sender.SendAsync(new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject("test"))));
I tried with .NET Framework 4.6.2 and Core, same exception. I suspect there may be some differences in the default protocol that these libraries use, but I could not figure out that for sure.
P.S. Have tried the example from Microsoft docs but result is still the same exception
The old client supported ConnectivityMode using TCP, HTTP, HTTPS, and AutoDetect. ServiceBus Explorer is using AutoDetect, trying TCP first and then failing over to HTTPS, regardless of the TransportMode you were using (SBMP or AMQP).
With the new client this has changed. TransportMode now combines both options and offers Amqp (AMQP over TCP) or AmqpWebSockets (AMQP over WebSockets). There's no AutoDetect mode. You will have to create your clients and specify TransportType as AmqpWebSockets to bypass blocked TCP port 5671 and instead use port 443.
It seems that the documentation is lacking a lot on how to connect using HTTPS (Amqp over WebSockets) but after some help from Sean Feldman in the accepted answer I managed to connect. Here is the code that I used if someone is interested:
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(
"RootManageSharedAccessKey", // SharedAccessKeyName
"SomeToken");
var sender = new MessageSender(
"sb://mydomain.servicebus.windows.net/",
"topicName",
tokenProvider,
TransportType.AmqpWebSockets);
Or a variant that let's you have the whole connection string in one piece
var builder = new ServiceBusConnectionStringBuilder("YouConnectionString");
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(
builder.SasKeyName,
builder.SasKey);
var sender = new MessageSender(
builder.Endpoint,
"TopicName",
tokenProvider,
TransportType.AmqpWebSockets);
It is actually possible to use ConnectionString directly but then it has to be augmented to use the right protocol.
var sender = new MessageSender("TransportType=AmqpWebSockets;Endpoint=...", "TopicName")
Or the version that allows to embed EntityPath into the ConnectionString
var connectionBuilder = new ServiceBusConnectionStringBuilder("EntityPath=MyTopic;TransportType=AmqpWebSockets;Endpoint=...")
var sender = new MessageSender(connectionBuilder);
I was having the same issue but his worked for me
var clientOptions = new ServiceBusClientOptions();
clientOptions.TransportType = ServiceBusTransportType.AmqpWebSockets;
client = new ServiceBusClient(connectionString, clientOptions);
sender = client.CreateSender(topicName);
// create a batch
using ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync();
I'm creating a client application with the idea of publish new messages to a remote RabbitMQ queue. I'm using MassTransit to create this client, and my code looks this way:
static IBusControl CreateBus()
{
return Bus.Factory.CreateUsingRabbitMq(x =>
{
var host = x.Host(new Uri(ConfigurationManager.AppSettings["RabbitMQHost"]), h =>
{
h.Username("user");
h.Password("password");
});
});
}
static IRequestClient<ISyncProject, IProjectSynced> CreateRequestClient(IBusControl busControl)
{
var serviceAddress = new Uri(ConfigurationManager.AppSettings["ServiceAddress"]);
IRequestClient<ISyncProject, IProjectSynced> client =
busControl.CreateRequestClient<ISyncProject, IProjectSynced>(serviceAddress, TimeSpan.FromDays(1));
return client;
}
private static async Task MainLogic(IBusControl busControl)
{
IRequestClient<ISyncProject, IProjectSynced> client = CreateRequestClient(busControl);
//I'm using the client here as I show below, this part is not important it works with localhost
IProjectSynced response = await client.Request(new ProjecToSync() { OriginalOOMID = OriginalOOMID });
}
And the config file looks like this:
<appSettings>
<add key="RabbitMQHost" value="rabbitmq://ServerName" />
<add key="ServiceQueueName" value="queueName" />
<add key="ServiceAddress" value="rabbitmq://ServerName/queueName" />
</appSettings>
I'm not using guest user, I created a new one and I added all the rights as administrator.
Now this code works if I run the client application in the same server where is running RabbitMQ and also changing ServerName by localhost. If I run the client in my local machine using whatever ServerName or IP address of server, RabbitMQ is blocking my connection:
I presume this is has to be with some configuration that I need to do in the server but I have not found it so far.
One thing I noticed now is disk space is in red and and a big amount of generic exchanges have been created
As your question shows, down at the bottom you have a connection, but it is blocked.
The RabbitMQ documentation lists some conditions where a connection is blocked. These generally have to do with resource limitations on the broker machine itself. In this case, we've managed to get a clear picture that the free disk space available to the broker is below its low-water mark. Thus, all connections will be blocked until this condition is resolved (either lower the mark - not recommended, or increase the available free space).