Here, we discussed the building of an infrastructure for a microservice application, based on spring cloud stack.

1. Service registry (eureka)

1.1. Configuration Eureka Server

@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {
    ..
}

1.2. Make the application discoverable (client side)

@SpringBootApplication
@EnableDiscoveryClient
public class ComplaintServiceApplication {
    ..
}

1.3. Set properties

spring.application.name=complaint-service

The name of the service will be used to find it in many cases This is exactly the identifier that you need to know for neighboring services and applications

1.4. Make a client for the complaint-service

@RestController
@RequestMapping("clients")
public class ComplaintClientsController {

    private RestTemplate restTemplate;

    @Autowired
    public GatewayBookController(@LoadBalanced RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @RequestMapping(value = "/init", method = RequestMethod.GET)
    public String getComplaintName(UUID id){
        return restTemplate.getForEntity("http://complaint-service/{id}", (1)
                                         String.class,
                                         id)
                           .getBody();
    }
}
1 Now we can access the service without the ip address on which it is deployed, eureka will help us find the required service instance by its name

2. Centralized microservices configuration (Spring config server)

2.1. Making config server

add dependency:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

properties:

spring.application.name=config-service
server.port=${PORT:8888}

spring.cloud.config.failFast=true
spring.cloud.config.label=master
spring.cloud.config.profile=default
spring.cloud.config.server.bootstrap=true
spring.cloud.config.server.git.uri= // GITHUB_REPO_URL, BITBUCKET_REPO_URL, LOCAL_REPO_URL
spring.cloud.config.server.git.username= // USER_NAME
spring.cloud.config.server.git.password= // USER_PASSWORD
spring.cloud.config.server.git.clone-on-start=true

configuration:

@EnableConfigServer
@SpringBootApplication
public class ConfigServiceApplication {

	public static void main(String[] args) {
		CustomRunner.run(ConfigServiceApplication.class, args);
	}
}

2.2. Making spring config client

set properties:

spring.cloud.config.uri = http://localhost:8888

dynamic configuration for property value:

@RefreshScope
@RestController
public class TestController {

  @Value("${dynamic.property.name:default}")
  private String name;

  @RequestMapping("/")
  public String getName() {
    return this.name;
  }
}

2.3. Configuraion repository

$ ls
complaints-service.properties
complaints-service-production.properties
complaints-service-develop.properties
initiative-service-production.properties
poll-service.properties
wiki-service.properties
content-storage.properties
aggregator-service.properties
auth-service.properties

3. Declarative Web client (Feign)

Enable feign autoconfiguration config:

@Configuration
@EnableFeignClients
public class AppConfig {
  ...
}

Describe service API by declaration java interface:

@FeignClient(value = "content-storage", configuration = FeignConfig.class)
public interface ContentStorageFeign {

    @ResponseBody
    @PostMapping(value = "/files/upload", produces = APPLICATION_JSON_VALUE)
    ContentDescriptor create(@RequestPart(value = "data") MultipartFile file,
                             @RequestParam(value = "author") String author);

}

We use the service, and the implementation itself creates by spring, no restTemplates with ugly arguments and methods, just use it as a ordinary service:

@Autowired
private ContentStorageFeign contentStorageFeign;

public void upload() {
    ...
    contentStorageFeign.create(file, "admin");
    ...
}

4. Circuit breaker pattern (Hystrix)

configuration:

@Configuration
@EnableCircuitBreaker
public class AppConfig {
  ...
}

client side usage:

@Service
public class RatingService {

    @HystrixCommand(fallbackMethod = "default") (1)
    public String getRating(UUID seasonId) {
        return new RestTemplate()
                      .getForObject(DEEP_THOUGHT_URL_PATTERN, String.class, id);  (2)
    }

    private String default(UUID id) {
        return "42";
    }
}
1 specify the method that should be called when the chain is opened
2 there is a resource-intensive query, which can exceed the timeout

5. Logging

5.1. create an Aspect for logging:

@Slf4j
@Aspect
public class LoggingAspect {

    ...

    @Around("@within(org.springframework.web.bind.annotation.RestController)") (1)
    public Object onRestControllerAnnotation(ProceedingJoinPoint joinPoint)
            throws Throwable {

        Object result = joinPoint.proceed();

        logging(LoggingType.TRACE,
                String.valueOf(joinPoint.getSignature()),
                joinPoint.getArgs(),
                result);

        return result;
    }

    ...

}
1 intercept all methods inside the sprint REST controller

5.2. Write the simplest config

@Configuration
@EnableAspectJAutoProxy
public class LoggingAspectConfig {
    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

5.3. Wrap the configuration in the custom annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({LoggingAspectConfig.class})
public @interface EnableLogging {
}

5.4. Put in a separate dependency

artifactory will help us in this

jfrog

5.5. Use it!

@Configuration
@EnableLogging (1)
public class AppConfig {
    ...
}
1 One line in the selected service starts the logging in the required format