Home

Awesome

Halibut is the communication framework behind Octopus Deploy 3.0.

Overview

Like WCF and other RPC-based communication frameworks, Halibut uses a simple request/response based programming model. However, unlike other request/response frameworks, the transport layer can be configured to allow either party to be a TCP listener or TCP client.

Halibut

To understand the difference, consider WCF using wsHttpBinding. The WCF "client" is always a TCP client, and the WCF "service" is always a TCP listener. For a client to send a request to a server, TCP ports must be opened on the server. This is not always possible.

In Halibut, the relationship between the logical request/response client/service, and the underlying TCP client/listener, is decoupled. The Halibut client might in fact be a TCP listener, while the Halibut service is a TCP client, polling the TCP listener for requests to process.

For Octopus, this means that customers can configure the Tentacle (which hosts services that the Octopus client connects to) in either listening or polling mode.

Halibut has the following features:

A more detailed look at the protocol can be found under the Halibit Protocol page.

Usage

Clients and servers both make use of HalibutRuntime to distribute messages. In this example, there's a "Tentacle" that listens on a port, and Octopus connects to it:

using (var octopus = new HalibutRuntime(services, Certificates.Bob))
using (var tentacleListening = new HalibutRuntime(services, Certificates.Alice))
{
    tentacleListening.Listen(8014);
    tentacleListening.Trust(Certificates.BobPublicThumbprint);

    var calculator = octopus.CreateClient<ICalculatorService>(new ServiceEndPoint(new Uri("https://localhost:8014"), Certificates.AlicePublicThumbprint));
    var three = calculator.Add(1, 2);
    Assert.That(three, Is.EqualTo(3));
}

Alternatively, here's a mode where Octopus listens, and Tentacle polls it:

using (var octopus = new HalibutRuntime(services, Certificates.Bob))
using (var tentaclePolling = new HalibutRuntime(services, Certificates.Alice))
{
    octopus.Listen(8013);
    tentaclePolling.Poll(new Uri("poll://subscription123"), new ServiceEndPoint(new Uri("https://localhost:8013"), Certificates.BobPublicThumbprint));

    var calculator = octopus.CreateClient<ICalculatorService>(new ServiceEndPoint(new Uri("poll://subscription123"), Certificates.AlicePublicThumbprint));
    var three = calculator.Add(1, 2);
    Assert.That(three, Is.EqualTo(3));
}

Notice that while the configuration code changed, the request/response code didn't apart from the endpoint. Logically, the Octopus is still the request/response client, and the Tentacle is still the request/response server, even though the transport layer has Octopus as the TCP listener and Tentacle as the TCP client polling for work.

Failure modes

One area we've put a lot of thought into with Halibut is failure modes. Below is a list of possible failure reasons, and how Halibut will handle them.