Giter Club home page Giter Club logo

spring-cloud-workshop-1's Introduction

#Labs for Spring Boot and Spring Cloud Welcome to Pivotal Cloud Native Workshop, we will use this repository for Spring Boot and Spring Cloud labs.

###Prerequisites

  1. CF command line tools installed and login
  2. Get a free PWS account, we don't mind you swipe your credit card too :)
  3. CURL or any browser-based REST client like this
  4. JDK 1.8

To save your time and resources, Config server, Eureka, Hystrix Dashboard and Zipkin have been always setup and configurated in both provider and consumer projects.

To set the proxy, please add below to mvnw or mvnw.cmd

set MAVEN_OPTS=-Dhttp.proxyHost=proxy.ha.org.hk -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.ha.org.hk -Dhttps.proxyPort=8080โ€‹ -Dproxy.user=user -Dproxy.password=pass

##Lab 1 - create a simple backend microservice, let's call it a provider app, with a database, a table, and RESTful API

  1. Open 01-provider-init with your favourite IDE

  2. Take a look on pom.xml, you will find below dependencies:

  • spring-boot-starter-actuator for metrics, configuration, jmx, etc
  • spring-cloud-starter-eureka for service discovery
  • spring-boot-starter-data-rest to quickly expose RESTful API
  • spring-cloud-starter-sleuth for distributed tracing
  • spring-cloud-starter-zipkin for sending trace to zipkin server
  • mysql, h2
  1. Add below annotation in the main class

    @EnableDiscoveryClient // to register and be discoverable by others
  2. Add a JPA Entity class named Contact with below attributes and a no argument constructor, I'm using @Data from Lombok below, to make the code cleaner.

    @Entity // JPA
    @Data @NoArgsConstructor // Lombok
    class Contact{
    	@Id @GeneratedValue // simple JPA staff
    	Long id;
    	String name;
    	String address;
    }
  3. Add an @RestResource annotated interface extends JpaRepository to store the Contact class with a Long typed key

    @RestResource // Standard JPA staff except this annotation. It saves you a lot of effort to create RESTful API. Thanks Spring Data Rest!
    interface ContactRepository extends JpaRepository<Contact, Long>{
      // if you are familiar with JPA, feel free to add more method here, for example findAllByName. 
      // it will be appeared in http://your-consumer.cfapps.io/contacts/search
    }
  4. To avoid hostname conflict, in manifest.yml, replace the host with your name, for example

    host: cna-provider-dwong
  5. package the app (mvnw.cmd for windows)

    ./mvnw clean package 
  6. Create an User Provided Service by

    cf cups eureka-service -p '{"uri":"http://cna-eureka.cfapps.io"}'
  7. run cf push to push the app (cf cli read the content in manifest.yml, save you a bit of typing)

  8. Check the Apps Manager

  9. Check registry to see if your app is registered

    Image of Eureka

  10. try /contacts endpoint

  11. You can create a new record with HTTP POST to the same endpoint

    curl -X POST your-host.cfapps.io/contacts -H 'Content-type: application/json' -d '{"name":"any name", "address":"any address"}'
  12. ADVANCE: The app is using an embedded H2 database, you may also try to create a MySQL database from marketplace and bind to the app.

    cf create-service cleardb spark contact-db
    cf bind-service cna-provider contact-db
    cf restart cna-provider
  13. Do you notice that it is not necessary to specify any host/port/credential of the database? The action bind-service does all of those, give you a 'It just works!' experience.


##Lab 2 - create another service to consume above microservice, let's call it consumer app

  1. Open 02-consumer-init

  2. Take a look on pom.xml, you can find below dependencies besides Lab 1's

  • spring-cloud-starter-hystrix for circuit breaker
  • spring-boot-starter-hateoas for parsing HATEOAS response
  • spring-cloud-starter-feign for easily consume provider service
  1. Add below annotations

    @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL) // because of HATEOAS format
    @EnableFeignClients // because we will use Feign Client to consume the provider
    @EnableCircuitBreaker // because we will use circuit breaker to protect our consumer app
    @EnableDiscoveryClient // well, because we will lookup the provider app from registry
    // that's all
  2. Add Contact class with the same attributes as provider (Yes, in microservice architecture, you may need duplicating class definition. However, this give you a lot more flexibility, imagine a new attribute added in provider but you don't need in your consumer app, guess what? Nothing need to be done, we call it tolerence reader pattern!. Thinking your inhouse SOA?)

  3. Add Feign Client to integrate with provider

    // The name within FeignClient is the service name registered in Eureka, 
    // Feign Client will lookup the address(es) of provider.
    // Imagine there are multiple instances of provider sitting in multiple data center/cloud,
    // the client side load balancer (Ribbon) select the best one to consume.
    // If the circuit is open, the fallback class will be invoked.
    // Circuit open means something is wrong, close is normal, remember science class in sec school?
    // Not just try..catch..exception
    @FeignClient(name = "cna-provider", fallback = RemoteServiceFallback.class)
    interface RemoteService{
    
        // The /contacts request mapping below is the HTTP endpoint of provider app
        // Feign Client expect Resources of Contact from this endpoint 
    	@RequestMapping("/contacts")
    	Resources<Contact> getAllContacts(); 
    }
  4. Implement a fallback

    // Fallback implementation is easy, 
    // just create a class and implemnt the Feign Client interface, make sense?!
    @Component
    class RemoteServiceFallback implements RemoteService{
    	@Override
    	public Resources<Contact> getAllContacts() {
    	
    	    // just simple default thing here, 
    	    // but you can do a lot more to gracefully degrade a service
    		Contact defaultContact = new Contact();
    		defaultContact.setAddress("default");
    		defaultContact.setName("default");
    		defaultContact.setId(0L);
    		return new Resources<>(Arrays.asList(defaultContact));
    	}
    }
  5. Add an RestController and an method to consume the provider service

    @RestController
    class SimpleController{
    
        // Thanks for Spring Boot autowiring capability, get you out of the XML hell.    
    	@Autowired RemoteService remoteService;
    	
    	// Map this method to HTTP endpoint /names
    	@GetMapping("/names")
    	public List<String> getAllNames(){
    	    // Now, really consume the provider service by calling the Feign Client method
    	    // And you don't need to worry about cascade failure
    	    // Because this service is protected by circuit breaker!
    		return remoteService.getAllContacts()
    				.getContent()
    				.stream()
    				.map(c -> c.getName()) // simple Java 8 stream
    				.collect(Collectors.toList());
    	}
    
    }
  6. To avoid hostname conflict, in manifest.yml, replace the host with your name, for example

    host: cna-consumer-dwong
  7. package the app (mvnw.cmd for windows)

    ./mvnw clean package
  8. run cf push to push the app

  9. check registry to see if your app is registered

  10. Open Hystrix dashboard here

  11. Enter your Hystrix stream of this consumer app, for example

    http://cna-provider-dwong.cfapps.io/hystrix.stream
    
  12. You should see the Hystrix dashboard

    Image of Hystrix

  13. Create some requests to the /names endpoint and check the Hystrix dashboard again

  14. Stop the provider app and continue sending request to /names endpoint to see the response and Hystrix dashboard

  15. Restart provide app to see what happen


##Lab 3 - Distrubuted Tracing

  1. add a sampler in main class of both provider and consumer

    @Bean
    public AlwaysSampler defaultSampler() {
      // use always sampler here, but in production environment you want not want to keep every single trace 
      return new AlwaysSampler();
    }
  2. re-package and push for both provider and consumer

    ./mvnw clean package && cf push 
  3. Create request to consumer /names endpoint

  4. Take a look at Zipkin

    Image of Zipkin


Remarks:

spring-cloud-workshop-1's People

Contributors

derrickchwong avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.