View
2.733
Download
4
Category
Preview:
Citation preview
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
Recommended