Contexts and Dependency Injection

Home

Java EcoSystem Integration


© 2019-2022 rxmicro.io. Free use of this software is granted under the terms of the Apache License 2.0.

Copies of this entity may be made for Your own use and for distribution to others, provided that You do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

If You find errors or omissions in this entity, please don’t hesitate to submit an issue or open a pull request with a fix.

TODO

1. Request Id

To track an HTTP request when using a microservice architecture, the RxMicro framework provides an HTTP request identification feature.

1.1. Basic Rules

If the current request is identified, the provided unique id is used during the life-cycle of the current request. If the request is not identified, the RxMicro framework generates a unique id, which is further used in the life-cycle of the current request.

To store the request id, the Request-Id HTTP additional header is used.

If the HTTP request id is necessary for business logic, a separate field in the HTTP request Java model must be created:

public final class Request {

    (1)
    //@Header(HttpHeaders.REQUEST_ID)
    (2)
    @RequestId
    String requestId;

    public String getRequestId() {
        return requestId;
    }
}
1 For convenience, instead of specifying an HTTP header using the @Header annotation and the Request-Id name,
2 the special @RequestId annotation can be used, which is an alternative to the @Header(HttpHeaders.REQUEST_ID) configuration.

Having declared a field in the HTTP request Java model, You can use its value in the handler:

final class MicroService {

    @GET("/")
    void handle(final Request request) {
        System.out.println(request.getRequestId()); (1)
    }
}
1 Displaying the value of the current request id in the console.

The following test describes the basic requirements for the behavior of the request id function:

@RxMicroRestBasedMicroServiceTest(MicroService.class)
final class MicroServiceTest {

    @WithConfig
    private static final RestServerConfig CONFIG = new RestServerConfig()
            .setRequestIdGeneratorProvider(restServerConfig -> () -> "TestRequestId") (6)
            .setDevelopmentMode(true);

    private BlockingHttpClient blockingHttpClient;

    private SystemOut systemOut;

    @Test
    void Should_generate_RequestId_automatically() {
        final ClientHttpResponse response = blockingHttpClient.get("/");
        assertEquals("TestRequestId", systemOut.asString()); (1)
        assertEquals("TestRequestId", response.getHeaders().getValue(REQUEST_ID));  (2)
    }

    @Test
    void Should_use_provided_RequestId() {
        final ClientHttpResponse response =
                blockingHttpClient.get("/", HttpHeaders.of(REQUEST_ID, "Qwerty")); (3)

        assertEquals("Qwerty", systemOut.asString()); (4)
        assertEquals("Qwerty", response.getHeaders().getValue(REQUEST_ID));  (5)
    }
}
1 For HTTP requests without id, the RxMicro framework must generate a unique id automatically. (This id can be used in the HTTP request handler body.)
2 Each HTTP response must contain the required Request-Id HTTP header with the value of the generated request id.
(If the configuration parameter RestServerConfig.returnGeneratedRequestId = false, the HTTP response will not contain the Request-Id header.)
3 If the current HTTP request already contains an id, the RxMicro framework must use it instead of generating a new value.
4 The id set by the client can be used in the HTTP request handler body.
5 Each HTTP response must contain the required Request-Id HTTP header with a client specified request id value.
(If the configuration parameter RestServerConfig.returnGeneratedRequestId = false, the HTTP response will not contain the Request-Id header.)
6 For test purposes it is necessary to set deterministic request id provider with constant value.

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

1.2. Supported Generator Providers

The RxMicro framework provides the following predefined request id generator providers.

Table 1. Generator name and its implementation class.
Type Implementation class Description

UUID_128_BIT

RandomRequestIdGenerator

Generates unique 16 bytes (128 bits) request id

According to specification this generator is similar to the UUID.randomUUID() generation.

RANDOM_96_BIT

RandomRequestIdGenerator

Generates unique 12 bytes (96 bits) request id

PARTLY_RANDOM_96_BIT

PartlyRandom96BitsRequestIdGenerator

Generates unique 12 bytes (96 bits) request id

Each request id contains 52 random and 44 deterministic bits.

DETERMINISTIC_96_BIT

Deterministic96BitsRequestIdGenerator

Generates unique 12 bytes (96 bits) request id

Each request id contains 44 deterministic bits + 24 incremental counter bits + 28 checksum bits.

DEFAULT_96_BIT

PartlyRandom96BitsRequestIdGenerator, Deterministic96BitsRequestIdGenerator

Generates unique 12 bytes (96 bits) request id

This is default request id generator provider.

By default this request id generator provider tries to use PARTLY_RANDOM_96_BIT request id generator. But if during predefined timeout the java.security.SecureRandom instance can’t generate random bytes, the DETERMINISTIC_96_BIT request id generator will be used.

To change the request id generator provider, You must use the RestServerConfig configuration class:

new Configs.Builder()
        .withConfigs(new RestServerConfig()
                .setRequestIdGeneratorProvider(
                        PredefinedRequestIdGeneratorProvider.UUID_128_BITS (1)
                )
        )
        .build();

or

export rest-server.requestIdGeneratorProvider=\
@io.rxmicro.rest.server.PredefinedRequestIdGeneratorProvider:UUID_128_BIT

or using any other supported config types

To get additional information about how custom type can be used as valid config parameters read core.html

2. Request Tracing Usage Example

The following example demonstrates how request tracing feature can be used:

public final class Request implements RequestIdSupplier { (1)

    (2)
    @RequestId
    String requestId;

    @PathVariable
    Long id;

    @Override
    public String getRequestId() {
        return requestId;
    }

    public Long getId() {
        return id;
    }
}
public final class AccountController {

    @Inject
    private AccountService accountService;

    @GET("/account/${id}")
    Mono<Response> findById(final Request request) {
        return accountService.findById(request);
    }
}
public final class AccountService {

    (1)
    private static final Logger LOGGER = LoggerFactory.getLogger(AccountService.class);

    @Inject
    private AccountRepository accountRepository;

    public Mono<Response> findById(final Request request) {
        LOGGER.debug(
                request, (2)
                "Finding account by id=?...", request.getId()
        );
        return accountRepository.findById(request, request.getId())
                .switchIfEmpty(Mono.error(() -> {
                    LOGGER.error(
                            request, (3)
                            "Account not found by id=?!", request.getId()
                    );
                    return new AccountNotFound404Exception();
                }))
                .map(account -> {
                    LOGGER.debug(
                            request, (4)
                            "Account exists by id=?: ?",
                            request.getId(), account
                    );
                    return new Response(
                            account.getEmail(), account.getFirstName(), account.getLastName()
                    );
                });
    }
}
@PostgreSQLRepository
public interface AccountRepository {

    @Select("SELECT * FROM ${table} WHERE ${by-id-filter}")
    Mono<Account> findById(RequestIdSupplier requestIdSupplier, long id); (1)
}

The `jul.properties' config classpath resource:

(1)
io.rxmicro.logger.jul.PatternFormatter.pattern=%date{HH:mm:ss.SSS} {%id} [%level] %logger{0}: %message%n

io.rxmicro.rest.server.level=TRACE
io.rxmicro.examples.monitoring.request.tracing.level=TRACE

Invoke the test request:

:$ curl -v localhost:8080/account/1
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /account/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: /
>
< HTTP/1.1 200 OK
< Content-Length: 88
< Server: RxMicro-NettyServer/0.7-SNAPSHOT
< Date: Wed, 25 Nov 2020 17:30:51 GMT
< Content-Type: application/json
< Request-Id: AkinnfVzx1752012 (1)
<
* Connection #0 to host localhost left intact
{"email":"richard.hendricks@piedpiper.com","firstName":"Richard","lastName":"Hendricks"}

The log output:

19:30:50.494 {null} [TRACE] NettyClientConnectionController: Client connection created: Channel=eaaf0399, IP=/127.0.0.1:33312

19:30:50.589 {AkinnfVzx1752012} [TRACE] NettyRequestHandler: HTTP request:  (Channel=eaaf0399, IP=/127.0.0.1:33312):
GET /account/1 HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.58.0
Accept: /
content-length: 0


19:30:50.591 {AkinnfVzx1752012} [DEBUG] AccountService: Finding account by id=1...

19:30:50.998 {AkinnfVzx1752012} [TRACE] AccountRepository: Execute SQL 'SELECT id, email, first_name, last_name FROM account WHERE id = $1' with params: [1] using connection: class='PooledConnection', id='c1275307250'...

19:30:51.074 {AkinnfVzx1752012} [TRACE] AccountRepository: SQL 'SELECT id, email, first_name, last_name FROM account WHERE id = $1' with params: [1] executed successful

19:30:51.148 {AkinnfVzx1752012} [DEBUG] AccountService: Account exists by id=1: Account{id=1, email=richard.hendricks@piedpiper.com, firstName=Richard, lastName=Hendricks}

19:30:51.166 {AkinnfVzx1752012} [TRACE] AccountRepository: Connection closed: class='PooledConnection', id='c1275307250', signal='onComplete'

19:30:51.167 {AkinnfVzx1752012} [TRACE] NettyRequestHandler: HTTP response: (Channel=eaaf0399, Duration=588.093042ms):
HTTP/1.1 200 OK
Content-Length: 88
Server: RxMicro-NettyServer/0.7-SNAPSHOT
Date: Wed, 25 Nov 2020 17:30:51 GMT
Content-Type: application/json
Request-Id: AkinnfVzx1752012

{"email":"richard.hendricks@piedpiper.com","firstName":"Richard","lastName":"Hendricks"}

For more information, we recommend that You familiarize yourself with the following examples:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

Contexts and Dependency Injection

Home

Java EcoSystem Integration