Skip to content

Using Wolverine's Lightweight TCP Transport

Wolverine has a lightweight transport option built in that relies on batching messages through raw socket communication. At this point, this transport is absolutely robust enough for production usage (that's my story and I'm sticking to it), but does not yet have any facility for security. As such, it may be most useful for testing or development scenarios where the "real" message broker is not really usable in local environments. Either way, there's not much necessary to use the TCP transport:

TIP

You can listen to messages from as many ports as you like, but be aware of port contention issues.

To listen for messages with the TCP transport, use the ListenAtPort() extension method shown below:

cs
public static IHost CreateHostBuilder()
{
    var builder = Host.CreateApplicationBuilder();
    
    // This adds Wolverine with inline configuration
    // of WolverineOptions
    builder.UseWolverine(opts =>
    {
        // This is an example usage of the application's
        // IConfiguration inside of Wolverine bootstrapping
        var port = builder.Configuration.GetValue<int>("ListenerPort");
        opts.ListenAtPort(port);

        // If we're running in development mode and you don't
        // want to worry about having all the external messaging
        // dependencies up and running, stub them out
        if (builder.Environment.IsDevelopment())
        {
            // This will "stub" out all configured external endpoints
            opts.StubAllExternalTransports();
        }
    });

    return builder.Build();
}

snippet source | anchor

Likewise, to publish via TCP, use the ToPort() extension method to publish to another port on the same machine:

cs
using var host = await Host.CreateDefaultBuilder()
    .UseWolverine(opts =>
    {
        opts.PublishAllMessages().ToPort(5555)
            .Named("One");

        opts.PublishAllMessages().ToPort(5555)
            .Named("Two");
    }).StartAsync();

var bus = host.Services
    .GetRequiredService<IMessageBus>();

// Explicitly send a message to a named endpoint
await bus.EndpointFor("One").SendAsync(new SomeMessage());

// Or invoke remotely
await bus.EndpointFor("One").InvokeAsync(new SomeMessage());

// Or request/reply
var answer = bus.EndpointFor("One")
    .InvokeAsync<Answer>(new Question());

snippet source | anchor

or use ToServerAndPort() to send messages to a port on another machine:

cs
using var host = Host.CreateDefaultBuilder()
    .UseWolverine(opts =>
    {
        // Route a single message type
        opts.PublishMessage<PingMessage>()
            .ToServerAndPort("server", 1111);

        // Send every possible message to a TCP listener
        // on this box at port 2222
        opts.PublishAllMessages().ToPort(2222);

        // Or use a more fluent interface style
        opts.Publish().MessagesFromAssembly(typeof(PingMessage).Assembly)
            .ToPort(3333);

        // Complicated rules, I don't think folks will use this much
        opts.Publish(rule =>
        {
            // Apply as many message matching
            // rules as you need

            // Specific message types
            rule.Message<PingMessage>();
            rule.Message<Message1>();

            // Implementing a specific marker interface or common base class
            rule.MessagesImplementing<IEventMarker>();

            // All types in a certain assembly
            rule.MessagesFromAssemblyContaining<PingMessage>();

            // or this
            rule.MessagesFromAssembly(typeof(PingMessage).Assembly);

            // or by namespace
            rule.MessagesFromNamespace("MyMessageLibrary");
            rule.MessagesFromNamespaceContaining<PingMessage>();

            // Express the subscribers
            rule.ToPort(1111);
            rule.ToPort(2222);
        });

        // Or you just send all messages to a certain endpoint
        opts.PublishAllMessages().ToPort(3333);
    }).StartAsync();

snippet source | anchor

Released under the MIT License.