Spring Cloud - broyda/development-stack GitHub Wiki
-Spring Cloud derives environment properties from a configuration server.
Bootstrap process (runs before Spring startup)
@Component
@ConfigurationProperties("limits-service") // This is the prefix in application properties file
server.port=8888 // Configure port for this microservice
@EnableConfigServer annotation - publishes a config server
# Below line in any service connects to the config service
spring.application.name=limits-service
spring.config.import=optional:configserver:http://localhost:8888
spring.profiles.active=dev
spring.cloud.config.profile=qa // this one is ACTUALLY picked up!
What the above will do is fetch the properties from http://localhost:8888/limits-service/default where:
http://localhost:8888 is the location of configuration server
limits-service is the application name whose properties we want to fetch (should match name of limits-service.properties file in git)
default - is the profile name
In the project that has the properties files:
limit-services.properties
limit-services-dev.properties
limit-services-qa.properties
There are two separate dependencies - spring-config server and spring-config client
If we launch two instances of the same microservices by specifying -DServer.port=8001 in the command line args, then that would override the port specified in the properties file.
Invoking one microservice from another
@GetMapping("/currency-conversion/from/{from}/to/{to}/quantity/{quantity}")
public CurrencyConversion calculateCurrencyConversion(
@PathVariable String from,
@PathVariable String to,
@PathVariable BigDecimal quantity
) {
HashMap<String, String> uriVariables = new HashMap<>();
uriVariables.put("from",from);
uriVariables.put("to",to);
ResponseEntity<CurrencyConversion> responseEntity = restTemplate.getForEntity
("http://localhost:8000/currency-exchange/from/{from}/to/{to}",
CurrencyConversion.class, uriVariables);
CurrencyConversion currencyConversion = responseEntity.getBody();
return new CurrencyConversion(currencyConversion.getId(),
from, to, quantity,
currencyConversion.getConversionMultiple(),
quantity.multiply(currencyConversion.getConversionMultiple()),
currencyConversion.getEnvironment()+ " " + "rest template");
}
Feign (invocation of another external service from within a service, also used for loadbalancing)
Instead of above, a cleaner way is to use Feign
//@FeignClient(name="currency-exchange", url="localhost:8000")
@FeignClient(name="currency-exchange")
public interface CurrencyExchangeProxy {
@GetMapping("/currency-exchange/from/{from}/to/{to}")
public CurrencyConversion retrieveExchangeValue(
@PathVariable String from,
@PathVariable String to);
@SpringBootApplication
@EnableFeignClients
public class CurrencyConversionServiceApplication {
}
All services register themselves with a naming server. This helps in loadbalancing and dynamic service discovery. Eureka (spring boot actuator)
@EnableEurekaServer
Need to add netflix-eureka-client as a dependency in the other microservices. Add eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka to point to the Eureka server.
in the earlier versions of Spring Cloud, ribbon was the load balancer, now it has been replaced by Spring cloud loadbalancer (a dependency from Netflix Eureka).
API Gateways
Zuul was the earlier solution, replaced by Spring Cloud Gateway.
Idea is we hit the gateway first which then routes to the underlying service. Even services that calls into another service routes through the api gateway. The idea is to extract cross cutting functionalities into the common API gateway (think security, logging).
API gateway itself registers to the Eureka server and can discover other services.
To the API gateway we need to append the application name (as can be found from the Eureka console). So an url would look like:
localhost:8744/currency-conversion/
In order to simplify the urls, we can use a ApiGatewayConfiguration class that does things like url rewriting (via filters on many different request attributes such as path, hosts etc), routing (through .route() etc.
A custom filter can be created by extending Spring's GlobalFilter.
Resilience4j (Circuit Breaker Framework - replacement for Hysterix)
- Service returns a fallback response if a service is down.
- Ckt breaker pattern to reduce load / implement rate limit
@CircuitBreaker(name="", fallbackMethod=someMethod)
- Retry request in case of temporary failures. @Retry (default - 3 times, fallbackMethod=methodname). The fallback method should accept a Throwable (or any custom exception for special handling based on specific exceptions.)
- retry configurations (number of times to retry, duration between retries, exponential backoff - per api url) can be made in application.properties
- @RateLimiter (10 requests every 2 seconds - configure in properties file)
- @Bulkhead (number of concurrent requests)