Slide 1

Slide 1 text

Getting back in the ring 09.05.2019, Mainz

Slide 2

Slide 2 text

Tim Zöller what do I do? 2 Mainz Freiburg Hamburg IT Consultant / Team Lead Java Developer since 2008 Co-Founder of JUG Mainz javahippie @javahippie

Slide 3

Slide 3 text

Speed Memory PACKAGE SIZE A WINNER Things I won’t cover … although They might be important 3 Feel free to discuss these aspects with me afterwards

Slide 4

Slide 4 text

The Scenario

Slide 5

Slide 5 text

Online shop is running on own infrastructure Want to adapt new technology Nerd Fashion Inc. Imaginary Online Shop 5

Slide 6

Slide 6 text

Sarah Senior Software Developer Matthew Senior Software Developer ABOUT THE TEAM Employees at Nerd Fashion Inc. 6 Helen Department Lead ?

Slide 7

Slide 7 text

Application Landscape 7 Customer Service (existing) Complaint Service Order Service REST REST REST REST

Slide 8

Slide 8 text

Communicate via REST, Document Interfaces 04 03 02 01 Requirements Apply resilience measurements Provide health status and metrics Configure everything externally 8

Slide 9

Slide 9 text

Initializing

Slide 10

Slide 10 text

Maven or Gradle Java, Kotlin, Groovy Java 8 or 11 Many(!) libraries Spring Initializr Access via Web or API 10

Slide 11

Slide 11 text

Specification Examples Server implementations Java 8 only No additional libraries Microprofile starter Still in beta 11

Slide 12

Slide 12 text

CONFIGURATION PROPERTIES

Slide 13

Slide 13 text

Configuration … 13 … Should not require the application to be repackaged … should provide Multiple mechanisms … should be changeable at runtime

Slide 14

Slide 14 text

Spring Boot - Config Type safe Configuration Classes 14 @Value("customerservice.uri") private String clientServiceUri; @Autowired private Environment env; String uri = env.getProperty("customerservice.uri"); 1 2

Slide 15

Slide 15 text

Spring Boot - Config Property Sources 15 1.Devtools global settings properties 2.@TestPropertySource annotations on your tests. 3.properties attribute on your tests. 4.Command line arguments. 5.Properties from SPRING_APPLICATION_JSON 6.ServletConfig init parameters. 7.ServletContext init parameters. 8.JNDI attributes from java:comp/env. 9.Java System properties 10.OS environment variables. 11.A RandomValuePropertySource that has properties only in random.*. 12.Profile-specific application properties outside of your packaged jar 13.Profile-specific application properties packaged inside your jar 14.Application properties outside of your packaged jar 15.Application properties packaged inside your jar 16.@PropertySource annotations on your @Configuration classes. 17.Default properties ORDINAL

Slide 16

Slide 16 text

Spring Boot - Config Write your own property source 16 public class MyPropertySource extends PropertySource> { Map myMap = new HashMap<>(); public MyPropertySource() { super("myProperties"); } @Override public Object getProperty(String name) { return myMap.get(name); } } 2 3 1

Slide 17

Slide 17 text

Spring Boot - Config WIRE your own property source 17 @Configuration public class PropertySourceConfig { @Autowired private ConfigurableEnvironment env; @Bean @Lazy(false) public MyPropertySource test() { MyPropertySource propertySource = new MyPropertySource(); env.getPropertySources().addBefore("systemProperties", propertySource); return propertySource; } } 1 2

Slide 18

Slide 18 text

Microprofile Config How to access the Config 18 @ConfigProperty(name = "customerservice.uri") // Injection Time private String clientServiceUri; @ConfigProperty(name = "customerservice.uri") // Just in time private Provider clientServiceUriProdiver; clientServiceUriProvider.get(); Config config = ConfigProvider.getConfig(); // Programmatically config.getValue("customerservice.uri", String.class); 1 2 3

Slide 19

Slide 19 text

Microprofile Config Config Sources 19 Static Properties File Environment Variables System Properties Your Own ConfigSource 400 300 100 ??? Ordinal

Slide 20

Slide 20 text

Microprofile Config Write your own ConfigSource 20 public class MyConfigSource implements ConfigSource { @Override public Map getProperties() {…} @Override public int getOrdinal() {…} @Override public String getValue(String propertyName) {…} @Override public String getName() {…} } 1 2 3 4 5

Slide 21

Slide 21 text

CALLING REST SERVICES

Slide 22

Slide 22 text

REST Clients … 22 … should be type safe … should require low configuration

Slide 23

Slide 23 text

Spring Boot - REST TEMPLATE Simple construction of clients 23 1 2 @Component public class CustomerClient { @Value("${customerservice.uri}") String resourceUrl; RestTemplate restTemplate; public CustomerClient() { this.restTemplate = new RestTemplate(); } public Customer findCustomerByNumber(String customerNumber) { String path = resourceUrl + "/" + customerNumber; ResponseEntity response = restTemplate.getForEntity(path, Customer.class); return response.getBody(); } }

Slide 24

Slide 24 text

Spring Boot - Feign Declarative Clients with feign 24 @FeignClient(name = "customers", url = "${customerservice.uri}") public interface CustomerService { @GetMapping(value = "/{customerNumber}", produces = "application/json") Customer findCustomerByNumber(@PathVariable("customerNumber") String customerNumber); @GetMapping(value = "/search", produces = "application/json") List searchCustomerByNumber(@RequestParam("searchString") String searchString); } 1 2 3

Slide 25

Slide 25 text

Microprofile - JSON-B Define the interface to be called 25 @Path("/customer") public interface CustomerService { @GET @Path("/{customerNumber}") @Produces("application/json") Customer findCustomerByNumber(@PathParam("customerNumber") String customerNumber); @GET @Path("/search") @Produces("application/json") List searchCustomerByNumber(@QueryParam("searchString") String searchString); } 1 2 3

Slide 26

Slide 26 text

Microprofile - JSON-B Build the REST Client 26 @ApplicationScoped public class CustomerClient { @Inject @ConfigProperty(name = "customerservice.uri") private URI clientServiceUri; @Produces public CustomerService build() { return RestClientBuilder.newBuilder() .baseUri(clientServiceUri) .build(CustomerService.class); } } 1 2

Slide 27

Slide 27 text

DIRECT COMPARISON

Slide 28

Slide 28 text

DIRECT COMPARISON Pretty similar 28 @FeignClient(name = "customers", url = "${customerservice.uri}") public interface CustomerService { @GetMapping(value = "/{customerNumber}", produces = "application/json") Customer findCustomerByNumber(@PathVariable("customerNumber") String customerNumber); @GetMapping(value = "/search", produces = "application/json") List searchCustomerByNumber(@RequestParam("searchString") String searchString); } 1 2 3

Slide 29

Slide 29 text

DIRECT COMPARISON Pretty similar 29 @Path("/customer") public interface CustomerService { @GET @Path= "/{customerNumber}") @Produces("application/json") Customer findCustomerByNumber(@PathParam("customerNumber") String customerNumber); @GET @Path("/search") @Produces("application/json") List searchCustomerByNumber(@QueryParam("searchString") String searchString); } 1 2 3

Slide 30

Slide 30 text

PROVIDING REST SERVICES

Slide 31

Slide 31 text

REST Services … 31 … should be type safe … should require low configuration … should provide documentation

Slide 32

Slide 32 text

Spring Boot - REST CONTROLLER Defining a REST Controller 32 @RestController @RequestMapping("/api/order") public class OrderController { @Autowired private OrderRepository orderRepository; @Autowired private CustomerService customerService; @PostMapping public ResponseEntity create(@RequestParam("customerNumber") String customerNumber, @RequestParam("orderText") String orderText) { Customer customer = customerService.findCustomerByNumber(customerNumber); Order order = new Order(customerNumber, orderText); orderRepository.createOrder(order); return ResponseEntity.created(constructCreationUri(order)).build(); } } 1 2 3 4

Slide 33

Slide 33 text

Spring Boot - REST CONTROLLER Swagger documentation 33 GET /v2/api-docs … "/api/order/{orderNumber}": { "get": { "tags": ["order-controller“], "summary": "getOrder", "operationId": "getOrderUsingGET", "consumes": [ "application/json"], "parameters": [ { "name": "orderNumber", "in": "path", "description": "orderNumber", "required": true, "type": "string" } ] …

Slide 34

Slide 34 text

Spring Boot - REST CONTROLLER Swagger documentation 34 GET /v2/api-docs … "/api/order/{orderNumber}": { "get": { "tags": ["order-controller“], "summary": "getOrder", "operationId": "getOrderUsingGET", "consumes": [ "application/json"], "parameters": [ { "name": "orderNumber", "in": "path", "description": "orderNumber", "required": true, "type": "string" } ] … Library needed! io.springfox springfox-swagger2

Slide 35

Slide 35 text

Microprofile - JAX-RS Defining a REST Controller 35 @Path("/complaint") public class ComplaintController { @Inject private ComplaintRepository repository; @Inject private CustomerService customerService; @POST public Response create(@QueryParam("caption") String caption, @QueryParam("content") String content, @QueryParam("customerNumber") String customerNumber) { Customer customer = customerService.findCustomerByNumber(customerNumber); Complaint complaint = repository.createComplaint(caption, content, customer); return Response.created(this.constructCreationUri(complaint)).build(); } … } 1 2 3 4

Slide 36

Slide 36 text

Microprofile - JAX-RS OPEN API Support 36 GET /openapi { "openapi": "3.0.1", "info": { "title": "Generated API", "version": "1.0" }, "paths": { "/api/complaint": { "get": { "responses": { "200": { "description": "OK", "content": { "*/*": {} } } …

Slide 37

Slide 37 text

DIRECT COMPARISON

Slide 38

Slide 38 text

DIRECT COMPARISON PRETTY SIMILAR 38 @RestController @RequestMapping("/api/order") public class OrderController { @Autowired private OrderRepository orderRepository; @Autowired private CustomerService customerService; @PostMapping public ResponseEntity create(@RequestParam("customerNumber") String customerNumber, @RequestParam("orderText") String orderText) { Customer customer = customerService.findCustomerByNumber(customerNumber); Order order = new Order(customerNumber, orderText); orderRepository.createOrder(order); return ResponseEntity.created(constructCreationUri(order)).build(); } } 1 2 3 4

Slide 39

Slide 39 text

DIRECT COMPARISON PRETTY SIMILAR 39 @Path("/api/order") public class OrderController { @Inject private OrderRepository orderRepository; @Inject private CustomerService customerService; @POST public Response create(@QueryParam("customerNumber") String customerNumber, @QueryParam("orderText") String orderText) { Customer customer = customerService.findCustomerByNumber(customerNumber); Order order = new Order(customerNumber, orderText); orderRepository.createOrder(order); return Response.created(constructCreationUri(order)).build(); } … } 1 3 4 2

Slide 40

Slide 40 text

RESILIENCE MEASURES

Slide 41

Slide 41 text

RESILIENCE … 41 … should react on different error scenarios … should enable the developer to provide fallbacks

Slide 42

Slide 42 text

Hystrix Resilience4j Spring Boot – Fault Tolerance An overview 42

Slide 43

Slide 43 text

Spring Boot – Hystrix Application 43 @Component public class CustomerClient { @Value("${customerservice.uri}") String resourceUrl; RestTemplate restTemplate; public CustomerClient() { this.restTemplate = new RestTemplate(); } @HystrixCommand(raiseHystrixExceptions = HystrixException.RUNTIME_EXCEPTION) public Customer findCustomerByNumber(String customerNumber) { String path = resourceUrl + "/" + customerNumber; ResponseEntity response = restTemplate.getForEntity(path, Customer.class); return response.getBody(); } } 1

Slide 44

Slide 44 text

Retry Try and call the same method again Rate Limiter Define the number of calls per timeframe Fallback React to an outage with a fallback Circuitbreaker Let services fail fast and don’t call them again Bulkhead Limit concurrent requests Spring Boot – Resilience4j Mechanisms 44

Slide 45

Slide 45 text

Spring Boot – Fault Tolerance Resilience4j – Circuit breaker 45 @RestController @RequestMapping("/api/order") public class OrderController { @Autowired private CustomerClient customerClient; @Autowired private CircuitBreakerConfig cbConfig; private Function clientCall; @PostConstruct public void init() { CircuitBreaker circuitBreaker = CircuitBreakerRegistry.of(cbConfig).circuitBreaker("customer-client"); clientCall = CircuitBreaker.decorateFunction(circuitBreaker, customerClient::findCustomerByNumber); } private Customer findCustomer(String customerNumber) { return clientCall.apply(customerNumber); } 1 2 3

Slide 46

Slide 46 text

Spring Boot – Fault Tolerance Resilience4j – Circuit breaker 46 1 @Component public class CustomerClient { @Value("${customerservice.uri}") String resourceUrl; RestTemplate restTemplate; public CustomerClient() { this.restTemplate = new RestTemplate(); } @CircuitBreaker(name = "customer-cb") @RateLimiter(name = "customer-limiter") public Customer findCustomerByNumber(String customerNumber) { String path = resourceUrl + "/" + customerNumber; ResponseEntity response = restTemplate.getForEntity(path, Customer.class); return response.getBody(); } }

Slide 47

Slide 47 text

Spring Boot – Fault Tolerance Resilience4j – Circuit breaker 47 1 @Component public class CustomerClient { @Value("${customerservice.uri}") String resourceUrl; RestTemplate restTemplate; public CustomerClient() { this.restTemplate = new RestTemplate(); } @CircuitBreaker(name = "customer-cb") @RateLimiter(name = "customer-limiter") public Customer findCustomerByNumber(String customerNumber) { String path = resourceUrl + "/" + customerNumber; ResponseEntity response = restTemplate.getForEntity(path, Customer.class); return response.getBody(); } } Library needed! io.github.resilience4j resilience4j-spring-boot2 0.13.2

Slide 48

Slide 48 text

Spring Boot – Fault Tolerance Resilience4j – Circuit breaker 48 1 @Component public class CustomerClient { @Value("${customerservice.uri}") String resourceUrl; RestTemplate restTemplate; public CustomerClient() { this.restTemplate = new RestTemplate(); } @CircuitBreaker(name = "customer-cb") @RateLimiter(name = "customer-limiter") public Customer findCustomerByNumber(String customerNumber) { String path = resourceUrl + "/" + customerNumber; ResponseEntity response = restTemplate.getForEntity(path, Customer.class); return response.getBody(); } } Library needed! io.github.resilience4j resilience4j-spring-boot2 0.13.2 BUT WAIT! https://github.com/spring-cloud- incubator/spring-cloud-circuitbreaker

Slide 49

Slide 49 text

Retry Try and call the same method again Timeout Define a maximum wait time Fallback React to an outage with a fallback Circuitbreaker Let services fail fast and don’t call them again Bulkhead Limit concurrent requests Microprofile - FAULT TOLERANCE Mechanisms 49

Slide 50

Slide 50 text

Microprofile - FAULT TOLERANCE Example with Timeout, Circuit breaker and fallback 50 @Path("/customer") public interface CustomerService { @GET @Path("/{customerNumber}") @Produces("application/json") @Timeout(value = 500, unit = ChronoUnit.MILLIS) @CircuitBreaker(requestVolumeThreshold = 5, failureRatio = 0.25, delay = 5000L, successThreshold = 1) @Fallback(CustomerFallbackHandler.class) Customer findCustomerByNumber(@PathParam("customerNumber") String customerNumber); } 1 2 3 4

Slide 51

Slide 51 text

HEALTH CHECK

Slide 52

Slide 52 text

Health Check … 52 … should be accessible via HTTP … should indicate health status to K8s … should expose complex health conditions

Slide 53

Slide 53 text

Spring – Actuator Health Constructing the health endpoint 53 @Component public class HealthCheck implements HealthIndicator { @Override public Health health() { Status status = (jdbcConnectionIsUp() && amqpIsConnected()) ? Status.UP : Status.DOWN; return Health .status(status) .withDetail("datapoint", "my-app-data") .build(); } } 2 3 1

Slide 54

Slide 54 text

Spring – Actuator Health Payload 54 GET /actuator/health { "status": "UP", "details": { "healthCheck": { "status": "UP", "details": { "datapoint": "my-app-data" } }, … } }

Slide 55

Slide 55 text

Microprofile Health Constructing the Health Endpoint 55 @Health @ApplicationScoped public class ServiceHealthCheck implements HealthCheck { @Override public HealthCheckResponse call() { return HealthCheckResponse.named(ServiceHealthCheck.class.getSimpleName()) .withData(“datapoint", “my-app-data") .state(jdbcConnectionIsUp() && amqpIsConnected()) .build(); } } 1 2 3 4

Slide 56

Slide 56 text

Microprofile Health PAYLOAD 56 GET /health { "outcome": "UP", "checks": [ { "name": "ServiceHealthCheck", "state": "UP", "data": { “datapoint": “my-app-data" } } ] }

Slide 57

Slide 57 text

DIRECT COMPARISON

Slide 58

Slide 58 text

DIRECT COMPARISON QUITE SIMILAR 58 GET /actuator/health { "status": "UP", "details": { "healthCheck": { "status": "UP", "details": { "datapoint": "my-app-data" } }, … } }

Slide 59

Slide 59 text

DIRECT COMPARISON QUITE SIMILAR 59 GET /health { "outcome": "UP", "checks": [ { "name": "ServiceHealthCheck", "state": "UP", "data": { “datapoint": “my-app-data" } } ] }

Slide 60

Slide 60 text

METRICS

Slide 61

Slide 61 text

METRICS … 61 … should be accessible via HTTP … should be integrated with monitoring solutions … should expose complex metrics

Slide 62

Slide 62 text

Spring – Actuator Metrics query metrics via HTTP 62 GET /actuator/metrics/hystrix.circuit.breaker.open { "name": "hystrix.circuit.breaker.open", "description": null, "baseUnit": null, "measurements": [ { "statistic": "VALUE", "value": 1 } ], "availableTags": […] }

Slide 63

Slide 63 text

Spring – Actuator Metrics Add your own metrics 63 @Component public class OrderRepository { private Counter counter; private List orders = new ArrayList<>(); @Autowired public OrderRepository(MeterRegistry registry) { this.counter = registry.counter("metric.business.order.created"); } public Order createOrder(Order order) { this.orders.add(order); counter.increment(); return order; } } 1 2

Slide 64

Slide 64 text

Spring – Actuator Metrics query metrics via HTTP 64 GET /actuator/metrics/metric.business.order.created { "name": "metric.business.order.created", "description": null, "baseUnit": null, "measurements": [ { "statistic": "COUNT", "value": 14 } ], "availableTags": [] }

Slide 65

Slide 65 text

Microprofile METRICS BASE 65 GET /metrics/base { "classloader.totalLoadedClass.count": 14849, "cpu.systemLoadAverage": 2.57568359375, "thread.count": 83, "classloader.currentLoadedClass.count": 14844, "jvm.uptime": 40513, "gc.PS MarkSweep.count": 3, "memory.committedHeap": 1216348160, "thread.max.count": 90, "gc.PS Scavenge.count": 12, "cpu.availableProcessors": 8, "thread.daemon.count": 14, "classloader.totalUnloadedClass.count": 5, "memory.maxHeap": 3817865216, "memory.usedHeap": 892483384, "gc.PS MarkSweep.time": 367, "gc.PS Scavenge.time": 229 }

Slide 66

Slide 66 text

Microprofile METRICS Application 66 GET /metrics/application { "ft.{…}.CustomerService.findCustomerByNumber.circuitbreaker.open.total": 0, "ft.{…}CustomerService.findCustomerByNumber.circuitbreaker.closed.total": 16354121928, "ft.{…}.CustomerService.findCustomerByNumber.invocations.total": 13, "ft.{…}.CustomerService.findCustomerByNumber.circuitbreaker.halfOpen.total": 0, "ft.{…}.CustomerService.findCustomerByNumber.fallback.calls.total": 12, "ft.{…}.CustomerService.findCustomerByNumber.circuitbreaker.callsSucceeded.total": 13, "ft.{…}.CustomerService.findCustomerByNumber.timeout.callsNotTimedOut.total": 13, {…} }

Slide 67

Slide 67 text

Microprofile METRICS Getting the units 67 OPTIONS /metrics/base/jvm.uptime { "jvm.uptime": { "unit": "milliseconds", "type": "gauge", "description": "Displays the start time of the Java virtual machine in milliseconds. This attribute displays the approximate time when the Java virtual machine started.", "displayName": "JVM Uptime", "tags": "" } }

Slide 68

Slide 68 text

Microprofile METRICS Add your own 68 1 2 @ApplicationScoped public class ComplaintRepository { @Inject @Metric(name = "metric.business.complaint.created") private Counter counter; private List complaints = new ArrayList<>(); public Complaint createComplaint(String caption, String text, Customer customer) { Complaint complaint = new Complaint(caption, text, customer.getCustomerNumber()); complaints.add(complaint); counter.inc(); return complaint; } }

Slide 69

Slide 69 text

Something is missing… ?

Slide 70

Slide 70 text

Security? HARD TO COMPARE 70 MP JWT SPRING SECURITY ALL IN ONE JWT ONLY

Slide 71

Slide 71 text

Summary

Slide 72

Slide 72 text

Matthews impressions Building a new service with spring boot 72 „I was able to complete all requirements. I liked the huge ecosystem that I could access, if I needed to. I spent some time searching for spring cloud dependencies. Openapi docs and fault tolerance needed external libraries“

Slide 73

Slide 73 text

Sarahs impressions Building a new service with microprofile 73 „I was able to complete all requirements. I liked that everything could be solved with the microprofile API and the huge variety of fault tolerance methods. The startup time and the ‚Getting started‘ webpage could be better“

Slide 74

Slide 74 text

Helens Impressions Running both services in production 74 „Both services are working as expected and were completed in roughly the same time. Operations is able to configure them in Kubernetes and set up monitoring with prometheus. I am not able to tell a difference.“

Slide 75

Slide 75 text

Tims Impressions ;) After working with both 75 „Both approaches are viable. Spring boot has the bigger ecosystem, It can do more. Microprofile has fresh ideas based on proven methods. Microprofiles API approach with different implementations is a blessing and a curse.“

Slide 76

Slide 76 text

Questions?