<dependency>
<groupId>org.wildfly.swarm</groupId>
<artifactId>ribbon</artifactId>
</dependency>
NetflixOSS
Introduction
Netflix, the online movie streaming company, has invested heavily in microservices. To support their architectures, they have created many projects that have been integrated into WildFly Swarm.
Ribbon
Ribbon provides a method for registering services by name, and allowing clients to invoke those services. One of the primary facilities of Ribbon is client-side load-balancing. With Ribbon, each client discovers possibly many instances of a given service, and uses a strategy (such as round-robin) to balance requests to the service across the many providers.
Additionally, Ribbon provides a method for wrapping an HTTP/REST invocation in an easy-to-use Java interface.
Ribbon makes heavy use of both RxJava and Netty.
Hystrix
Part of the Netflix strategy for using microservices involves being able to satisfy (for some value of "satisfy") requests even if remote services are unavailable. Sometimes called a circuit-breaker, this pattern allows for providing locally-derived fallback responses when the bonafide service fails to respond.
The Hystrix components work with Ribbon to provide this circuit-breaker functionality.
For instance, in the case of Netflix’s own consumer product, the "recommendations" for a given movie or television show may be provided by a specific service. If that service is unavailable, the UI can still provide some default list of recommendations. Everybody loves The Big Lebowksi, so it could be returned by the Hystrix component if the intelligent recommendation service is unavailable.
RxJava
RxJava provides a framework for working with asynchronous & reactive components. If one service needs to invoke an additional 3 services, RxJava provides the way to fire off the three additional requests and perform work as each completes (or once all have completed).
Netty
Netty is an asynchronous I/O framework maintained by Trustin Lee of Twitter (formerly of Red Hat). Ribbon uses Netty underneath the covers to perform its network actions. The primary way that Netty is exposed to users of Ribbon is through the ByteBuf
interface, which provides interaction with bytes sent or received from an I/O stream.
Configuration
To use the Netflix OSS stack within your WildFly Swarm application, you need to add the following dependency:
The necessary components for RxJava, Hystrix and Netty will be transitively made available to your code.
Usage
In order for your service to participate in the Ribbon service discovery (as a service to be discovered, a client performing discovery, or both), your deployment should be converted to a RibbonArchive
at some point.
In the event your deployment has a service you wish to advertise, you subsequently need to setServiceName(name)
on the resulting RibbonArchive
.
A client not advertising a service
An example of a client that ultimately might discover and use other services but does not advertise itself for external usage:
JAXRSArchive deployment = ShrinkWrap.create(JAXRSArchive.class);
deployment.as(RibbonArchive.class);
container.deploy( deployment );
A service advertising itself
There are a couple of ways to advertise a service’s name. By default the advertise()
method on RibbonArchive
will advertise the simplified name of the archive without the extension.
JAXRSArchive deployment = ShrinkWrap.create(JAXRSArchive.class, "recommendations.war");
deployment.as(RibbonArchive.class)
.advertise();
container.deploy( deployment );
If you haven’t named the archive, or wish to advetise it with a distinct service-name, there is a version of advertise(…)
which can take the service name as an argument.
JAXRSArchive deployment = ShrinkWrap.create(JAXRSArchive.class);
deployment.as(RibbonArchive.class)
.advertise( "recommendations" );
container.deploy( deployment );
Note
|
The Ribbon discovery mechanism uses the traditional WildFly Clustering capabilities, which are based upon JGroups. By default, JGroups uses multicast to discover other peers. In some environments, multicast is not supported. Different configuration of the clustering fraction will be required. |
Ribbon Webapp
WildFly Swarm also brings Ribbon services to the browser through the ribbon-webapp
fraction. The fraction enables Javascript clients to maintain an asynchronously updated list of ribbon-based services and their respective URLs, as well as a simple API for communicating with those services.
To include the ribbon-webapp fraction in your application, you’ll need to add an additional dependency.
<dependency>
<groupId>org.wildfly.swarm</groupId>
<artifactId>ribbon-webapp</artifactId>
</dependency>
This will add an implicit deployment to your application’s container which provides a web resource at /ribbon/ribbon.js
, and a servlet for retrieving the service topology. The default context for the javascript URL can be changed by setting the swarm.ribbon.context.path
system property in your application. For example,
// Makes ribbon.js available at /tape/ribbon.js
System.setProperty("swarm.ribbon.context.path", '/tape');
Javascript API
The Javascript API provided by ribbon-webapp
allows clients to open a persistent connection to the provided servlet. The known service topology is fed to clients as JSON data, and updated automatically via Server Sent Events as services come up and go down. Clients can register listeners to respond to these change events. When you include the ribbon.js script in your client application, a ribbon
constructor function is added to the global scope. Example usage:
var Ribbon = ribbon(),
component = someReactComponent;
// listen for changes to the service topology
// and update our component on change
Ribbon.onTopologyChange(function(topology) {
component.setState({data: JSON.parse(topology)});
});
The JSON received from this request will look similar to this.
{
"time": ["http://192.168.0.5:9000", "http://192.168.0.5:9001"],
"events": ["http://192.168.0.6:9000", "http://192.168.0.6:9001"]
}
That is, the client will receive a JSON object that has service names as keys, and a list of available servers as values. To call these services, however, clients do not need to know the host names and ports. Ribbon Webapp manages these for you. You simply need to know the service names. The Javascript API makes 3 asynchronous functions available.
-
getJSON
Makes an asynchronous HTTP GET request to a known service. -
postJSON
Makes an asynchronous HTTP POST request to a known service. -
ajax
Makes an AJAX request to a known service. This function allows for customizable AJAX settings.
Each of these functions returns a promise. Here is some example usage.
// Call the time service
// activate a browser alert on response
Ribbon.getJSON("time").then(alert);
// Post to the events service a new event
// Activate a browser alert on response
Ribbon.postJSON("events", {name: 'my-event-name'}).then(alert);
// Call a remote event service and provide a custom header
// alert on response
Ribbon.ajax( "events", '/', {
method: 'POST',
data: {
name: 'event-name',
value: 'event-value'
},
headers: {
Pragma: 'no-cache'
}
})
.then(alert);
Secured Ribbon
If your application is using Keycloak to secure your services, and you are using Ribbon to invoke those services, you’ll want to use the secured variant of Ribbon. This requires a slightly different dependency:
<dependency>
<groupId>org.wildfly.swarm</groupId>
<artifactId>ribbon-secured</artifactId>
</dependency>
In addition to your normal usage of .as(RibbonArchive)
and .as(Secured)
, instead of using the Netflix-provided Ribbon
class, you should use the WildFly Swarm-provided SecuredRibbon
class to construct your clients.
RecommendationService recommendations = SecuredRibbon.from(RecommendationService.class);
By using the SecuredRibbon
factory, any Keycloak security token will propagate across invocations of the services using an HTTP Authorization
header and a bearer token.
Ribbon Webapp can be used on the client side to secure calls to ribbon services as well. Just include the keycloak.js
Javascript that is provided with the Keycloak server in your HTML file and call the ribbon
constructor function with a keycloak object.
<script src="/ribbon/ribbon.js"></script>
<script src="http://keycloak-server:9191/auth/js/keycloak.js"></script>
<script>
var keycloak = new Keycloak( '/keycloak.json' );
var Ribbon = ribbon( {keycloak: keycloak} );
</script>
Properties
The following properties control ribbon options:
Name | Description | Default |
---|---|---|
swarm.ribbon.context.path |
Can be used to override the default |
/ribbon |