├── README.md ├── code ├── config-service │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ConfigServiceApplication.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── ConfigServiceApplicationTests.java ├── eureka-service │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── EurekaServiceApplication.java │ │ └── resources │ │ │ └── bootstrap.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── EurekaServiceApplicationTests.java ├── hystrix-dashboard │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── HystrixDashboardApplication.java │ │ └── resources │ │ │ └── bootstrap.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── HystrixDashboardApplicationTests.java ├── reservation-client │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReservationClientApplication.java │ │ └── resources │ │ │ └── bootstrap.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── ReservationClientApplicationTests.java ├── reservation-service │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReservationServiceApplication.java │ │ └── resources │ │ │ ├── banner.txt │ │ │ └── bootstrap.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── ReservationServiceApplicationTests.java └── zipkin-service │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── ZipkinServiceApplication.java │ └── resources │ │ └── bootstrap.properties │ └── test │ └── java │ └── com │ └── example │ └── ZipkinServiceApplicationTests.java └── images ├── git-config-repo.png ├── graphite.png ├── hystrix-dashboard.png ├── spring-cloud-netflix-eureka.png └── zipkin-traces.png /README.md: -------------------------------------------------------------------------------- 1 | # Microservices 2 | by [Josh Long](http://twitter.com/starbuxman) 3 | 4 | 5 | ## Survival is Not Mandatory 6 | > It is not necessary to change. Survival is not mandatory. -W. Edwards Deming 7 | 8 | In today's hypercompetitive international market, _all_ businesses are software businesses (or they're quickly replaced by software businesses) and speed-to-market is a differentiator, sometimes _the only_ one! Organizations look for small batches of work that they can move from concept to production, quickly. These small batches of work demand fewer people to finish, are easier to iterate on, and can be moved to production independently; they are microservices. 9 | 10 | It is critical that an organization be prepared to address the complexities of standing up new services and to address the complexities implied by moving to a distributed systems world. 11 | 12 | ### Moving Beyond the Wiki Page: "500 Easy Steps to Production" 13 | 14 | Microservices are APIs. How quickly can you standup a new service? Microframeworks like [Spring Boot](http://start.spring.io), [Grails](http://grails.org), [Dropwizard](http://dropwizard.io), [Play framework](https://www.playframework.com/), [Wildfly Swarm](http://wildfly.org/swarm/) and [Payara Micro](http://www.payara.fish/payara_micro) are optimized for quickly standing up REST services with a minimum of fuss. Extra points go to technologies that make it easy to build smart, self-describing _hypermedia_ APIs as Spring Boot does with Spring HATEOAS. 15 | 16 | ### You Can't Fix What You Can't Measure 17 | A microservice must support visibility and transparency, indicators of its own state and of system state, in a single-pane-of-glass experience. The [DropWizard Metrics library](http://www.dropwizard.io) is one of the more popular approaches to capturing application metrics (gauges, meters, histograms, and counters). [Spring Boot's _Actuator_](http://start.spring.io) module provides deep integration with the DropWizard Metrics library, and supports exposing health endpoints, environment information, endpoint mapping information, request logs, and more. Time-series databases like Statsd, Graphite, InfluxDB and OpenTSDB support visualization and processing of metrics. DropWizard Metrics and Spring Boot Actuator can transparently export collected metrics to these time-series databases. 18 | 19 | 20 | ```java 21 | @SpringBootApplication 22 | public class DemoApplication { 23 | 24 | public static void main(String args[]) { 25 | SpringApplication.run(DemoApplication.class, args); 26 | } 27 | 28 | @Bean 29 | GraphiteReporter graphite(@Value("${graphite.prefix}") String prefix, 30 | @Value("${graphite.url}") URL url, 31 | @Value("${graphite.port}") int port, 32 | MetricRegistry registry) { 33 | 34 | GraphiteReporter reporter = GraphiteReporter.forRegistry(registry) 35 | .prefixedWith(prefix) 36 | .build(new Graphite(url.getHost(), port)); 37 | reporter.start(1, TimeUnit.SECONDS); 38 | return reporter; 39 | } 40 | } 41 | 42 | @RestController 43 | class FulfillmentRestController { 44 | 45 | @Autowired 46 | private CounterService counterService; 47 | 48 | @RequestMapping("/customers/{customerId}/fulfillment") 49 | Fulfillment fulfill(@PathVariable long customerId) { 50 | // .. 51 | counterService.increment("meter.customers-fulfilled"); 52 | // .. 53 | } 54 | } 55 | ``` 56 | 57 | 58 | 59 | 60 | Log multiplexers like Logstash or Cloud Foundry's Loggregator funnel the logs from application instances and ship them to downstream log analysis tools like ElasticSearch, Splunk, or PaperTrail. 61 | 62 | Getting all of this out of the box is a good start, but not enough. There is often much more to be done before a service can get to production. Spring Boot uses a mechanism called auto-configuration that lets developers codify things - identity provider integrations, connection pools, frameworks, auditing infrastructure, literally _anything_ - and have it stood up as part of the Spring Boot application just by being on the CLASSPATH _if_ all the conditions stipulated by the auto-configuration are met! These conditions can be anything, and Spring Boot ships with many common and reusable conditions: is a library on the `CLASSPATH`? Is a bean of a certain type defined (or not defined)? Is an environment property specified? Starting a new service need not be more complex than a `public static void main` entry-point and a library on the `CLASSPATH` if you use the right technology. 63 | 64 | ### Centralized Configuration 65 | The [12 Factor manifesto](http://12factor.net/) provides a set of guidelines for building applications with good, clean cloud hygiene. One tenant is that environment specific configuration should live external to the application itself. It might live in environment variables, `-D` arguments, externalized `.properties`, and `.yml` files, or any other place, so long as the application code itself need not be recompiled. Dropwizard, Spring Boot, [Apache Commons Configuration](http://commons.apache.org/proper/commons-configuration/) and others support this foundational requirement. However, this approach fails a few key use-cases: how do you change configuration centrally and propagate those changes? How do you support symmetric encryption and decryption of things like connection credentials? How do you support _feature flags_ which toggle configuration values at runtime, without restarting the process? 66 | 67 | [Spring Cloud](http://start.spring.io) provides the Spring Cloud Config Server which stands up a REST API in front of a version controlled repository of configuration files, and Spring Cloud provides support for using Apache Zookeeper and [Hashicorpo Consul](https://www.consul.io) as configuration sources. Spring Cloud provides various clients for all of these so that all properties, whether they come from the Config Server, Consul, a `-D` argument or an environment variable, work the same way for a Spring client. Netflix provides a solution called [Archaius](https://github.com/Netflix/archaius) which acts as a client to a pollable configuration source. This is a bit too low-level for many organizations and lacks a supported, open-source configuration source counterpart, but Spring Cloud bridges the Archaius properties with Spring's, too. 68 | 69 | 70 | *the Config Server* 71 | 72 | ```properties 73 | # application.properties 74 | spring.cloud.config.server.git.uri=https://github.com/joshlong/my-config.git 75 | server.port=8888 76 | ``` 77 | 78 | ```java 79 | @EnableConfigServer 80 | @SpringBootApplication 81 | public class ConfigServiceApplication { 82 | 83 | public static void main(String[] args) { 84 | SpringApplication.run(ConfigServiceApplication.class, args); 85 | } 86 | } 87 | ``` 88 | 89 | *the Config Client* 90 | 91 | ```properties 92 | # application.properties 93 | spring.cloud.config.uri=http://localhost:8888 94 | spring.application.name=message-client 95 | # will read https://github.com/joshlong/my-config/message-client.properties 96 | ``` 97 | 98 | ```java 99 | @SpringBootApplication 100 | public class ConfigClientApplication { 101 | 102 | public static void main(String[] args) { 103 | SpringApplication.run(ConfigClientApplication.class, args); 104 | } 105 | } 106 | 107 | // supports dynamic re-configuration: 108 | // curl -d{} http://localhost:8000/refresh 109 | @RestController 110 | @RefreshScope 111 | class MessageRestController { 112 | 113 | @Value("${message}") 114 | private String message; 115 | 116 | @RequestMapping("/message") 117 | String read() { 118 | return this.message; 119 | } 120 | } 121 | ``` 122 | 123 | ### Service Registration and Discovery 124 | DNS is sometimes a poor fit for intra-service communication. DNS benefits from layers of caching and time-to-liveness that works against services in a dynamic cloud environment. In most cloud environments, DNS resolution requires a trip out of the platform to the router and then back again, introducing latency. DNS doesn't provide a way to answer the question: is the service I am trying to call still alive and responding? A request to such a fallen service will block until the service responds, unless the client specifies a timeout (which it should!). DNS is often paired with load-balancers, but third-party load-balancers are not sophisticated things: they may support round-robin load-balancing, or even availability-zone aware load-balancing, but may not be able to accomodate business-logic specific routing like routing a request with an OAuth token to a specific node, or routing requests to nodes collocated with data, etc.) It's important to decouple the client from the location of the service, but DNS might be a poor fit. A little bit of indirection is required. A service registry provides that indirection. A service registry is a phonebook, letting clients look up services by their logical name. There are many such service registries out there. [Netflix's Eureka](https://github.com/Netflix/eureka), [Apache Zookeeper](https://zookeeper.apache.org/), and [Hashicorp Consul](https://www.consul.io/) are three good examples. Spring Cloud's `DiscoveryClient` abstraction provides a convenient client-side API implementations for working with service registries. 125 | 126 | 127 | 128 | ```java 129 | @Autowired 130 | public void enumerateServiceInstances(DiscoveryClient client){ 131 | client.getInstances("reservation-service") 132 | .forEach( si -> System.out.println( si.getHost() + ":" + si.getPort() )); 133 | } 134 | 135 | ``` 136 | 137 | ### Client Side Load Balancing 138 | A big benefit of using a service registry is client-side load-balancing. Client-side load-balancing let's the client pick from among the registered instances of a given service - if there are 10 or a thousand they're all discovered through the registry - and then choose from among the candidate instances which one to route requests to. The client can programmatically decide based on whatever criteria it likes - capacity, round-robin, cloud-provider availability-zone awareness, multi-tenancy, etc., to which node a request should be sent. Netlfix provide a great client-side load-balancer called [Ribbon](https://github.com/Netflix/ribbon). Spring Cloud readily integrates Ribbon and it is automatically in play at all layers of the framework, whether you're using the `RestTemplate`, declarative REST clients powered by Netflix's Feign, or the Zuul microproxy. 139 | 140 | ```java 141 | @EnableDiscoveryClient 142 | @SpringBootApplication 143 | public class ReservationClientApplication { 144 | 145 | @Bean 146 | @LoadBalanced // lets us use service registry service IDs as hosts 147 | RestTemplate restTemplate() { 148 | return new RestTemplate(); 149 | } 150 | 151 | public static void main(String[] args) { 152 | SpringApplication.run(ReservationClientApplication.class, args); 153 | } 154 | } 155 | 156 | @RestController 157 | class ApiClientRestController { 158 | 159 | @Autowired 160 | private RestTemplate restTemplate; 161 | 162 | @RequestMapping(method = RequestMethod.GET, value = "/reservations/names") 163 | public Collection names() { 164 | 165 | ResponseEntity responseEntity = 166 | restTemplate.exchange("http://reservation-service/reservations", 167 | HttpMethod.GET, null, JsonNode.class); 168 | // ... 169 | } 170 | } 171 | ``` 172 | 173 | ### Edge Services: Micro Proxies and API Gateways 174 | 175 | Client-side load-balancing works for intra-service communication, usually behind a firewall. External clients - iPhones, HTML5 clients, Android clients, etc. - have client-specific security, payload and protocol requirements. An edge service may proxy or mediate requests and replies between the system of services and the clients. An edge service is exposed via DNS and forwards requests using service discovery. Edge services are intermediaries and an ideal place to insert API translation or protocol translation. HTML5 clients, for example, exist in a sandbox and must issue requests to the same origin host and port. HTML5 clients may reach across their origin server to other resources so long as those resources have been configured to support CORS. This requirement is untenable and unscalable as you add more clients connecting to more microservices. A microproxy, like [Netflix's Zuul](https://github.com/Netflix/zuul) forwards all requests at the edge service to microservices, often to those it discovers in a service registry. If your application is an HTML5 application it might be enough to standup a microproxy, insert HTTP BASIC or OAuth security, support HTTPS, and be done with it. 176 | 177 | Sometimes the client needs a coarser-grained view of the data coming from the services. This implies API translation. An edge service, stood up using something like Spring Boot, might use Reactive programming technologies like [Netflix's RxJava](https://github.com/ReactiveX/RxJava), Typesafe's [Akka](http://Akka.io), [RedHat's Vert.x](http://vertx.io/), and [Pivotal's Reactor](http://projectreactor.io/) to compose requests and transformations across multiple services into a single response. Indeed, all of these technologies implement a common API called the [reactive streams API](http://www.reactive-streams.org/) because this subset of problems is so common. 178 | 179 | 180 | An edge service is the last line of defense from the outside world and must be tolerant to service outages and failure. 181 | 182 | ```java 183 | @EnableZuulProxy 184 | @EnableCircuitBreaker 185 | @EnableDiscoveryClient 186 | @SpringBootApplication 187 | public class EdgeServiceApplication { 188 | 189 | public static void main(String[] args) { 190 | SpringApplication.run(EdgeServiceApplication.class, args); 191 | } 192 | } 193 | 194 | @RestController 195 | class TradesStreamingApiGateway { 196 | 197 | @Autowired 198 | private MarketService marketService; 199 | 200 | @RequestMethod(method=HttpMethod.GET, value = "/market/trades") 201 | public SseEmitter trades() { 202 | SseEmitter sseEmitter = new SseEmitter(); 203 | Observable trades = marketService.observeTrades(); // RxJava 204 | trades.subscribe( value -> publishNewTrade(sseEmitter, value), 205 | sseEmitter::completeWithError, 206 | sseEmitter::complete 207 | ); 208 | return sseEmitter; 209 | } 210 | 211 | private void publishNewTrade(SseEmitter sseEmitter, Trade t) { 212 | try { 213 | sseEmitter.send(t); 214 | } catch (IOException e) { 215 | e.printStackTrace(); 216 | } 217 | } 218 | } 219 | ``` 220 | 221 | 222 | ### Clustering Primitives 223 | In a complex distributed systems, there are many actors with many roles to play. Cluster coordination and cluster consensus is one of the most difficult problems to solve. How do you handle leadership election, active/passive handoff or global locks? Thankfully, many technologies provide the primitives required to support this sort of coordination, including Apache Zookeeper, [Redis](http://redis.io) and [Hazelcast](https://hazelcast.com/). [Spring Cloud's Cluster](http://start.spring.io) support provides a clean integration with all of these technologies. 224 | 225 | In the following example, we've configured a component to change its state whenever Spring Cloud Cluster emits a `OnGrantedEvent` or a `OnRevokedEvent`, which it'll do when it the underlying coordination technology promotes and demotes a leader node. 226 | 227 | ```java 228 | @Component 229 | class LeadershipApplicationListener { 230 | 231 | @EventListener 232 | public void leadershipGranted(OnGrantedEvent evt){ 233 | // .. 234 | } 235 | 236 | @EventListener 237 | public void leadershipRevoked(OnRevokedEvent evt){ 238 | // .. 239 | } 240 | } 241 | ``` 242 | 243 | ### Messaging, CQRS and Stream Processing 244 | When you move into the world of microservices, state synchronization becomes more difficult. The reflex of the experienced architect might be to reach for distributed transactions, a la JTA. Ignore this urge at all costs. Transactions are a stop-the-world approach to state synchronization and slow the system as a whole; the worst possible outcome in a distributed system. Instead, services today use eventual consistency through messaging to ensure that state eventually reflects the correct system worldview. REST is a fine technology for _reading_ data but it doesn't provide any guarantees about the propagation and eventual processing of a transaction. Actor systems like [Typesafe Akka](http://akka.io) and message brokers like [Apache ActiveMQ](http://activemq.apache.org/), [Apache Kafka](http://kafka.apache.org/), [RabbitMQ](http://rabbitmq.com) or [even Redis](http://redis.io/) have become the norm. Akka provides a supervisory system guarantees a message will be processed at-least once. If you're using messaging, there are many APIs that can simplify the chore including [Apache Camel](http://camel.apache.org/), [Spring Integration](http://projects.spring.io/spring-integration/) and - at a higher abstraction level - Spring Cloud Stream. Using messaging for writes and use REST for reads optimizes reads separately from writes. The [Command Query Responsibility Segregation](http://martinfowler.com/bliki/CQRS.html), or CQRS, design pattern specifically describes this approach. 245 | 246 | ```java 247 | 248 | @EnableBinding(CrmChannels.class) 249 | @SpringBootApplication 250 | public class ProductsEdgeService { 251 | 252 | public static void main(String[] args) { 253 | SpringApplication.run(ReservationClientApplication.class, args); 254 | } 255 | } 256 | 257 | interface CrmChannels { 258 | 259 | @Output 260 | MessageChannel orders(); 261 | 262 | @Output 263 | MessageChannel customers(); 264 | 265 | @Output 266 | MessageChannel products(); 267 | } 268 | 269 | 270 | @RestController 271 | @RequestMapping("/products") 272 | class ProductsApiGatewayRestController { 273 | 274 | @Autowired 275 | private MessageChannel products; 276 | 277 | @RequestMapping(method = RequestMethod.POST) 278 | public void write(@RequestBody Product p) { 279 | Message msg = MessageBuilder.withPayload (p).build(); 280 | products.send(msg); 281 | } 282 | } 283 | ``` 284 | 285 | ```java 286 | @EnableBinding(Sink.class) 287 | public class ProductHandler { 288 | 289 | @Autowired 290 | private ProductRepository products; 291 | 292 | @StreamListener(Sink.INPUT) 293 | public void handle(Product p) { 294 | products.addProduct(vote); 295 | } 296 | } 297 | ``` 298 | 299 | ### Circuit Breakers 300 | Circuit breakers, like [Netflix's Hystrix](https://github.com/Netflix/Hystrix) or [JRugged](https://github.com/Comcast/jrugged), help prevent a downstream service from being overwhelmed and help isolate failures, permitting downstream services time to recover. Systems are complex, living things. Failure in one system can trigger a domino effect across other systems if care isn't taken to isolate them. A circuit breaker will slowly attempt to reintroduce traffic. Circuit breakers represent connections between services in a system; it is important to monitor them. Hystrix provides a dashboard for its circuits. Wildfly Swarm has support for using Hystrix. The [Play framework](https://www.playframework.com/) provides support for circuit breakers. Spring Cloud also has deep support for Hystrix and the dashboard, as well as multiplexing the server-sent event streams emitted from different components into a single stream using Spring Cloud Turbine. 301 | 302 | ```java 303 | @RestController 304 | class EdgeService { 305 | 306 | public Collection fallback(){ 307 | // .. 308 | } 309 | 310 | // the dashboard will show a circuit named 'reservation-service' 311 | @HystrixCommand(fallbackMethod = "fallback") 312 | @RequestMapping(method = RequestMethod.GET, value = "/names") 313 | public Collection names() { 314 | // .. 315 | } 316 | } 317 | ``` 318 | 319 | Here is the dashboard: 320 | 321 | 322 | 323 | ### Distributed Tracing 324 | It is difficult to reason about a microservice system with REST-based, messaging-based and proxy-based egress and ingress points. How do you trace (correllate) requests across a series of services and understand where something has failed? This is difficult enough a challenge _without_ a sufficient upfront investment in a tracing strategy. Google introduced their distributed tracing strategy in their [Dapper paper](http://research.google.com/pubs/pub36356.html). [Apache HTRace](http://incubator.apache.org/projects/htrace.html) is a Dapper-inspired alternative. 325 | [Twitter's Zipkin](https://blog.twitter.com/2012/distributed-systems-tracing-with-zipkin) is another Dapper-inspired tracing system. It provides the trace collection infrastructure and a UI in which you can view waterfall graphs of calls across services along with their timings and trace-specific information. Spring Cloud Sleuth provides an abstraction around the concepts of distributed tracing. Spring Cloud Sleuth automatically traces common ingress and egress points in the system. Spring Cloud Zipkin integrates Twitter Zipkin in terms of the Spring Cloud Sleuth abstraction. 326 | 327 | 328 | 329 | 330 | ### Single Sign-On and Security 331 | 332 | Security is hard. In a distributed system, it is critical to ascertain the providence and authenticity of a request in a consistent way across all services. OAuth and OpenID Connect are very popular on the open web and SAML rules the enterprise. OAuth 2 provides explicit integration with SAML. API gateway tools like [Apigee](http://apigee.com/) and SaaS identity providers like [Stormpath](https://stormpath.com/) can act as a security hub, exposing OAuth (for example) and connecting the backend to more traditional identity providers like ActiveDirectory, SiteMinder, or LDAP. Finally, [Spring Security OAuth](http://projects.spring.io/spring-security-oauth/) provides an identity server which can then talk to any identity provider in the backend. Whatever your choice of identity provider, it should be trivial to protect services based on some sort of token. Spring Cloud Security makes short work of protecting any REST API with tokens from _any_ OAuth 2 provider - Google, Facebook, the Spring Security OAuth server, Stormpath, etc. [Apache Shiro](http://shiro.apache.org/) can also act as an OAuth client using the Scribe OAuth client. 333 | 334 | 335 | ### A Cloud Native Architecture is an Agile Architecture 336 | 337 | Instead, systems must optimize for time-to-remediation; when a service goes down, how quickly can the system replace it? If time-to-remediation is 0 seconds, then the system is (apparently) highly available 100% of the time. The apparent appearance of the system is the same in a single-node service that is 100% highly available, but it has _profound_ impacts on the architecture of the system. The patterns we've looked at in this refcard support building systems that are tolerant of failure and service topology changes common in a dynamic cloud environment. Remember: the goal here is to achieve velocity, and to waste as little time as possible on non-functional requirements. Automation at the platform and application tiers support this velocity. Embracing one without the other only invites undifferentiating complexity into an architecture and defeats the purpose of moving to this architecture in the first place. 338 | -------------------------------------------------------------------------------- /code/config-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | config-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | config-service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-config-server 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-test 35 | test 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-dependencies 44 | Brixton.RC1 45 | pom 46 | import 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-maven-plugin 56 | 57 | 58 | 59 | 60 | 61 | 62 | spring-snapshots 63 | Spring Snapshots 64 | https://repo.spring.io/snapshot 65 | 66 | true 67 | 68 | 69 | 70 | spring-milestones 71 | Spring Milestones 72 | https://repo.spring.io/milestone 73 | 74 | false 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /code/config-service/src/main/java/com/example/ConfigServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.config.server.EnableConfigServer; 6 | 7 | @EnableConfigServer 8 | @SpringBootApplication 9 | public class ConfigServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ConfigServiceApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /code/config-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.server.git.uri=${HOME}/Desktop/config 2 | server.port=8888 -------------------------------------------------------------------------------- /code/config-service/src/test/java/com/example/ConfigServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | 8 | @RunWith(SpringJUnit4ClassRunner.class) 9 | @SpringApplicationConfiguration(classes = ConfigServiceApplication.class) 10 | public class ConfigServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /code/eureka-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | eureka-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | eureka-service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-config 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-eureka-server 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-test 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-dependencies 48 | Brixton.RC1 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-maven-plugin 60 | 61 | 62 | 63 | 64 | 65 | 66 | spring-snapshots 67 | Spring Snapshots 68 | https://repo.spring.io/snapshot 69 | 70 | true 71 | 72 | 73 | 74 | spring-milestones 75 | Spring Milestones 76 | https://repo.spring.io/milestone 77 | 78 | false 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /code/eureka-service/src/main/java/com/example/EurekaServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; 6 | 7 | @EnableEurekaServer 8 | @SpringBootApplication 9 | public class EurekaServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(EurekaServiceApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /code/eureka-service/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://localhost:8888 2 | spring.application.name=eureka-service 3 | -------------------------------------------------------------------------------- /code/eureka-service/src/test/java/com/example/EurekaServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | 8 | @RunWith(SpringJUnit4ClassRunner.class) 9 | @SpringApplicationConfiguration(classes = EurekaServiceApplication.class) 10 | public class EurekaServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /code/hystrix-dashboard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | hystrix-dashboard 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | hystrix-dashboard 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-config 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-eureka 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-starter-hystrix-dashboard 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-dependencies 52 | Brixton.RC1 53 | pom 54 | import 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | 68 | 69 | 70 | spring-snapshots 71 | Spring Snapshots 72 | https://repo.spring.io/snapshot 73 | 74 | true 75 | 76 | 77 | 78 | spring-milestones 79 | Spring Milestones 80 | https://repo.spring.io/milestone 81 | 82 | false 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /code/hystrix-dashboard/src/main/java/com/example/HystrixDashboardApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.netflix.hystrix.EnableHystrix; 7 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 8 | 9 | @EnableHystrixDashboard 10 | @EnableDiscoveryClient 11 | @SpringBootApplication 12 | public class HystrixDashboardApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(HystrixDashboardApplication.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /code/hystrix-dashboard/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://localhost:8888 2 | spring.application.name=hystrix-dashboard 3 | -------------------------------------------------------------------------------- /code/hystrix-dashboard/src/test/java/com/example/HystrixDashboardApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | 8 | @RunWith(SpringJUnit4ClassRunner.class) 9 | @SpringApplicationConfiguration(classes = HystrixDashboardApplication.class) 10 | public class HystrixDashboardApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /code/reservation-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | reservation-client 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | reservation-client 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-config 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-eureka 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-starter-hystrix 38 | 39 | 40 | org.springframework.cloud 41 | spring-cloud-starter-zipkin 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-data-rest 46 | 47 | 48 | org.springframework.cloud 49 | spring-cloud-starter-stream-rabbit 50 | 51 | 52 | org.springframework.cloud 53 | spring-cloud-starter-zuul 54 | 55 | 56 | org.springframework.cloud 57 | spring-cloud-starter-feign 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-web 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-test 67 | test 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.cloud 75 | spring-cloud-dependencies 76 | Brixton.RC1 77 | pom 78 | import 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-maven-plugin 88 | 89 | 90 | 91 | 92 | 93 | 94 | spring-snapshots 95 | Spring Snapshots 96 | https://repo.spring.io/snapshot 97 | 98 | true 99 | 100 | 101 | 102 | spring-milestones 103 | Spring Milestones 104 | https://repo.spring.io/milestone 105 | 106 | false 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /code/reservation-client/src/main/java/com/example/ReservationClientApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 8 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 9 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 10 | import org.springframework.cloud.netflix.feign.EnableFeignClients; 11 | import org.springframework.cloud.netflix.feign.FeignClient; 12 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 13 | import org.springframework.cloud.stream.annotation.EnableBinding; 14 | import org.springframework.cloud.stream.annotation.Output; 15 | import org.springframework.context.annotation.Bean; 16 | import org.springframework.hateoas.Resources; 17 | import org.springframework.integration.support.MessageBuilder; 18 | import org.springframework.messaging.MessageChannel; 19 | import org.springframework.web.bind.annotation.RequestBody; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestMethod; 22 | import org.springframework.web.bind.annotation.RestController; 23 | import org.springframework.web.client.RestTemplate; 24 | 25 | import java.util.ArrayList; 26 | import java.util.Collection; 27 | import java.util.stream.Collectors; 28 | 29 | 30 | interface ReservationChannels { 31 | 32 | @Output 33 | MessageChannel output(); 34 | } 35 | 36 | 37 | @FeignClient("reservation-service") 38 | interface ReservationReader { 39 | 40 | @RequestMapping(method = RequestMethod.GET, value = "/reservations") 41 | Resources read(); 42 | } 43 | 44 | @EnableFeignClients 45 | @EnableZuulProxy 46 | @EnableCircuitBreaker 47 | @EnableBinding(ReservationChannels.class) 48 | @EnableDiscoveryClient 49 | @SpringBootApplication 50 | public class ReservationClientApplication { 51 | 52 | @Bean 53 | @LoadBalanced 54 | RestTemplate restTemplate() { 55 | return new RestTemplate(); 56 | } 57 | 58 | public static void main(String[] args) { 59 | SpringApplication.run(ReservationClientApplication.class, args); 60 | } 61 | } 62 | 63 | @RestController 64 | @RequestMapping("/reservations") 65 | class ReservationsApiGatewayRestController { 66 | 67 | @Autowired 68 | private RestTemplate restTemplate; 69 | 70 | 71 | public Collection fallback() { 72 | return new ArrayList<>(); 73 | } 74 | 75 | @Autowired 76 | private ReservationChannels channels; 77 | 78 | 79 | @RequestMapping(method = RequestMethod.POST) 80 | public void write(@RequestBody Reservation r) { 81 | 82 | MessageChannel output = this.channels.output(); 83 | output.send(MessageBuilder.withPayload(r.getReservationName()).build()); 84 | } 85 | 86 | 87 | @HystrixCommand(fallbackMethod = "fallback") 88 | @RequestMapping(method = RequestMethod.GET, value = "/names") 89 | public Collection names() { 90 | 91 | /*ParameterizedTypeReference> ptr = 92 | new ParameterizedTypeReference>() { 93 | }; 94 | 95 | ResponseEntity> responseEntity = 96 | this.restTemplate.exchange("http://reservation-service/reservations", HttpMethod.GET, null, ptr); 97 | 98 | return responseEntity 99 | .getBody() 100 | .getContent() 101 | .stream() 102 | .map(Reservation::getReservationName) 103 | .collect(Collectors.toList()); 104 | */ 105 | 106 | return this.reservationReader.read() 107 | .getContent() 108 | .stream() 109 | .map(Reservation::getReservationName) 110 | .collect(Collectors.toList()); 111 | 112 | 113 | } 114 | 115 | @Autowired 116 | private ReservationReader reservationReader; 117 | 118 | } 119 | 120 | class Reservation { 121 | private String reservationName; 122 | 123 | public String getReservationName() { 124 | return reservationName; 125 | } 126 | } -------------------------------------------------------------------------------- /code/reservation-client/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://localhost:8888 2 | spring.application.name=reservation-client -------------------------------------------------------------------------------- /code/reservation-client/src/test/java/com/example/ReservationClientApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | import org.springframework.test.context.web.WebAppConfiguration; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = ReservationClientApplication.class) 11 | @WebAppConfiguration 12 | public class ReservationClientApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /code/reservation-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | reservation-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | reservation-service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | org.springframework.cloud 34 | spring-cloud-starter-config 35 | 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-eureka 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-zipkin 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-stream-rabbit 48 | 49 | 50 | 51 | 52 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-web 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-actuator 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-data-rest 67 | 68 | 69 | com.h2database 70 | h2 71 | runtime 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-test 76 | test 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.springframework.cloud 84 | spring-cloud-dependencies 85 | Brixton.RC1 86 | pom 87 | import 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | org.springframework.boot 96 | spring-boot-maven-plugin 97 | 98 | 99 | 100 | 101 | 102 | 103 | spring-snapshots 104 | Spring Snapshots 105 | https://repo.spring.io/snapshot 106 | 107 | true 108 | 109 | 110 | 111 | spring-milestones 112 | Spring Milestones 113 | https://repo.spring.io/milestone 114 | 115 | false 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /code/reservation-service/src/main/java/com/example/ReservationServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.jboss.logging.annotations.Message; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.actuate.health.Health; 9 | import org.springframework.boot.actuate.health.HealthIndicator; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 12 | import org.springframework.cloud.context.config.annotation.RefreshScope; 13 | import org.springframework.cloud.stream.annotation.EnableBinding; 14 | import org.springframework.cloud.stream.messaging.Sink; 15 | import org.springframework.data.jpa.repository.JpaRepository; 16 | import org.springframework.data.repository.query.Param; 17 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 18 | import org.springframework.data.rest.core.annotation.RestResource; 19 | import org.springframework.integration.annotation.MessageEndpoint; 20 | import org.springframework.integration.annotation.ServiceActivator; 21 | import org.springframework.stereotype.Component; 22 | import org.springframework.web.bind.annotation.RequestMapping; 23 | import org.springframework.web.bind.annotation.RestController; 24 | 25 | import javax.persistence.Entity; 26 | import javax.persistence.GeneratedValue; 27 | import javax.persistence.Id; 28 | import java.util.Collection; 29 | import java.util.stream.Stream; 30 | 31 | @EnableBinding(Sink.class) 32 | @EnableDiscoveryClient 33 | @SpringBootApplication 34 | public class ReservationServiceApplication { 35 | 36 | public static void main(String[] args) { 37 | SpringApplication.run(ReservationServiceApplication.class, args); 38 | } 39 | } 40 | 41 | @MessageEndpoint 42 | class ReservationProcessor { 43 | 44 | @Autowired 45 | private ReservationRepository reservationRepository ; 46 | 47 | @ServiceActivator (inputChannel = "input") 48 | public void acceptNewReservationsPlease ( String rn) { 49 | this.reservationRepository.save(new Reservation(rn)) ; 50 | } 51 | 52 | } 53 | 54 | @RestController 55 | @RefreshScope 56 | class MessageRestController { 57 | 58 | @Value("${message}") 59 | private String message; 60 | 61 | @RequestMapping("/message") 62 | String m() { 63 | return this.message; 64 | } 65 | 66 | } 67 | 68 | @Component 69 | class CustomHealthIndicator implements HealthIndicator { 70 | 71 | @Override 72 | public Health health() { 73 | return Health.status("I <3 First Data!").build(); 74 | } 75 | } 76 | 77 | @Component 78 | class DummyCLR implements CommandLineRunner { 79 | 80 | @Autowired 81 | private ReservationRepository reservationRepository; 82 | 83 | @Override 84 | public void run(String... args) throws Exception { 85 | 86 | Stream.of("Josh", "Ritesh", "Yoram", "Boris", "Tony", "Joe", "Gsaravanan", "Ian") 87 | .forEach(name -> reservationRepository.save(new Reservation(name))); 88 | 89 | reservationRepository.findAll().forEach(System.out::println); 90 | 91 | } 92 | } 93 | 94 | @RepositoryRestResource 95 | interface ReservationRepository extends JpaRepository { 96 | 97 | // select * from reservatins where reservation_name = :rn 98 | @RestResource(path = "by-name") 99 | Collection findByReservationName(@Param("rn") String rn); 100 | } 101 | 102 | 103 | @Entity 104 | class Reservation { 105 | 106 | @Id 107 | @GeneratedValue 108 | private Long id; 109 | 110 | private String reservationName; // reservation_name 111 | 112 | @Override 113 | public String toString() { 114 | return "Reservation{" + 115 | "id=" + id + 116 | ", reservationName='" + reservationName + '\'' + 117 | '}'; 118 | } 119 | 120 | public Long getId() { 121 | return id; 122 | } 123 | 124 | public String getReservationName() { 125 | return reservationName; 126 | } 127 | 128 | public Reservation() { 129 | 130 | } 131 | 132 | public Reservation(String reservationName) { 133 | 134 | this.reservationName = reservationName; 135 | } 136 | } -------------------------------------------------------------------------------- /code/reservation-service/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BRIGHT_BLUE}████████████████████████████████████████████████████████████████████████████████ 2 | ${AnsiColor.BRIGHT_BLUE}████████████████████████████████████████████████████████████████████████████████ 3 | ${AnsiColor.RED}██████████████████${AnsiColor.BRIGHT_BLUE}████████████████${AnsiColor.BLACK}██████████████████████████████${AnsiColor.BRIGHT_BLUE}████████████████ 4 | ${AnsiColor.RED}████████████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████████████████████████████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██████████████ 5 | ${AnsiColor.BRIGHT_RED}████${AnsiColor.RED}██████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.MAGENTA}██████████████████████${AnsiColor.WHITE}██████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████████████ 6 | ${AnsiColor.BRIGHT_RED}██████████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.MAGENTA}████████████████${AnsiColor.BLACK}████${AnsiColor.MAGENTA}██████${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██${AnsiColor.BLACK}████${AnsiColor.BRIGHT_BLUE}██████ 7 | ${AnsiColor.BRIGHT_RED}██████████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.MAGENTA}██████${AnsiColor.WHITE}██${AnsiColor.BLACK}████${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████ 8 | ${AnsiColor.BRIGHT_YELLOW}██████████████████${AnsiColor.BRIGHT_RED}████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.MAGENTA}██████${AnsiColor.WHITE}██${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████ 9 | ${AnsiColor.BRIGHT_YELLOW}██████████████████████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_YELLOW}██████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BLACK}████████${AnsiColor.WHITE}████████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████ 10 | ${AnsiColor.BRIGHT_YELLOW}████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.BLACK}██${AnsiColor.BRIGHT_YELLOW}████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████████████████████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████ 11 | ${AnsiColor.BRIGHT_GREEN}██████████████████${AnsiColor.BRIGHT_YELLOW}██${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.BLACK}████████${AnsiColor.WHITE}██${AnsiColor.MAGENTA}██████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████████████████████████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██ 12 | ${AnsiColor.BRIGHT_GREEN}██████████████████████${AnsiColor.WHITE}████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}██████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BRIGHT_YELLOW}██${AnsiColor.WHITE}██████████${AnsiColor.BRIGHT_YELLOW}██${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██ 13 | ${AnsiColor.BRIGHT_GREEN}██████████████████████${AnsiColor.BLACK}████${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}██████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.BLACK}████${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██ 14 | ${AnsiColor.BLUE}██████████████████${AnsiColor.BRIGHT_GREEN}████████${AnsiColor.BLACK}██████${AnsiColor.WHITE}██${AnsiColor.MAGENTA}██████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.MAGENTA}████${AnsiColor.WHITE}████████████████${AnsiColor.MAGENTA}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██ 15 | ${AnsiColor.BLUE}██████████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.MAGENTA}██████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BLACK}████████████${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████ 16 | ${AnsiColor.BRIGHT_BLUE}██████████████████${AnsiColor.BLUE}████${AnsiColor.BLUE}██████${AnsiColor.BLACK}████${AnsiColor.WHITE}██████${AnsiColor.MAGENTA}██████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████████████████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██████ 17 | ${AnsiColor.BRIGHT_BLUE}██████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██${AnsiColor.BLACK}████${AnsiColor.WHITE}████████████████████${AnsiColor.BLACK}██████████████████${AnsiColor.BRIGHT_BLUE}████████ 18 | ${AnsiColor.BRIGHT_BLUE}████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}██████${AnsiColor.BLACK}████████████████████████████████${AnsiColor.WHITE}██${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████████████ 19 | ${AnsiColor.BRIGHT_BLUE}████████████████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}██${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.BRIGHT_BLUE}████████████${AnsiColor.BLACK}██${AnsiColor.WHITE}████${AnsiColor.BLACK}████${AnsiColor.WHITE}████${AnsiColor.BLACK}██${AnsiColor.BRIGHT_BLUE}████████████ 20 | ${AnsiColor.BRIGHT_BLUE}████████████████████████${AnsiColor.BLACK}██████${AnsiColor.BRIGHT_BLUE}████${AnsiColor.BLACK}██████${AnsiColor.BRIGHT_BLUE}████████████${AnsiColor.BLACK}██████${AnsiColor.BRIGHT_BLUE}████${AnsiColor.BLACK}██████${AnsiColor.BRIGHT_BLUE}████████████ 21 | ████████████████████████████████████████████████████████████████████████████████ 22 | ${AnsiColor.BRIGHT_BLUE}:: Meow :: Running Spring Boot ${spring-boot.version} :: \ö/${AnsiColor.BLACK} -------------------------------------------------------------------------------- /code/reservation-service/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.config.uri=http://localhost:8888 2 | spring.application.name=reservation-service 3 | -------------------------------------------------------------------------------- /code/reservation-service/src/test/java/com/example/ReservationServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | import org.springframework.test.context.web.WebAppConfiguration; 8 | 9 | @RunWith(SpringJUnit4ClassRunner.class) 10 | @SpringApplicationConfiguration(classes = ReservationServiceApplication.class) 11 | @WebAppConfiguration 12 | public class ReservationServiceApplicationTests { 13 | 14 | @Test 15 | public void contextLoads() { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /code/zipkin-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.example 7 | zipkin-service 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | zipkin-service 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.3.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-config 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-eureka 34 | 35 | 36 | io.zipkin.java 37 | zipkin-server 38 | 39 | 40 | 41 | io.zipkin 42 | zipkin-ui 43 | runtime 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-dependencies 57 | Brixton.BUILD-SNAPSHOT 58 | pom 59 | import 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-maven-plugin 69 | 70 | 71 | 72 | 73 | 74 | 75 | spring-snapshots 76 | Spring Snapshots 77 | https://repo.spring.io/snapshot 78 | 79 | true 80 | 81 | 82 | 83 | spring-milestones 84 | Spring Milestones 85 | https://repo.spring.io/milestone 86 | 87 | false 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /code/zipkin-service/src/main/java/com/example/ZipkinServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import zipkin.server.EnableZipkinServer; 6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 7 | 8 | @EnableZipkinServer 9 | @EnableDiscoveryClient 10 | @SpringBootApplication 11 | public class ZipkinServiceApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ZipkinServiceApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /code/zipkin-service/src/main/resources/bootstrap.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=zipkin-service 2 | spring.cloud.config.uri=http://localhost:8888 3 | -------------------------------------------------------------------------------- /code/zipkin-service/src/test/java/com/example/ZipkinServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.SpringApplicationConfiguration; 6 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 | 8 | @RunWith(SpringJUnit4ClassRunner.class) 9 | @SpringApplicationConfiguration(classes = ZipkinServiceApplication.class) 10 | public class ZipkinServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /images/git-config-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-dzrc/b90d75329d9f93282cb9de3e1be0da051b0680d4/images/git-config-repo.png -------------------------------------------------------------------------------- /images/graphite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-dzrc/b90d75329d9f93282cb9de3e1be0da051b0680d4/images/graphite.png -------------------------------------------------------------------------------- /images/hystrix-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-dzrc/b90d75329d9f93282cb9de3e1be0da051b0680d4/images/hystrix-dashboard.png -------------------------------------------------------------------------------- /images/spring-cloud-netflix-eureka.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-dzrc/b90d75329d9f93282cb9de3e1be0da051b0680d4/images/spring-cloud-netflix-eureka.png -------------------------------------------------------------------------------- /images/zipkin-traces.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshlong-attic/microservices-dzrc/b90d75329d9f93282cb9de3e1be0da051b0680d4/images/zipkin-traces.png --------------------------------------------------------------------------------