• Stars
    star
    122
  • Rank 290,316 (Top 6 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created about 8 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Riposte is a Netty-based microservice framework for rapid development of production-ready HTTP APIs.

Riposte

Maven Central Build Code Coverage License

Riposte is a Netty-based microservice framework for rapid development of production-ready HTTP APIs. It includes robust features baked in like distributed tracing (provided by the Zipkin-compatible Wingtips), error handling and validation (pluggable implementation with the default provided by Backstopper), and circuit breaking (provided by Fastbreak). It works equally well as a fully-featured microservice by itself (see the template microservice project), or as an embedded HTTP server inside another application.

Quickstart

Java 8 is required.

Please see the template microservice project for the recommended starter template project AND usage documentation. The template project is a production-ready microservice with a number of bells and whistles and the template project's README.md contains in-depth usage information and should be consulted first when learning how to use Riposte.

That said, the following class is a simple Java application containing a fully-functioning Riposte server. It represents the minimal code necessary to run a Riposte server. You can hit this server by calling http://localhost:8080/hello and it will respond with a text/plain payload of Hello, world!.

public class MyAppMain {

    public static void main(String[] args) throws Exception {
        Server server = new Server(new AppServerConfig());
        server.startup();
    }

    public static class AppServerConfig implements ServerConfig {
        private final Collection<Endpoint<?>> endpoints = Collections.singleton(new HelloWorldEndpoint());

        @Override
        public Collection<Endpoint<?>> appEndpoints() {
            return endpoints;
        }
    }

    public static class HelloWorldEndpoint extends StandardEndpoint<Void, String> {
        @Override
        public Matcher requestMatcher() {
            return Matcher.match("/hello");
        }

        @Override
        public CompletableFuture<ResponseInfo<String>> execute(RequestInfo<Void> request,
                                                               Executor longRunningTaskExecutor,
                                                               ChannelHandlerContext ctx) {
            return CompletableFuture.completedFuture(
                ResponseInfo.newBuilder("Hello, world!")
                            .withDesiredContentWriterMimeType("text/plain")
                            .build()
            );
        }
    }

}

The Hello World Sample is similar to this but contains a few more niceties, and that sample's README.md includes information on some of the features you can expect from Riposte, but again, please see the template microservice project for the recommended starter template project and usage documentation.

Riposte usage with other JVM-based languages

Since Riposte is straight Java 8 with no bytecode manipulation, plugins, or other magic required it works seamlessly with whatever JVM language you prefer. Here's the same hello world app from above, but this time in Kotlin:

fun main(args : Array<String>) {
    val server = Server(AppServerConfig)
    server.startup()
}

object AppServerConfig : ServerConfig {
    private val endpoints = Collections.singleton(HelloWorldEndpoint)

    override fun appEndpoints(): Collection<Endpoint<*>> {
        return endpoints
    }
}

object HelloWorldEndpoint : StandardEndpoint<Void, String>() {
    override fun requestMatcher(): Matcher {
        return Matcher.match("/hello")
    }

    override fun execute(request: RequestInfo<Void>,
                         longRunningTaskExecutor: Executor,
                         ctx: ChannelHandlerContext
    ): CompletableFuture<ResponseInfo<String>> {

        return CompletableFuture.completedFuture(
                ResponseInfo.newBuilder("Hello, world!")
                        .withDesiredContentWriterMimeType("text/plain")
                        .build()
        )
    }
}

And again in Scala:

object Main extends App {
  val server = new Server(AppServerConfig)
  server.startup()
}

object AppServerConfig extends ServerConfig {
  val endpoints: java.util.Collection[Endpoint[_]] = java.util.Collections.singleton(HelloWorldEndpoint)

  override def appEndpoints(): java.util.Collection[Endpoint[_]] = endpoints
}

object HelloWorldEndpoint extends StandardEndpoint[Void, String] {
  override def requestMatcher(): Matcher = Matcher.`match`("/hello")

  override def execute(
    request: RequestInfo[Void],
    longRunningTaskExecutor: Executor,
    ctx: ChannelHandlerContext): CompletableFuture[ResponseInfo[String]] =
  {
    CompletableFuture.completedFuture(
      ResponseInfo.newBuilder("Hello, world!")
        .withDesiredContentWriterMimeType("text/plain")
        .build()
    )
  }
}

Template Microservice Project

It's been mentioned already, but it bears repeating: Please see the template microservice project for the recommended starter template project AND usage documentation. The template project is a production-ready microservice with a number of bells and whistles and the template project's README.md contains in-depth usage information and should be consulted first when learning how to use Riposte. The rest of the documentation below in this readme will be focused on the Riposte core libraries.

Riposte Libraries

Riposte is a collection of several libraries, mainly divided up based on dependencies. Note that only riposte-spi and riposte-core are required for a functioning Riposte server. Everything else is optional, but potentially useful depending on the needs of your application:

These libraries are all deployed to Maven Central and can be pulled into your project by referencing the relevant dependency: com.nike.riposte:[riposte-lib-artifact-name]:[version].

Full Core Libraries Documentation

Full documentation on the Riposte libraries will be coming eventually. In the meantime the javadocs for Riposte classes are fairly fleshed out and give good guidance, and the template microservice project is a reasonable user guide. Here are some important classes to get you started:

  • com.nike.riposte.server.Server - The Riposte server class. Binds to a port and listens for incoming HTTP requests. Uses ServerConfig for all configuration purposes.
  • com.nike.riposte.server.config.ServerConfig - Responsible for configuring a Riposte server. There are lots of options and the javadocs explain what everything does and recommended usage.
  • com.nike.riposte.server.http.StandardEndpoint - A "typical" endpoint where you receive the full request and provide a full response. The javadocs in StandardEndpoint's class hierarchy (com.nike.riposte.server.http.NonblockingEndpoint and com.nike.riposte.server.http.Endpoint) are worth reading as well for usage guidelines and to see what endpoint options are available.
  • com.nike.riposte.server.http.ProxyRouterEndpoint - A "proxy" or "router" style endpoint where you control the "first chunk" of the downstream request (downstream host, port, headers, path, query params, etc) and the payload is streamed to the destination immediately as chunks come in from the caller. The response is similarly streamed back to the caller immediately as chunks come back from the downstream server. This is incredibly efficient and fast, allowing you to provide proxy/routing capabilities on tiny servers without any fear of large payloads causing OOM, the whole of Java at your fingertips for implementing complex routing logic, and all while enjoying sub-millisecond lag times added by the Riposte server.

Performance Comparisons

To give you an idea of how Riposte performs we did some comparisons against a few popular well-known stacks in a handful of scenarios. These tests show what you can expect from each stack under normal circumstances - excessive tuning was not performed, just some basic configuration to get everything on equal footing (part of Riposte's philosophy is that you should get excellent performance without a lot of hassle).

See the test environment and setup notes section for more detailed information on how the performance testing was conducted.

Raw hello world performance

This test measures the simplest "hello world" type API, with a single endpoint that immediately returns a 200 HTTP response with a static string for the response payload.

NOTE: Spring Boot was using the Undertow embedded container for maximum performance in these tests. The default Tomcat container was significantly worse than the numbers shown here.

Concurrent Call Spammers Stack Realized Requests Per Second Avg latency (millis) 50% latency (millis) 90% latency (millis) 99% latency (millis) CPU Usage
1 Riposte 7532 0.138 0.131 0.135 0.148 36%
1 Springย Boot 4868 0.220 0.205 0.220 0.305 34%
3 Riposte 18640 0.176 0.154 0.187 0.246 92%
3 Springย Boot 11888 0.307 0.238 0.284 0.980 75%
5 Riposte 22038 0.269 0.222 0.286 1.13 99%+
5 Springย Boot 12930 0.775 0.358 1.39 8.25 84%
10 Riposte 23136 1.08 0.251 3.18 8.32 99%+
10 Springย Boot 13862 2.26 0.552 6.61 24.24 92%
15 Riposte 23605 1.38 0.429 4.39 9.56 99%+
15 Springย Boot 14062 3.26 0.817 9.14 35.51 93%

Async non-blocking endpoint performance

This test measures how well the stacks perform when executing asynchronous non-blocking tasks. For a real service this might mean using a NIO client for database or HTTP calls (e.g. Riposte's AsyncHttpClientHelper) to do the vast majority of the endpoint's work, where the endpoint is just waiting for data from an outside process (and therefore NIO allows us to wait without using a blocking thread).

For these tests we simulate that scenario by returning CompletableFutures that are completed with a "hello world" payload after a 130 millisecond delay using a scheduler. In the case of Riposte we can reuse the built-in Netty scheduler via ctx.executor().schedule(...) calls, and for Spring Boot we reuse a scheduler created via Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * 2) to match the Netty scheduler as closely as possible. In both cases the thread count on the application remains small and stable even when handling thousands of concurrent requests.

NOTE: Spring Boot was using the Undertow embedded container for maximum performance in these tests. The default Tomcat container was significantly worse than the numbers shown here.

Each call in the tests below has a 130 millisecond scheduled delay before being completed and returned to the spammer, so 130 millis is the ideal latency.

Concurrent Call Spammers Stack Realized Requests Per Second Avg latency (millis) 90% latency (millis) 95% latency (millis) CPU Usage
700 Riposte 5356 130 131 131 29%
700 Springย Boot 5206 134 140 143 64%
1400 Riposte 10660 131 132 134 57%
1400 Springย Boot 8449 165 181 188 97%
2100 Riposte 15799 132 136 139 80%
2100 Springย Boot 8489 247 267 274 99%
2800 Riposte 20084 138 149 157 94%
2800ย (Notย Attempted) Springย Boot N/A N/A N/A N/A N/A
3500 Riposte 21697 160 187 198 99%
3500ย (Notย Attempted) Springย Boot N/A N/A N/A N/A N/A

Proxy/router performance

One of Riposte's endpoint types (ProxyRouterEndpoint) is for proxy and/or routing use cases where you can adjust request and/or response headers and determine the destination of the call, but otherwise leave the payload alone. This allows Riposte to stream chunks to and from the destination and caller as they become available rather than waiting for the entire request/response to enter memory. The end result is a system that lets you use Riposte as a proxy or router on very low-end hardware and still get excellent performance; payload size essentially doesn't matter - e.g. you can act as a proxy/router piping gigabyte payloads between systems on a box that only has a few hundred megabytes of RAM allocated to Riposte. It also doesn't matter if the downstream service takes 5 seconds or 5 milliseconds to respond since Riposte uses nonblocking I/O under the hood and you won't end up with an explosion of threads.

These kinds of robust proxy/routing features are not normally available in Java microservice stacks, so to provide a performance comparison we put Riposte up against industry-leading NGINX. Riposte does not win the raw performance crown vs. NGINX (it's unlikely any Java-based solution could), however:

  • Riposte comes with distributed tracing and circuit breaking baked into its proxy/routing features, both critical features in a distributed microservice environment.
  • You have the full wealth of the Java ecosystem for implementing your proxy/routing logic, allowing you to easily add features like service discovery, load balancing, auth/security schemes, custom circuit breaker logic, dynamic configuration, or anything else you can dream up.
    • This is not something to be discounted, especially if you're in an environment with a lot of Java expertise but not much NGINX/C/C++/Lua expertise.
  • Creating, implementing, and wiring up ProxyRouterEndpoint endpoints in Riposte is as simple as the StandardEndpoint endpoints.
  • Mix and match your proxy/router endpoints with standard REST-style endpoints in the same application - again, in Riposte it's just a different endpoint type.
  • Riposte required less tuning to achieve its max performance in these tests, including zero operating-system-level tweaks.
  • Riposte does manage a respectable showing against NGINX, all things considered.

Each call in the tests below is proxied through the stack to a backend service that has a 130 millisecond scheduled delay before responding, so 130 millis is the ideal latency.

Concurrent Call Spammers Stack Realized Requests Per Second Avg latency (millis) 90% latency (millis) 95% latency (millis) CPU Usage
140 Riposte 1068 131 132 132 18%
140 NGINX 1070 130 131 131 6%
700 Riposte 5256 133 135 139 77%
700 NGINX 5330 131 132 132 21%
1050 โ€  Riposte 7530 139 150 156 95%
1050ย (Notย Attempted) NGINX N/A N/A N/A N/A N/A
2240ย (Notย Attempted) Riposte N/A N/A N/A N/A N/A
2240 โ€ โ€  NGINX 15985 139 138 140 57%

Proxy/router test footnotes

โ€  - Riposte maxed out on these tests at about 7500 RPS. The bottleneck was CPU usage. NGINX wasn't tested at this throughput, but would have performed very well given its max.

โ€ โ€  - NGINX maxed out on these tests at about 16000 RPS. The bottleneck was not CPU usage, but something else. Increasing concurrent spammers simply caused larger and larger bursts of multi-second response times - even at 2240 concurrent spammers there was a small number of outliers which caused the average to jump above the 90th percentile. Throughput could not be pushed above 16000 RPS even though there was plenty of CPU headroom.

Performance comparison test environment and setup notes

  • All tests were performed on an Amazon c4.large EC2 instance spun up with the basic Amazon Linux.
  • Gatling and wrk were used as the benchmarking/load generating frameworks.
  • Spring Boot was chosen for Java microservice API performance comparisons as it is a well known, well supported, popular stack that is also geared towards spinning up projects quickly and easily. We used Undertow as the Spring Boot embedded container as it had the best performance characteristics. Also note that these performance comparisons should not be construed as testing Netty vs. Undertow, or even Riposte vs. Undertow. There are performance comparisons that directly exercise raw Netty and raw Undertow (example), but the ones here are specifically testing Spring Boot and Riposte, not the underlying framework/containers.
  • NGINX was chosen for proxy/router performance comparisons as it is a well known, well supported, popular solution for high performance proxy/routing needs.
  • Logging output was turned off for each stack via standard config mechanisms (we're measuring what the stacks can do, not logging frameworks or disk I/O capabilities).
  • The Java stacks were started with a few "one-size-fits-many" JVM options we've found to be a good starting place for many use cases - new generation size 1/3 of total JVM memory, CMS garbage collector, etc. The JVM args used for these tests on the c4.large EC2 instance: -Xmx2607876k -Xms2607876k -XX:NewSize=869292k -XX:+UseConcMarkSweepGC -XX:SurvivorRatio=6 -server
  • In order for NGINX to be able to handle some of the load we were sending it without "Too many open files" errors we had to adjust the operating system limits and the NGINX configuration. We also turned on connection pooling and keep-alive connections for NGINX in the config.
  • To test maximum performance capabilities the tests are designed with "concurrent spammers" where a certain number of concurrent callers are calling the stack being tested as quickly as they can - as soon as a caller receives a response it sends out another request.
  • Keep-alive connections were used for the concurrent spammers and for proxied backend calls when doing proxy/router tests.
  • All stacks were given warm-up periods to adjust to the test loads before measurement began. Measured test periods lasted 20 minutes for each individual test.
  • Disabling the core distributed tracing functionality in Riposte is not something that is expected or desired and would fall under the "excessive tuning" category so it was left on. Since logging is off the result is essentially wasted CPU cycles for Riposte, but it does mean that when logging is turned on for normal microservices that distributed tracing is effectively "free" relative to these performance tests, where adding distributed tracing to the other stacks would require extra work and result in worse performance compared to these tests.

License

Riposte is released under the Apache License, Version 2.0

More Repositories

1

Willow

Willow is a powerful, yet lightweight logging library written in Swift.
Swift
1,334
star
2

gimme-aws-creds

A CLI that utilizes Okta IdP via SAML to acquire temporary AWS credentials
Python
902
star
3

Elevate

Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable.
Swift
612
star
4

koheesio

Python framework for building efficient data pipelines. It promotes modularity and collaboration, enabling the creation of complex pipelines from simple, reusable components.
Python
570
star
5

burnside

Fast and Reliable E2E Web Testing with only Javascript
JavaScript
382
star
6

wingtips

Wingtips is a distributed tracing solution for Java based on the Google Dapper paper.
Java
326
star
7

hal

hal provides an AWS Lambda Custom Runtime environment for your Haskell applications.
Haskell
235
star
8

brickflow

Pythonic Programming Framework to orchestrate jobs in Databricks Workflow
Python
176
star
9

spark-expectations

A Python Library to support running data quality rules while the spark job is runningโšก
Python
161
star
10

SQift

Powerful Swift wrapper for SQLite
Swift
141
star
11

timeseries-generator

A library to generate synthetic time series data by easy-to-use factors and generator
Python
122
star
12

bartlett

A simple Jenkins command line client to serve your needs.
Haskell
81
star
13

cerberus-doc-site

Secure Property Store for Cloud Applications
CSS
81
star
14

aws-greengrass-core-sdk-rust

Provides an idiomatic Rust wrapper around the AWS Greengrass Core C SDK to more easily enable Greengrass native lambda functions in Rust.
Rust
71
star
15

cerberus

The Cerberus micro-service, a secure property store for cloud applications. It includes a REST API, authentication and encryption features, as well as a self-service web UI for users.
Java
62
star
16

referee

Referee is a UI for using Spinnaker Kayenta as a standalone service.
TypeScript
59
star
17

moirai

Libraries that can be used to determine if a feature should be exposed to a user.
Java
52
star
18

riposte-microservice-template

An example template for quickly creating a new Riposte microservice project.
Java
51
star
19

harbormaster

Harbormaster is a webhook handler for the Kubernetes API.
Go
42
star
20

fastbreak

Fastbreak is a simple Java 8 native circuit breaker supporting async future, blocking, and callback/manual modes.
Java
40
star
21

signal_analog

A troposphere-inspired library for programmatic, declarative definition and management of SignalFx Charts, Dashboards, and Detectors.
Python
39
star
22

backstopper

Backstopper is a framework-agnostic API error handling and (optional) model validation solution for Java 7 and up.
Java
38
star
23

react-virtualized-item-grid

React component for efficiently rendering a large, scrollable list of items in a series of wrapping rows
JavaScript
38
star
24

knockoff-factory

A library for generating fake data and populating database tables.
Python
34
star
25

pterradactyl

Pterradactyl is a library developed to abstract Terraform configuration from the Terraform environment setup.
Python
32
star
26

lambda-logger-node

A middleware logger that implements the MDC logging pattern for use in AWS NodeJS Lambdas.
TypeScript
29
star
27

lambda-router

JavaScript
23
star
28

bokor

Bokor is a simple, Record and Playback Mock Server written in Node.js, utilized for Service Virtualization.
JavaScript
23
star
29

piggyback

This tool allows you to tunnel SSH (using ProxyCommand) via HTTPS (with Squid Proxy). It is a python implementation of corkscrew, but over https (TLS) instead of http (plaintext).
Python
17
star
30

cerberus-node-client

Node client for interacting with a Cerberus backend. It can be used in Amazon EC2 instances and Amazon Lambdas.
JavaScript
16
star
31

cerberus-java-client

Java Client for Cerberus
Java
14
star
32

cerberus-lifecycle-cli

Command Line Interface for managing a Cerberus environment in AWS
Java
14
star
33

cerberus-python-client

Python Client for Cerberus
Python
13
star
34

cerberus-management-dashboard

A single page react app that is the self service web UI for administration of Safe Deposit Boxes, access control, and data.
HTML
13
star
35

tdd-training-cube

Papercraft cube used as training aid for Outside-In Test Driven Development
11
star
36

cerberus-go-client

A Golang client for interacting with Cerberus, a secure property store for cloud applications.
Go
11
star
37

cerberus-serverless-components

A collection of AWS Serverless components for Cerberus
Java
11
star
38

gradle-localstack

Gradle plugin for working with mock AWS endpoints using LocalStack.
Java
11
star
39

aws-thin-dynamo-node

A small, fast re-implementation of the AWS Dynamo DocumentClient
JavaScript
10
star
40

cerberus-archaius-client

An Archaius property provider implementation backed by Cerberus.
Java
9
star
41

epc-standards

Implementation of decoding GS1 EPC tags
Java
9
star
42

lambda-zipper

Zip up your node lambda code and production dependencies without pruning node_modules
JavaScript
9
star
43

java-vault-client

This is a java based Vault client library for communicating with Vault via HTTP.
Java
8
star
44

cerberus-cli

A CLI for the Cerberus API.
Go
8
star
45

cerberus-integration-tests

Groovy
8
star
46

cerberus-gateway-puppet-module

Puppet Module for installing Nginx and config downloader scripts
Python
8
star
47

cerberus-consul-puppet-module

A Puppet module for installing Hashicorp's Consul as a service with customized start up scripts for Cerberus.
HTML
7
star
48

Fleam

Scala
6
star
49

bluegreen-manager

Java
6
star
50

aws-thin-s3-node

A super-thin AWS S3 client
JavaScript
5
star
51

homebrew-nike

Homebrew formulas provided by Nike, Inc.
Ruby
5
star
52

sagerender

A library for configuring SageMaker pipelines using hierarchical configuration pattern.
Python
5
star
53

dynamo-arc

TypeScript
5
star
54

metrics-new-relic-insights

Reporter to send Dropwizard Metrics to New Relic Insights.
Java
5
star
55

cerberus-vault-puppet-module

A Puppet module for installing Hashicorp's Vault as a service with customized start up scripts for Cerberus.
HTML
5
star
56

cerberus-spring-boot-client

Spring Boot client for interacting with a Cerberus backend.
Java
4
star
57

dabber

Dabber is a Node CLI tool and AWS Lambda that helps you work with Dynamo.
JavaScript
3
star
58

actions-cerberus-secrets

Read secrets from Cerberus and make it as environment variables in GitHub Actions job so that it can be used in CICD process.
TypeScript
3
star
59

nike-inc.github.io

HTML
3
star
60

aws-thin-ses-node

A super-thin AWS Simple Email Service client
JavaScript
3
star
61

phiera

Python
2
star
62

cerberus-ruby-client

Ruby Client for Cerberus
Ruby
2
star
63

aws-scale

AWS Scaling Made Simple
JavaScript
2
star
64

gimme-a-cli

Gimme a CLI is a Java library for creating quick and easy command line interfaces (CLIs) using JCommander and Spring dependency injection.
Java
2
star
65

Fawcett

A collection of Monocle lenses for navigating Amazon's API models.
Scala
1
star
66

dynamo-butter

JavaScript
1
star
67

redwiggler

The composting worm. Composts your contract specification and tests and confirms that the contract specification is being followed.
Scala
1
star
68

gradle-localdynamodb-plugin

XSLT
1
star
69

gimme-a-cli-starter-project

Clone and modify this project to quickly create your own CLI based on the Gimme a CLI library.
Java
1
star