Skip to content
This repository has been archived by the owner on May 11, 2021. It is now read-only.

Base module

Tomasz Nurkiewicz edited this page Jan 29, 2015 · 6 revisions

Parts of base module (micro-infra-spring-base)


Service discovery

Description

This project reuses the beans and configurations set up in the micro-deps-spring-config project. Please check the documentation of that project to receive more in depth information about its content.

To put it briefly we are setting up via ServiceDiscoveryConfiguration that imports ServiceResolverConfiguration configurations and beans that provide (names in brackets are names of classes)

  • Microservice's host and port (MicroserviceAddressProvider)
  • Zookeeper connection client (CuratorFramework)
  • Registration of the microservice in Zookeeper (ServiceInstance)
  • Service discovery (ServiceDiscovery)
  • Dependency watching - checking if dependency is still alive (DependencyWatcher)
  • Parsing of microservice configuration - defaults to classpath resource microservice.json (ServiceConfigurationResolver)
  • Service resolution (ServiceResolver)

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and the you have to either:

component scan over com.ofg.infrastructure.discovery:

@Configuration
@ComponentScan("com.ofg.infrastructure.discovery")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.discovery.ServiceDiscoveryConfiguration)
class MyModuleConfiguration {
}

Spring Environment setup

Description

EnvironmentSetupVerifier is a Spring's ApplicationListener that verifies that you have provided a spring profile upon application execution (via spring.profiles.active system property). If it's not provided the application will close with error.

Example of usage

Example setup for Groovy (note that you don't have to register EnvironmentSetupVerifier as a bean):

@TypeChecked
@Configuration
@EnableAutoConfiguration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan(basePackages = ["com.ofg.microservice", "com.ofg.twitter", "com.mangofactory.swagger"])
@EnableCaching
@EnableAsync
class Application {

    static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application)
        application.addListeners(new EnvironmentSetupVerifier(Profiles.all()))
        application.run(args)
    }
}

where Profiles looks like this:

@TypeChecked
class Profiles {
    public static final String PRODUCTION = "prod"
    public static final String TEST = "test"
    public static final String DEVELOPMENT = "dev"

    static List<String> all() {
        return [PRODUCTION, TEST, DEVELOPMENT]
    }
}

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

Health check

Description

It's nice to know that your application is up and running, isn't it? We monitor our microservices via Zabbix that pings to our controllers. If you want to you can do the same by picking this module.

In HealthCheckConfiguration we are registering a PingController that if you send a GET request to /ping it will respond with OK if it's alive.

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.healthcheck:

@Configuration
@ComponentScan("com.ofg.infrastructure.healthcheck")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.healthcheck.HealthCheckConfiguration)
class MyModuleConfiguration {
}

Metrics publishing

Description

We have microservices so we need to monitor our system. Not only whether they are alive or not. We want to measure as much as possible. From every possible angle - amount of requests, technical stuff like how many errors we have or from business perspective - how many loans are issued per minute (or whatever). We collect those measurements in Graphite.

If you select this module we will give you two publishers (Graphite - GraphitePublisher and JMX - JmxPublisher) that will provide setup for your metrics to be published in Graphite and as an MBean.

As a Spring bean we are providing beans (including MetricRegistry that is required by all metrics) that will take care of the metric name standards that we have in 4finance:

 (root-name).(environment).(country).(application-name).(metric-name)

Example of usage

Sample metric gathering class (extract from Boot microservice template):

class MatchProbabilityMetrics {

    private final Map<PlaceResolutionProbability, Meter> probabilityMeters = [:]

    MatchProbabilityMetrics(MetricRegistry metricRegistry) {
        registerProbabilityMetrics(metricRegistry)
    }

    void update(PlaceResolutionProbability probability) {
        probabilityMeters[probability].mark()
    }

    private void registerProbabilityMetrics(MetricRegistry metricRegistry) {
        PlaceResolutionProbability.values().each { probability ->
            probabilityMeters[probability] = metricRegistry.meter("twitter.places.analyzed.probability.$probability")
        }
    }
}

Example of a metric path (for different measurements of high probability of locations of tweets)

apps.test.pl.twitter-places-analyzer.places.analyzed.probability.high

Metrics properties

Below you can find properties with its default values:

# Host where Graphite is deployed
graphite.host=graphite.4finance.net

# Port for Graphite
graphite.port=2003

# Publishing interval in ms 
graphite.publishing.interval=15000

# Root metrics param of 'apps'
metrics.path.root=apps

# Environment to which you deploy application ('test', 'stage', 'prod')
metrics.path.environment=test

# Country for which you deploy your app
metrics.path.country=pl

# Your app name
metrics.path.app=service-name

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.metrics.publishing:

@Configuration
@ComponentScan("com.ofg.infrastructure.metrics")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.metrics.registry.MetricsConfiguration)
class MyModuleConfiguration {
}

Controller exceptions handling

Description

Sometimes exceptions roam around like crazy in your system and might forget to catch them. Not to worry - we will come to the rescue! Our ControllerExceptionHandler catches all exceptions there are and will present them in a nice JSON format (assuming that you use our JsonViewResolver).

You are a good programmer so most likely you are using some JSR validation implementations. That's great and we'll be happy to present those errors in a nice way.

Beware that this module is not included in default configuration.

Example of usage

Let's assume that we have a following controller

@RestController
class TestController {
    @RequestMapping(value = "/test", produces = "application/json", method = RequestMethod.POST)
    String test(@RequestBody @Valid TestRequest request, BindingResult result) {
        checkIfResultHasErrors(result)
        return "OK"
    }

    private void checkIfResultHasErrors(BindingResult result) {
        if (result.hasErrors()) {
            throw new BadParametersException(result.getAllErrors())
        }
    }
}

class TestRequest {
    @AssertTrue
    boolean shouldBeTrue
}

If validation fails we throw BadParametersException that we catch in ControllerExceptionHandler and using JsonViewResolver we can pretty print that JSON for you!

Module configuration

If you want to use this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.web.exception:

@Configuration
@ComponentScan("com.ofg.infrastructure.web.exception")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.web.exception.ControllerExceptionConfiguration)
class MyModuleConfiguration {
}

JSON View resolving

Description

Configures JSON serialization for objects returned by controllers' methods.

Module configuration

If you want to setup only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.web.view:

@Configuration
@ComponentScan("com.ofg.infrastructure.web.view")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.web.view.ViewConfiguration)
class MyModuleConfiguration {
}

CorrelationId setting

Description

We are working with microservices. Many microservices. Imagine a series of 20 microservices processing one request - let's say that we want to grant a loan to a Mr Smith. Since we are professionals we have log collecting tools like logstash and kibana. Now imagine that something broke - an exception occurred. How can you find in those hundreds of lines of logs which ones are related to Mr Smith's case? Correlation id will speed up your work effortlessly.

Since we are using Spring then most likely we can receive or send a request by

  • a @Controller annotated controller
  • a @RestController annotated controller
  • by sending a request via a RestTemplate

To deal with all of those approaches we:

  • created a filter CorrelationIdFilter that will set a correlation id header named correlationId on your request
  • created an aspect CorrelationIdAspect that makes it possible to work with Servlet 3.0 async feature (you have to have a controller method that returns a Callable)
  • the very same aspect allows checks if you are using a RestOperations- base interface of RestTemplate. If that is the case then we are setting the correlationId header on the request that you are sending (via exchange method).

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.web.filter.correlationid:

@Configuration
@ComponentScan("com.ofg.infrastructure.web.filter.correlationid")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.web.filter.correlationid.CorrelationIdConfiguration)
class MyModuleConfiguration {
}

Request body logging

Description

This module is responsible for logging request body in debug mode. It registers a Log4jNestedDiagnosticContextFilter extension called RequestBodyLoggingContextFilter.

Example of usage

You can provide max payload that should be printed in logs by providing a property like presented below (example for a payload of 1000 chars).

request.payload.logging.maxlength:1000

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.web.filter.logging:

@Configuration
@ComponentScan("com.ofg.infrastructure.web.filter")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.web.filter.logging.RequestFilterConfiguration)
class MyModuleConfiguration {
}

also you have to explicitly provide DEBUG logging level for this component.

Customized rest template

Description

This module with a customized RestTemplate. that gives you:

  • Error handling via ResponseRethrowingErrorHandler - logs error body if it's present and rethrows exception
  • Request factory (BufferingClientHttpRequestFactory) - so that we can access request's body several times throughout request's processing

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and you can extend this RestTemplate and use it in your own code.

Abstraction over RestTemplate - bound with service discovery

Description

You want to send requests to your collaborators. That's good! But do you want to take care of service discovery and request building manually from scratch? Of course not! That's why we provide a ServiceRestClient that will take care of everything for you and gives you a fluent interface to achieve that.

What does it consist of?

ServiceRestClient

The entry point to the abstraction is ServiceRestClient. It takes two parameters to its constructor

Once your ServiceRestClient is created you are ready to send some requests. You have one of two methods to start with

  • forExternalService() - if you want to send a request to an URL (for example http://some.address.com/api/whatever/123)
  • forService('cola') - if you want to send a request to your collaborator (for example in your microservice.json you have provided a collaborator by name cola)

HTTP Methods

Ok, so we picked either of them and now we can choose which http method we want to call. You can pick either of these:

  • delete
  • get
  • head
  • options
  • post
  • put

Request body

Depending on the method you now can have different methods available. Some HTTP methods accept request bodies others don't. So you may have the body() method but it's not obligatory.

Request headers

You can use the withHeaders() method to provide additional headers. There are some predefined most commonly used methods, but you can also provide your own custom headers if you want to.

Response

You can receive a response in a number of ways:

  • anObject().ofType(String) - converts the body of response to String (you can provide any other type)
  • aResponseEntity().ofType(String) - returns the response as a ResponseEntity with body of type String (you can provide any other type)
  • ignoringResponse() - doesn't return any type of response

There are also some additional custom response returning methods like forLocation() etc. but they are specific for given HTTP methods.

Example of usage

For more examples please check the specifications available under com.ofg.infrastructure.web.resttemplate.fluent package under HTTP method named packages.

Example of sending to URL

Let's assume that we want to send a request to http://some.address.com/api/whatever/123

serviceRestClient.forExternalService()
                             .post()
                             .onUrl('http://some.address.com/api/whatever/123')
                             .body('''{"some":"json"}''')
                             .withHeaders()
                                .contentTypeJson()
                             .andExecuteFor()
                             .anObject()
                             .ofType(String)

Example of sending to collaborator

Let's assume that we want to send a request to a service 'cola' that has registered itself in Zookeeper at address http://some.address.com and we want to call it at path /api/whatever/123

serviceRestClient.forService('cola')
                 .post()
                 .onUrl('/api/whatever/123')
                 .body('''{"some":"json"}''')
                 .withHeaders()
                    .contentTypeJson()
                 .andExecuteFor()
                 .anObject()
                 .ofType(String)

Module configuration

If you want to use only this module just add a dependency:

repositories {
    jcenter()
}

dependencies {
    compile 'com.ofg:micro-infra-spring-base:0.4.1'
}

and either

component scan over com.ofg.infrastructure.web.resttemplate.fluent:

@Configuration
@ComponentScan("com.ofg.infrastructure.web.resttemplate.fluent")
class MyWebAppConfiguration {
}

or add the configuration explicitly

@Configuration
@Import(com.ofg.infrastructure.web.resttemplate.fluent.ServiceRestClientConfiguration)
class MyModuleConfiguration {
}
Clone this wiki locally