building distributed systems with netflix oss and spring cloud

Post on 14-Jul-2015

2.733 Views

Category:

Software

4 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Building Distributed Systems with

Netflix OSSandSpring Cloud© 2015 Matt Stine 1

MeMatt Stine (@mstine)Senior Product ManagerPivotalhttp://www.mattstine.commatt.stine@gmail.com

© 2015 Matt Stine 2

There Seems to Be Some Hype...

© 2015 Matt Stine 3

Define: Microservice“Loosely coupled service oriented architecture with bounded contexts...”Adrian Cockcroft

© 2015 Matt Stine 4

Spring BootA Microframework for Microservices

© 2015 Matt Stine 5

It Can Get Pretty Small...@RestControllerclass ThisWillActuallyRun { @RequestMapping("/") String home() { "Hello World!" }}

© 2015 Matt Stine 6

DEMO© 2015 Matt Stine 7

With Spring Data REST!@Entity@Table(name = "city")public class City implements Serializable {

@Id @GeneratedValue private Long id;

@Column(nullable = false) private String name;

@Column(nullable = false) private String county;

//...

}

© 2015 Matt Stine 8

With Spring Data REST!@RepositoryRestResource(collectionResourceRel = "cities", path = "cities")public interface CityRepository extends PagingAndSortingRepository<City, Long> {}

© 2015 Matt Stine 9

With Spring Data REST!@SpringBootApplication@EnableJpaRepositories@Import(RepositoryRestMvcConfiguration.class)public class Application {

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

© 2015 Matt Stine 10

With Spring Data REST!{ "_links" : { "next" : { "href" : "http://localhost:8080/cities?page=1&size=20" }, "self" : { "href" : "http://localhost:8080/cities{?page,size,sort}", "templated" : true } }, "_embedded" : { "cities" : [ { "name" : "HOLTSVILLE", "county" : "SUFFOLK", "stateCode" : "NY", "postalCode" : "00501", "latitude" : "+40.922326", "longitude" : "-072.637078",

© 2015 Matt Stine 11

DEMO© 2015 Matt Stine 12

Writing a Single Service is

Nice...© 2015 Matt Stine 13

But No Microservice

is an Island© 2015 Matt Stine 14

Challenges of Distributed Systems» Configuration Management

» Service Registration & Discovery

» Routing & Load Balancing

» Fault Tolerance (Circuit Breakers!)

» Monitoring

» Concurrent API Aggregation & Transformation

© 2015 Matt Stine 15

© 2015 Matt Stine 16

Spring CloudDistributed System Patterns FTW!

© 2015 Matt Stine 17

ConfigurationManagement© 2015 Matt Stine 18

Spring Environment» Properties

» Profiles

© 2015 Matt Stine 19

app.groovy@RestControllerclass BasicConfig {

@Value('${greeting}') String greeting

@RequestMapping("/") String home() { "${greeting} World!" }}

© 2015 Matt Stine 20

application.ymlgreeting: Hello

© 2015 Matt Stine 21

DEMO© 2015 Matt Stine 22

Boot Priority1.Command Line Args

2.JNDI

3.Java System Properties

4.OS Environment Variables

5.Properties Files

6.@PropertySource

7.Defaults

© 2015 Matt Stine 23

DEMO© 2015 Matt Stine 24

Profiles© 2015 Matt Stine 25

application.ymlgreeting: Hello

---

spring: profiles: spanishgreeting: Hola

© 2015 Matt Stine 26

DEMO© 2015 Matt Stine 27

Distributed?

© 2015 Matt Stine 28

ConfigServer!

© 2015 Matt Stine 29

Config Server app.groovy@Grab("org.springframework.cloud:spring-cloud-starter-bus-amqp:1.0.0.RC1")@Configuration@EnableAutoConfiguration@EnableConfigServerclass ConfigServer {}

© 2015 Matt Stine 30

Config Server application.ymlserver: port: 8888

spring: cloud: config: server: git: uri: https://github.com/mstine/config-repo.git

© 2015 Matt Stine 31

https://github.com/mstine/config-repo/blob/master/demo.ymlgreeting: Bonjour

© 2015 Matt Stine 32

Config Client app.groovy@Grab("org.springframework.cloud:spring-cloud-starter-bus-amqp:1.0.0.RC1")@RestControllerclass BasicConfig {

@Autowired Greeter greeter

@RequestMapping("/") String home() { "${greeter.greeting} World!" }}

@Component@RefreshScopeclass Greeter {

@Value('${greeting}') String greeting

}

© 2015 Matt Stine 33

Config Client bootstrap.ymlspring: application: name: demo

© 2015 Matt Stine 34

DEMO© 2015 Matt Stine 35

Cloud

Bus!© 2015 Matt Stine 36

curl -X POST http://localhost:8888/bus/refresh

© 2015 Matt Stine 37

DEMO© 2015 Matt Stine 38

ServiceRegistration &

Discovery© 2015 Matt Stine 39

Eureka© 2015 Matt Stine 40

ProducerConsumer© 2015 Matt Stine 41

Eureka Service Registry@GrabExclude("ch.qos.logback:logback-classic")@EnableEurekaServerclass Eureka {}

© 2015 Matt Stine 42

Producer@EnableDiscoveryClient@RestControllerpublic class Application {

int counter = 0

@RequestMapping("/") String produce() { "{\"value\": ${counter++}}" }}

© 2015 Matt Stine 43

Consumer@EnableDiscoveryClient@RestControllerpublic class Application {

@Autowired DiscoveryClient discoveryClient

@RequestMapping("/") String consume() { InstanceInfo instance = discoveryClient.getNextServerFromEureka("PRODUCER", false)

RestTemplate restTemplate = new RestTemplate() ProducerResponse response = restTemplate.getForObject(instance.homePageUrl, ProducerResponse.class)

"{\"value\": ${response.value}" }}

public class ProducerResponse { Integer value}

© 2015 Matt Stine 44

DEMO© 2015 Matt Stine 45

Routing &Load Balancing© 2015 Matt Stine 46

Ribbon© 2015 Matt Stine 47

Consumer with Load Balancer@AutowiredLoadBalancerClient loadBalancer

@RequestMapping("/")String consume() { ServiceInstance instance = loadBalancer.choose("producer") URI producerUri = URI.create("http://${instance.host}:${instance.port}");

RestTemplate restTemplate = new RestTemplate() ProducerResponse response = restTemplate.getForObject(producerUri, ProducerResponse.class)

"{\"value\": ${response.value}"}

© 2015 Matt Stine 48

DEMO© 2015 Matt Stine 49

Consumer with Ribbon-enabled RestTemplate@AutowiredRestTemplate restTemplate

@RequestMapping("/")String consume() { ProducerResponse response = restTemplate.getForObject("http://producer", ProducerResponse.class)

"{\"value\": ${response.value}"}

© 2015 Matt Stine 50

DEMO© 2015 Matt Stine 51

Feign Client@FeignClient("producer")public interface ProducerClient {

@RequestMapping(method = RequestMethod.GET, value = "/") ProducerResponse getValue();}

© 2015 Matt Stine 52

Consumer with Feign Client@SpringBootApplication@FeignClientScan@EnableDiscoveryClient@RestControllerpublic class Application {

@Autowired ProducerClient client;

@RequestMapping("/") String consume() { ProducerResponse response = client.getValue();

return "{\"value\": " + response.getValue() + "}"; }

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

© 2015 Matt Stine 53

Demo© 2015 Matt Stine 54

FaultTolerance© 2015 Matt Stine 55

Hystrix© 2015 Matt Stine 56

Circuit Breaker

© 2015 Matt Stine 57

Consumer app.groovy@EnableDiscoveryClient@EnableCircuitBreaker@RestControllerpublic class Application {

@Autowired ProducerClient client

@RequestMapping("/") String consume() { ProducerResponse response = client.getProducerResponse()

"{\"value\": ${response.value}" }

}

© 2015 Matt Stine 58

Producer Client@Componentpublic class ProducerClient {

@Autowired RestTemplate restTemplate

@HystrixCommand(fallbackMethod = "getProducerFallback") ProducerResponse getProducerResponse() { restTemplate.getForObject("http://producer", ProducerResponse.class) }

ProducerResponse getProducerFallback() { new ProducerResponse(value: 42) }}

© 2015 Matt Stine 59

Demo© 2015 Matt Stine 60

Monitoring© 2015 Matt Stine 61

DEMOhttp://localhost:8082/

© 2015 Matt Stine 62

HystrixDashboard© 2015 Matt Stine 63

Hystrix Dashboard

© 2015 Matt Stine 64

Hystrix Dashboard@Grab("org.springframework.cloud:spring-cloud-starter-hystrix-dashboard:1.0.0.RC1")

import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard

@EnableHystrixDashboardclass HystrixDashboard {}

© 2015 Matt Stine 65

Demo© 2015 Matt Stine 66

ConcurrentAPIAggregation &Transformation© 2015 Matt Stine 67

RxJava© 2015 Matt Stine 68

Movie Catalog Service@RequestMapping(value = "/catalog/movies/{mlId}", method = RequestMethod.GET)public Movie movie(@PathVariable String mlId) { return movieRepository.findByMlId(mlId);}

© 2015 Matt Stine 69

Movie Catalog Service{ id: 1001, title: "GoldenEye (1995)", mlId: "2", genres: [ { id: 1001, mlId: "1", name: "Action" }, { id: 1002, mlId: "2", name: "Adventure" }, { id: 1016, mlId: "16", name: "Thriller" } ]}

© 2015 Matt Stine 70

Movie Review Service@RequestMapping(value = "/reviews/reviews/{mlId}", method = RequestMethod.GET)public Iterable<Review> reviews(@PathVariable String mlId) { return reviewRepository.findByMlId(mlId);}

© 2015 Matt Stine 71

Movie Review Service[{ id: "54b85cbe004e0464177e90e4", mlId: "2", userName: "mstine", title: "GoldenEye (1995)", review: "Pretty good...", rating: 3},{ id: "54b85cbe004e0464177e90e5", mlId: "2", userName: "starbuxman", title: "GoldenEye (1995)", review: "BOND BOND BOND!", rating: 5},{ id: "54b85cbf004e0464177e90e8", mlId: "2", userName: "littleidea", title: "GoldenEye (1995)", review: "Good show!", rating: 4}]

© 2015 Matt Stine 72

Movie Recommendations Servicepublic interface MovieRepository extends GraphRepository<Movie> { Movie findByMlId(String mlId);

@Query("MATCH (movie:Movie) WHERE movie.mlId = {0} MATCH movie<-[:LIKES]-slm-[:LIKES]->recommendations " + "RETURN distinct recommendations") Iterable<Movie> moviesLikedByPeopleWhoLiked(String mlId);}

© 2015 Matt Stine 73

Movie Recommendations Service@RequestMapping(value = "/recommendations/forMovie/{mlId}", method = RequestMethod.GET)public Iterable<Movie> recommendedMoviesForMovie(@PathVariable String mlId) { return movieRepository.moviesLikedByPeopleWhoLiked(mlId);}

© 2015 Matt Stine 74

Movie Recommendations Service@RequestMapping(value = "/recommendations/forMovie/{mlId}", method = RequestMethod.GET)public Iterable<Movie> recommendedMoviesForMovie(@PathVariable String mlId) { return movieRepository.moviesLikedByPeopleWhoLiked(mlId);}

© 2015 Matt Stine 75

Movie Recommendations Service[{ id: 6, mlId: "1", title: "Toy Story (1995)"},{ id: 1, mlId: "4", title: "Get Shorty (1995)"},{ id: 2, mlId: "5", title: "Copycat (1995)"},{ id: 0, mlId: "3", title: "Four Rooms (1995)"}]

© 2015 Matt Stine 76

APIGateway© 2015 Matt Stine 77

Catalog Integration Service@Servicepublic class CatalogIntegrationService {

@Autowired RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "stubMovie") public Observable<Movie> getMovie(final String mlId) { return new ObservableResult<Movie>() { @Override public Movie invoke() { return restTemplate.getForObject("http://catalog-service/catalog/movies/{mlId}", Movie.class, mlId); } }; }

private Movie stubMovie(final String mlId) { Movie stub = new Movie(); stub.setMlId(mlId); stub.setTitle("Interesting...the wrong title. Sssshhhh!"); return stub; }}

© 2015 Matt Stine 78

Reviews Integration Service@Servicepublic class ReviewsIntegrationService {

@Autowired RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "stubReviews") public Observable<List<Review>> reviewsFor(String mlId) { return new ObservableResult<List<Review>>() { @Override public List<Review> invoke() { ParameterizedTypeReference<List<Review>> responseType = new ParameterizedTypeReference<List<Review>>() { }; return restTemplate.exchange("http://reviews-service/reviews/reviews/{mlId}", HttpMethod.GET, null, responseType, mlId).getBody(); } }; }

private List<Review> stubReviews(String mlId) { Review review = new Review(); review.setMlId(mlId); review.setRating(4); review.setTitle("Interesting...the wrong title. Sssshhhh!"); review.setReview("Awesome sauce!"); review.setUserName("joeblow"); return Arrays.asList(review); }

}

© 2015 Matt Stine 79

Recommendations Integration Service@Servicepublic class RecommendationsIntegrationService { @Autowired RestTemplate restTemplate;

@HystrixCommand(fallbackMethod = "stubRecommendations") public Observable<List<Movie>> getRecommendations(final String mlId) { return new ObservableResult<List<Movie>>() { @Override public List<Movie> invoke() { ParameterizedTypeReference<List<Movie>> responseType = new ParameterizedTypeReference<List<Movie>>() { }; return restTemplate.exchange("http://recommendations-service/recommendations/forMovie/{mlId}", HttpMethod.GET, null, responseType, mlId).getBody(); } }; }

private List<Movie> stubRecommendations(final String mlId) { Movie one = new Movie(); one.setMlId("25"); one.setMlId("A movie which doesn't exist"); Movie two = new Movie(); two.setMlId("26"); two.setMlId("A movie about nothing"); return Arrays.asList(one, two); }}

© 2015 Matt Stine 80

Concurrently Aggregate and Transform@RequestMapping("/movie/{mlId}")public DeferredResult<MovieDetails> movieDetails(@PathVariable String mlId) { Observable<MovieDetails> details = Observable.zip(

catalogIntegrationService.getMovie(mlId), reviewsIntegrationService.reviewsFor(mlId), recommendationsIntegrationService.getRecommendations(mlId),

(movie, reviews, recommendations) -> { MovieDetails movieDetails = new MovieDetails(); movieDetails.setMlId(movie.getMlId()); movieDetails.setTitle(movie.getTitle()); movieDetails.setReviews(reviews); movieDetails.setRecommendations(recommendations); return movieDetails; }

); return toDeferredResult(details);}

© 2015 Matt Stine 81

Demo© 2015 Matt Stine 82

Thanks!Matt Stine (@mstine)

» Spring Cloud: http://cloud.spring.io

» This Presentation: https://github.com/mstine/nfjs_2015/tree/master/DistributedSystemsWithSpringCloud

» SpringBox-Cloud: https://github.com/mstine/microservices-lab/tree/master/springbox-cloud

© 2015 Matt Stine 83

Image Credits» http://i.imgur.com/atz81.jpg

» http://theroomermill.net/wp-content/uploads/2014/06/island-house.jpg

» Circuit Breaker: Nygard, Michael. Release It!

© 2015 Matt Stine 84

top related