├── README.md ├── account-service ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ ├── AccountServiceApplication.java │ │ │ ├── client │ │ │ ├── AuthServiceClient.java │ │ │ ├── AuthServiceClientFallback.java │ │ │ └── AuthServiceRibbonClient.java │ │ │ ├── domain │ │ │ ├── Account.java │ │ │ ├── Currency.java │ │ │ ├── Item.java │ │ │ ├── Saving.java │ │ │ └── TimePeriod.java │ │ │ ├── service │ │ │ └── AccountService.java │ │ │ ├── vo │ │ │ └── User.java │ │ │ └── web │ │ │ └── controller │ │ │ └── AccountController.java │ └── resources │ │ ├── bootstrap.yml │ │ └── logback-spring.xml │ └── test │ └── java │ └── demo │ └── ms │ └── AccountServiceApplicationTests.java ├── api-gateway ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ ├── ApiGatewayApplication.java │ │ │ └── fallback │ │ │ ├── AccountServiceFallbackProvider.java │ │ │ ├── AuthServiceFallbackProvider.java │ │ │ └── SimpleClientHttpResponse.java │ └── resources │ │ ├── bootstrap.yml │ │ └── logback-spring.xml │ └── test │ └── java │ └── demo │ └── ms │ └── ApiGatewayApplicationTests.java ├── auth-service ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ ├── AuthServiceApplication.java │ │ │ ├── domain │ │ │ └── User.java │ │ │ ├── service │ │ │ └── UserService.java │ │ │ ├── util │ │ │ └── IPUtils.java │ │ │ └── web │ │ │ └── controller │ │ │ ├── ControllerExceptionHandler.java │ │ │ └── UserController.java │ └── resources │ │ ├── bootstrap.yml │ │ └── logback-spring.xml │ └── test │ └── java │ └── demo │ └── ms │ └── AuthServiceApplicationTests.java ├── build-images.sh ├── cloud-gateway ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ └── CloudGatewayApplication.java │ └── resources │ │ ├── bootstrap.yml │ │ ├── logback-spring.xml │ │ └── static │ │ └── index.html │ └── test │ └── java │ └── demo │ └── ms │ └── CloudGatewayApplicationTests.java ├── config-server ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ └── ConfigServerApplication.java │ └── resources │ │ ├── application.yml │ │ ├── bootstrap.yml │ │ ├── logback-spring.xml │ │ └── shared │ │ ├── account-service.yml │ │ ├── api-gateway.yml │ │ ├── application.yml │ │ ├── auth-service.yml │ │ ├── cloud-gateway.yml │ │ ├── monitor-dashboard.yml │ │ ├── service-registry.yml │ │ └── turbine-server.yml │ └── test │ └── java │ └── demo │ └── ms │ └── ConfigServiceApplicationTests.java ├── deploy-docker.sh ├── deploy-kubernetes.sh ├── docker-compose.yml ├── docker └── setup-docker-images.txt ├── kubernetes ├── account-service.yaml ├── api-gateway.yaml ├── auth-service.yaml ├── cloud-gateway.yaml ├── config-pv.yaml ├── config-secret.yaml ├── config-server.yaml ├── db-pv.yaml ├── env-configmap.yaml ├── log-pv.yaml ├── monitor-dashboard.yaml ├── mysql.yaml ├── rabbitmq.yaml ├── service-registry.yaml ├── turbine-server.yaml └── zipkin.yaml ├── monitor-dashboard ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ └── MonitorDashboardApplication.java │ └── resources │ │ ├── bootstrap.yml │ │ └── logback-spring.xml │ └── test │ └── java │ └── demo │ └── ms │ └── MonitorDashboardApplicationTests.java ├── pom.xml ├── push-images.sh ├── service-registry ├── Dockerfile ├── pom.xml └── src │ ├── main │ ├── java │ │ └── demo │ │ │ └── ms │ │ │ └── ServiceRegistryApplication.java │ └── resources │ │ ├── bootstrap.yml │ │ └── logback-spring.xml │ └── test │ └── java │ └── demo │ └── ms │ └── RegistryServiceApplicationTests.java ├── turbine-server ├── Dockerfile ├── pom.xml └── src │ └── main │ ├── java │ └── demo │ │ └── ms │ │ └── TurbineServerApplication.java │ └── resources │ ├── bootstrap.yml │ └── logback-spring.xml ├── undeploy-docker.sh └── undeploy-kubernetes.sh /README.md: -------------------------------------------------------------------------------- 1 | # Microservices Demo 2 | Using Spring Cloud to develop microservices application and deploy to Kubernetes. 3 | 4 | * Externalized configuration: Spring Cloud Config 5 | * Service registry and discovery: Eureka 6 | * Client side load balancing: Ribbon, Feign 7 | * API gateway: Zuul, Spring Cloud Gateway 8 | * Circuit breaker: Hystrix, Turbine 9 | * Distributed tracing: Sleuth, Zipkin 10 | 11 | ## Deployment 12 | * `build-images.sh` is used to build docker images. 13 | It tags the images with `192.168.99.100:5000` prefix for uploading them to a local registry server on `192.168.99.100`. 14 | Update to the correct address if your registry server's address is not `192.168.99.100:5000`. 15 | If you don't deploy to Kubernetes, you can remove those prefix. 16 | * `push-images.sh` is used to push docker images to the registry server. 17 | * `deploy-docker.sh` is used to deploy the demo app to docker using `docker-compose.yml`. 18 | * `undeploy-docker.sh` is used to undeploy the demo app in docker. 19 | * `deploy-kubernetes.sh` is used to deploy the demo app to kubernetes. 20 | To deploy to Kubernetes, this demo uses `Minikube`. 21 | * `undeploy-kubernetes.sh` is used to undeploy the demo app in kubernetes. 22 | * This demo use Spring Cloud Gateway(cloud-gateway) as its api gateway, if you wants to use Zuul, using api-gateway instead. 23 | 24 | There are three ways to deploy this demo application. 25 | ### Local Deployment 26 | * You need to provide RabbitMQ and Zipkin services. 27 | - RabbitMQ: 28 | 29 | Please see https://www.rabbitmq.com/download.html 30 | 31 | If using docker, you can use the following command: 32 | ``` 33 | docker run -d -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=amqp --name rabbitmq rabbitmq:management 34 | ``` 35 | 36 | - Zipkin: 37 | 38 | Please see https://github.com/openzipkin/zipkin 39 | ``` 40 | # Download Zipkin server 41 | curl -sSL https://zipkin.io/quickstart.sh | bash -s 42 | # Start Zipkin server 43 | RABBIT_ADDRESSES=localhost RABBIT_USER=admin RABBIT_PASSWORD=amqp java -jar zipkin.jar 44 | ``` 45 | 46 | If using docker, you can use the following command: 47 | ``` 48 | docker run -d -p 9411:9411 -e RABBIT_ADDRESSES=rabbitmq -e RABBIT_USER=admin -e RABBIT_PASSWORD=amqp --link rabbitmq:rabbitmq --name zipkin openzipkin/zipkin 49 | ``` 50 | 51 | * Edit `config-server/src/main/resources/application.properties`: 52 | * Make sure `RABBITMQ_SERVICE_HOST`, `RABBITMQ_SERVICE_PORT`, `RABBITMQ_SERVICE_USERNAME` and `RABBITMQ_SERVICE_PASSWORD` 53 | match your RabbitMQ configuration. 54 | * Set `APP_CONFIG_DIR` and `APP_LOGGINH_DIR` to your correct file path. 55 | * Use `java -jar` to start each service, config-server should be started first, service-registry next, then other services. 56 | * Access services with its URL, eg: 57 | ``` 58 | cloud-gateway: http://localhost:8080/ 59 | auth-service: http://localhost:9000/ 60 | account-service: http://localhost:9010/ 61 | service-registry: http://localhost:8761/ 62 | config-server: http://localhost:8888/ 63 | turbine-server: http://localhost:8989/ 64 | monitor-dashboard: http://localhost:7979/ 65 | zipkin: http://localhost:9411/ 66 | ``` 67 | 68 | ### Docker Deployment 69 | * Download rabbitmq:management image. 70 | Tag the image with `192.168.99.100:5000/rabbitmq:management` if you use `192.168.99.100:5000` prefix in `build-images.sh`. 71 | * Download openzipkin/zipkin image. 72 | Tag the image with `192.168.99.100:5000/zipkin` if you use `192.168.99.100:5000` prefix in `build-images.sh`. 73 | * Edit `microservices-demo/deploy-docker.sh`, set `APP_CONFIG_DIR` and `APP_LOGGINH_DIR` to your correct file path. 74 | * Run the script `microservices-demo/build-images.sh` to build docker images. 75 | * Run the script `microservices-demo/deploy-docker.sh` to start containers. 76 | * Run the script `microservices-demo/undeploy-docker.sh` if you want to stop and remove the containers. 77 | * Access a service with its URL (same as Local Deployment) 78 | 79 | ### Kubernetes Deployment (v1.10.0) 80 | * Download rabbitmq:management image. 81 | Tag the image with `192.168.99.100:5000/rabbitmq:management`. 82 | * Download openzipkin/zipkin image. 83 | Tag the image with `192.168.99.100:5000/zipkin`. 84 | * Edit `microservices-demo/kubernetes/config-pv.yaml`, set `spec.hostPath.path` to your correct file path. 85 | Edit `microservices-demo/kubernetes/log-pv.yaml`, set `spec.hostPath.path` to your correct file path. 86 | * Run the script `microservices-demo/build-images.sh` to build docker images. 87 | * Run the script `microservices-demo/push-images.sh` to upload docker images to registry server. 88 | * Run the script `microservices-demo/deploy-kubernetes.sh` to deploy to Kubernetes. 89 | * Run the script `microservices-demo/undeploy-kubernetes.sh` if you want to stop and remove the containers. 90 | * Access services using command `minikube service SERVICE_NAME` eg: 91 | ``` 92 | minikube service cloud-gateway 93 | minikube service auth-service 94 | minikube service account-service 95 | minikube service service-registry 96 | minikube service config-server 97 | minikube service monitor-dashboard 98 | minikube service zipkin 99 | ``` 100 | -------------------------------------------------------------------------------- /account-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/account-service.jar ./account-service.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "account-service.jar"] -------------------------------------------------------------------------------- /account-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | account-service 7 | account-service 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | 28 | 29 | org.springframework.cloud 30 | spring-cloud-starter-config 31 | 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-bus-amqp 36 | 37 | 38 | 39 | 40 | org.springframework.cloud 41 | spring-cloud-starter-netflix-eureka-client 42 | 43 | 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-netflix-hystrix 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-netflix-hystrix-stream 52 | 53 | 54 | org.springframework.cloud 55 | spring-cloud-starter-stream-rabbit 56 | 57 | 58 | 59 | 60 | org.springframework.cloud 61 | spring-cloud-starter-zipkin 62 | 63 | 64 | org.springframework.amqp 65 | spring-rabbit 66 | 67 | 68 | 69 | 70 | org.springframework.cloud 71 | spring-cloud-starter-openfeign 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-test 77 | test 78 | 79 | 80 | 81 | 82 | ${project.name} 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/AccountServiceApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 6 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 7 | import org.springframework.cloud.openfeign.EnableFeignClients; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.web.client.RestTemplate; 10 | 11 | @SpringBootApplication 12 | @EnableCircuitBreaker 13 | @EnableFeignClients 14 | public class AccountServiceApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(AccountServiceApplication.class, args); 18 | } 19 | 20 | /** 21 | * to enable Ribbon support. 22 | */ 23 | @LoadBalanced 24 | @Bean 25 | RestTemplate restTemplate() { 26 | return new RestTemplate(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/client/AuthServiceClient.java: -------------------------------------------------------------------------------- 1 | package demo.ms.client; 2 | 3 | import static org.springframework.web.bind.annotation.RequestMethod.DELETE; 4 | import static org.springframework.web.bind.annotation.RequestMethod.GET; 5 | import static org.springframework.web.bind.annotation.RequestMethod.POST; 6 | 7 | import java.util.List; 8 | 9 | import org.springframework.cloud.openfeign.FeignClient; 10 | import org.springframework.http.MediaType; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | 14 | import demo.ms.vo.User; 15 | 16 | @FeignClient(name = "auth-service", fallback = AuthServiceClientFallback.class) 17 | public interface AuthServiceClient { 18 | 19 | @RequestMapping(path = "/users", method = GET) 20 | List getUsers(); 21 | 22 | @RequestMapping(path = "/users/{username}", method = GET) 23 | User getUser(@PathVariable("username") String username); 24 | 25 | @RequestMapping(path = "/users", method = POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) 26 | User createUser(User user); 27 | 28 | // deleteUser is used to demo circuit breaker fallback 29 | @RequestMapping(path = "/users/{username}", method = DELETE) 30 | void deleteUser(@PathVariable("username") String username); 31 | 32 | } -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/client/AuthServiceClientFallback.java: -------------------------------------------------------------------------------- 1 | package demo.ms.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.stereotype.Component; 9 | 10 | import demo.ms.vo.User; 11 | 12 | @Component 13 | public class AuthServiceClientFallback implements AuthServiceClient { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(AuthServiceClientFallback.class); 16 | 17 | @Override 18 | public List getUsers() { 19 | log.warn("Fallback method is called for getting users"); 20 | return new ArrayList(); 21 | } 22 | 23 | @Override 24 | public User getUser(String username) { 25 | log.warn("Fallback method is called for getting user {}", username); 26 | return new User(); 27 | } 28 | 29 | @Override 30 | public User createUser(User user) { 31 | log.warn("Fallback method is called for creating user {}", user.getUsername()); 32 | return user; 33 | } 34 | 35 | @Override 36 | public void deleteUser(String username) { 37 | log.warn("Fallback method is called for deleting user {}", username); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/client/AuthServiceRibbonClient.java: -------------------------------------------------------------------------------- 1 | package demo.ms.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.client.RestTemplate; 11 | 12 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; 13 | 14 | import demo.ms.vo.User; 15 | 16 | @Component 17 | public class AuthServiceRibbonClient { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(AuthServiceRibbonClient.class); 20 | 21 | private RestTemplate restTemplate; 22 | 23 | public AuthServiceRibbonClient(RestTemplate restTemplate) { 24 | this.restTemplate = restTemplate; 25 | } 26 | 27 | @HystrixCommand(fallbackMethod = "defaultGetUsers") 28 | public List getUsers() { 29 | User[] users = restTemplate.getForObject("http://auth-service/users", User[].class); 30 | return Arrays.asList(users); 31 | } 32 | 33 | public List defaultGetUsers() { 34 | log.warn("Fallback method is called for getting users"); 35 | return new ArrayList(); 36 | } 37 | 38 | @HystrixCommand(fallbackMethod = "defaultGetUser") 39 | public User getUser(String username) { 40 | return restTemplate.getForObject("http://auth-service/users/{username}", User.class, username); 41 | } 42 | 43 | public User defaultGetUser(String username) { 44 | log.warn("Fallback method is called for getting user {}", username); 45 | return new User(); 46 | } 47 | 48 | @HystrixCommand(fallbackMethod = "defaultDeleteUser") 49 | public void deleteUser(String username) { 50 | restTemplate.delete("http://auth-service/users/{username}", username); 51 | } 52 | 53 | public void defaultDeleteUser(String username) { 54 | log.warn("Fallback method is called for deleting user {}", username); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/domain/Account.java: -------------------------------------------------------------------------------- 1 | package demo.ms.domain; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.Date; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public class Account { 9 | 10 | private String name; 11 | 12 | private Date lastSeen; 13 | 14 | private List incomes; 15 | 16 | private List expenses; 17 | 18 | private Saving saving; 19 | 20 | private String note; 21 | 22 | private Map extra; 23 | 24 | public Account() { 25 | } 26 | 27 | public Account(String name) { 28 | this.name = name; 29 | Saving saving = new Saving(); 30 | saving.setAmount(new BigDecimal(0)); 31 | saving.setCurrency(Currency.getDefault()); 32 | saving.setInterest(new BigDecimal(0)); 33 | saving.setDeposit(false); 34 | saving.setCapitalization(false); 35 | this.saving = saving; 36 | this.lastSeen = new Date(); 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public void setName(String name) { 44 | this.name = name; 45 | } 46 | 47 | public Date getLastSeen() { 48 | return lastSeen; 49 | } 50 | 51 | public void setLastSeen(Date lastSeen) { 52 | this.lastSeen = lastSeen; 53 | } 54 | 55 | public List getIncomes() { 56 | return incomes; 57 | } 58 | 59 | public void setIncomes(List incomes) { 60 | this.incomes = incomes; 61 | } 62 | 63 | public List getExpenses() { 64 | return expenses; 65 | } 66 | 67 | public void setExpenses(List expenses) { 68 | this.expenses = expenses; 69 | } 70 | 71 | public Saving getSaving() { 72 | return saving; 73 | } 74 | 75 | public void setSaving(Saving saving) { 76 | this.saving = saving; 77 | } 78 | 79 | public String getNote() { 80 | return note; 81 | } 82 | 83 | public void setNote(String note) { 84 | this.note = note; 85 | } 86 | 87 | public Map getExtra() { 88 | return extra; 89 | } 90 | 91 | public void setExtra(Map extra) { 92 | this.extra = extra; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/domain/Currency.java: -------------------------------------------------------------------------------- 1 | package demo.ms.domain; 2 | 3 | public enum Currency { 4 | 5 | NT, RMB, USD, EUR; 6 | 7 | public static Currency getDefault() { 8 | return NT; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/domain/Item.java: -------------------------------------------------------------------------------- 1 | package demo.ms.domain; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Item { 6 | 7 | private String title; 8 | 9 | private BigDecimal amount; 10 | 11 | private Currency currency; 12 | 13 | private TimePeriod period; 14 | 15 | private String icon; 16 | 17 | public String getTitle() { 18 | return title; 19 | } 20 | 21 | public void setTitle(String title) { 22 | this.title = title; 23 | } 24 | 25 | public BigDecimal getAmount() { 26 | return amount; 27 | } 28 | 29 | public void setAmount(BigDecimal amount) { 30 | this.amount = amount; 31 | } 32 | 33 | public Currency getCurrency() { 34 | return currency; 35 | } 36 | 37 | public void setCurrency(Currency currency) { 38 | this.currency = currency; 39 | } 40 | 41 | public TimePeriod getPeriod() { 42 | return period; 43 | } 44 | 45 | public void setPeriod(TimePeriod period) { 46 | this.period = period; 47 | } 48 | 49 | public String getIcon() { 50 | return icon; 51 | } 52 | 53 | public void setIcon(String icon) { 54 | this.icon = icon; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/domain/Saving.java: -------------------------------------------------------------------------------- 1 | package demo.ms.domain; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Saving { 6 | 7 | private BigDecimal amount; 8 | 9 | private Currency currency; 10 | 11 | private BigDecimal interest; 12 | 13 | private Boolean deposit; 14 | 15 | private Boolean capitalization; 16 | 17 | public BigDecimal getAmount() { 18 | return amount; 19 | } 20 | 21 | public void setAmount(BigDecimal amount) { 22 | this.amount = amount; 23 | } 24 | 25 | public Currency getCurrency() { 26 | return currency; 27 | } 28 | 29 | public void setCurrency(Currency currency) { 30 | this.currency = currency; 31 | } 32 | 33 | public BigDecimal getInterest() { 34 | return interest; 35 | } 36 | 37 | public void setInterest(BigDecimal interest) { 38 | this.interest = interest; 39 | } 40 | 41 | public Boolean getDeposit() { 42 | return deposit; 43 | } 44 | 45 | public void setDeposit(Boolean deposit) { 46 | this.deposit = deposit; 47 | } 48 | 49 | public Boolean getCapitalization() { 50 | return capitalization; 51 | } 52 | 53 | public void setCapitalization(Boolean capitalization) { 54 | this.capitalization = capitalization; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/domain/TimePeriod.java: -------------------------------------------------------------------------------- 1 | package demo.ms.domain; 2 | 3 | public enum TimePeriod { 4 | 5 | YEAR, QUARTER, MONTH, WEEK, DAY, HOUR 6 | 7 | } 8 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/service/AccountService.java: -------------------------------------------------------------------------------- 1 | package demo.ms.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | import java.util.function.Function; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | import javax.annotation.PostConstruct; 12 | 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.util.StringUtils; 17 | 18 | import demo.ms.client.AuthServiceClient; 19 | import demo.ms.client.AuthServiceRibbonClient; 20 | import demo.ms.domain.Account; 21 | import demo.ms.vo.User; 22 | 23 | @Service 24 | public class AccountService { 25 | 26 | private static final Logger log = LoggerFactory.getLogger(AccountService.class); 27 | 28 | private AuthServiceClient authServiceClient; 29 | 30 | private AuthServiceRibbonClient authServiceRibbonClient; 31 | 32 | private Map accounts; 33 | 34 | public AccountService(AuthServiceClient authServiceClient, AuthServiceRibbonClient authServiceRibbonClient) { 35 | this.authServiceClient = authServiceClient; 36 | this.authServiceRibbonClient = authServiceRibbonClient; 37 | } 38 | 39 | @PostConstruct 40 | public void createAccounts() { 41 | accounts = Stream.of( 42 | new Account("albert"), 43 | new Account("alex"), 44 | new Account("andrew")) 45 | .collect(Collectors.toMap(Account::getName, Function.identity())); 46 | } 47 | 48 | public List getAccounts() { 49 | // to demo ribbon 50 | authServiceRibbonClient.getUsers(); 51 | return new ArrayList(accounts.values()); 52 | } 53 | 54 | public Account getAccount(String name) { 55 | // to demo feign 56 | User user = authServiceClient.getUser(name); 57 | 58 | return Optional.ofNullable(accounts.get(name)) 59 | .map(account -> addUserInfo(account, user)) 60 | .orElseGet(() -> { 61 | log.info("Can not find account {}", name); 62 | return new Account(); 63 | }); 64 | } 65 | 66 | private Account addUserInfo(Account account, User user) { 67 | if (!StringUtils.isEmpty(user.getUsername())) { 68 | // to show which user-service instance is connected 69 | Map extraInfo = user.getExtra(); 70 | account.setExtra(extraInfo); 71 | } 72 | return account; 73 | } 74 | 75 | public Account createAccount(Account newAccount) { 76 | // to demo feign 77 | authServiceClient.createUser(new User(newAccount.getName())); 78 | 79 | accounts.put(newAccount.getName(), newAccount); 80 | return newAccount; 81 | } 82 | 83 | public Account updateAccount(Account account) { 84 | accounts.put(account.getName(), account); 85 | return account; 86 | } 87 | 88 | public void deleteAccount(String name) { 89 | // to demo ribbon with hystrix fallback 90 | authServiceRibbonClient.deleteUser(name); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/vo/User.java: -------------------------------------------------------------------------------- 1 | package demo.ms.vo; 2 | 3 | import java.util.Map; 4 | 5 | public class User { 6 | 7 | private String username; 8 | 9 | private String password; 10 | 11 | private String email; 12 | 13 | private Map extra; 14 | 15 | public User() { 16 | } 17 | 18 | public User(String username) { 19 | super(); 20 | this.username = username; 21 | } 22 | 23 | public String getUsername() { 24 | return username; 25 | } 26 | 27 | public void setUsername(String username) { 28 | this.username = username; 29 | } 30 | 31 | public String getPassword() { 32 | return password; 33 | } 34 | 35 | public void setPassword(String password) { 36 | this.password = password; 37 | } 38 | 39 | public String getEmail() { 40 | return email; 41 | } 42 | 43 | public void setEmail(String email) { 44 | this.email = email; 45 | } 46 | 47 | public Map getExtra() { 48 | return extra; 49 | } 50 | 51 | public void setExtra(Map extra) { 52 | this.extra = extra; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /account-service/src/main/java/demo/ms/web/controller/AccountController.java: -------------------------------------------------------------------------------- 1 | package demo.ms.web.controller; 2 | 3 | import java.util.List; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.web.bind.annotation.DeleteMapping; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.PutMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import demo.ms.domain.Account; 16 | import demo.ms.service.AccountService; 17 | 18 | @RestController 19 | public class AccountController { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(AccountController.class); 22 | 23 | private AccountService accountService; 24 | 25 | public AccountController(AccountService accountService) { 26 | this.accountService = accountService; 27 | } 28 | 29 | @GetMapping({"", "/", "/accounts"}) 30 | public List getAccounts() { 31 | return accountService.getAccounts(); 32 | } 33 | 34 | @GetMapping("/accounts/{name}") 35 | public Account getAccount(@PathVariable String name) { 36 | // log is used to demo sleuth: add span and trace IDs to log 37 | log.debug("Get account {}", name); 38 | return accountService.getAccount(name); 39 | } 40 | 41 | @PostMapping("/accounts") 42 | public Account createAccount(@RequestBody Account account) { 43 | log.info("Create account {}", account.getName()); 44 | return accountService.createAccount(account); 45 | } 46 | 47 | @PutMapping("/accounts/{name}") 48 | public Account updateAccount(@PathVariable String name, @RequestBody Account account) { 49 | log.info("Update account {}", name); 50 | return accountService.updateAccount(account); 51 | } 52 | 53 | @DeleteMapping("/accounts/{name}") 54 | public void deleteAccount(@PathVariable String name) { 55 | log.info("Delete account {}", name); 56 | accountService.deleteAccount(name); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /account-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 9010 5 | 6 | spring: 7 | application: 8 | name: account-service 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /account-service/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /account-service/src/test/java/demo/ms/AccountServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class AccountServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /api-gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/api-gateway.jar ./api-gateway.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "api-gateway.jar"] -------------------------------------------------------------------------------- /api-gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | api-gateway 7 | api-gateway 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-actuator 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-config 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-starter-bus-amqp 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-netflix-eureka-client 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-netflix-hystrix 39 | 40 | 41 | org.springframework.cloud 42 | spring-cloud-netflix-hystrix-stream 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-starter-stream-rabbit 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-zipkin 52 | 53 | 54 | org.springframework.amqp 55 | spring-rabbit 56 | 57 | 58 | 59 | 60 | org.springframework.cloud 61 | spring-cloud-starter-netflix-zuul 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-test 67 | test 68 | 69 | 70 | 71 | 72 | ${project.name} 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-maven-plugin 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/demo/ms/ApiGatewayApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.boot.web.servlet.error.ErrorController; 10 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 11 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 12 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.http.HttpHeaders; 15 | import org.springframework.http.HttpStatus; 16 | import org.springframework.http.MediaType; 17 | import org.springframework.http.ResponseEntity; 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.util.StringUtils; 20 | import org.springframework.web.bind.annotation.GetMapping; 21 | import org.springframework.web.client.RestTemplate; 22 | 23 | import demo.ms.fallback.AccountServiceFallbackProvider; 24 | import demo.ms.fallback.AuthServiceFallbackProvider; 25 | 26 | @SpringBootApplication 27 | @EnableZuulProxy 28 | @EnableCircuitBreaker 29 | public class ApiGatewayApplication { 30 | 31 | public static void main(String[] args) { 32 | SpringApplication.run(ApiGatewayApplication.class, args); 33 | } 34 | 35 | /** 36 | * To enable Ribbon and retry support. 37 | */ 38 | @LoadBalanced 39 | @Bean 40 | RestTemplate restTemplate() { 41 | return new RestTemplate(); 42 | } 43 | 44 | /** 45 | * To provide Hystrix fallback. 46 | */ 47 | @Bean 48 | AuthServiceFallbackProvider authServiceFallbackProvider() { 49 | return new AuthServiceFallbackProvider(); 50 | } 51 | 52 | @Bean 53 | AccountServiceFallbackProvider accountServiceFallbackProvider() { 54 | return new AccountServiceFallbackProvider(); 55 | } 56 | 57 | @Controller 58 | static class WebController implements ErrorController { 59 | 60 | private static final Logger log = LoggerFactory.getLogger(WebController.class); 61 | 62 | private static final String ERROR_MAPPING = "/error"; 63 | 64 | @Override 65 | public String getErrorPath() { 66 | return ERROR_MAPPING; 67 | } 68 | 69 | @GetMapping({ "", "/" }) 70 | public String index() { 71 | // for demo 72 | return "redirect:/acct/accounts"; 73 | } 74 | 75 | /** 76 | * Using JSON message instead of error page. 77 | * @param request 78 | * @return 79 | */ 80 | @GetMapping(ERROR_MAPPING) 81 | public ResponseEntity error(HttpServletRequest request) { 82 | Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); 83 | String errorMsg = (String) request.getAttribute("javax.servlet.error.message"); 84 | log.warn("Got error: {} {}", statusCode, errorMsg); 85 | 86 | HttpStatus status = HttpStatus.valueOf(statusCode); 87 | 88 | if (StringUtils.isEmpty(errorMsg)) { 89 | errorMsg = status.name(); 90 | } 91 | 92 | HttpHeaders headers = new HttpHeaders(); 93 | headers.setContentType(MediaType.APPLICATION_JSON); 94 | 95 | return new ResponseEntity(errorMsg, headers, status); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/demo/ms/fallback/AccountServiceFallbackProvider.java: -------------------------------------------------------------------------------- 1 | package demo.ms.fallback; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.client.ClientHttpResponse; 12 | 13 | import com.netflix.hystrix.exception.HystrixTimeoutException; 14 | 15 | public class AccountServiceFallbackProvider implements FallbackProvider { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(AccountServiceFallbackProvider.class); 18 | 19 | @Override 20 | public String getRoute() { 21 | return "account-service"; 22 | } 23 | 24 | @Override 25 | public ClientHttpResponse fallbackResponse(String route, final Throwable cause) { 26 | log.error("Fallback for error: {}", cause.getMessage()); 27 | if (cause instanceof HystrixTimeoutException) { 28 | return response(HttpStatus.GATEWAY_TIMEOUT); 29 | } else { 30 | return response(HttpStatus.INTERNAL_SERVER_ERROR); 31 | } 32 | } 33 | 34 | private ClientHttpResponse response(final HttpStatus status) { 35 | return new SimpleClientHttpResponse(status) { 36 | @Override 37 | public InputStream getBody() throws IOException { 38 | return new ByteArrayInputStream("Account Service Unavailabe!".getBytes()); 39 | } 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/demo/ms/fallback/AuthServiceFallbackProvider.java: -------------------------------------------------------------------------------- 1 | package demo.ms.fallback; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.client.ClientHttpResponse; 12 | 13 | import com.netflix.hystrix.exception.HystrixTimeoutException; 14 | 15 | public class AuthServiceFallbackProvider implements FallbackProvider { 16 | 17 | private static final Logger log = LoggerFactory.getLogger(AuthServiceFallbackProvider.class); 18 | 19 | @Override 20 | public String getRoute() { 21 | return "auth-service"; 22 | } 23 | 24 | @Override 25 | public ClientHttpResponse fallbackResponse(String route, final Throwable cause) { 26 | log.error("Fallback for error: {}", cause.getMessage()); 27 | if (cause instanceof HystrixTimeoutException) { 28 | return response(HttpStatus.GATEWAY_TIMEOUT); 29 | } else { 30 | return response(HttpStatus.INTERNAL_SERVER_ERROR); 31 | } 32 | } 33 | 34 | private ClientHttpResponse response(final HttpStatus status) { 35 | return new SimpleClientHttpResponse(status) { 36 | @Override 37 | public InputStream getBody() throws IOException { 38 | return new ByteArrayInputStream("Auth Service Unavailabe!".getBytes()); 39 | } 40 | }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/demo/ms/fallback/SimpleClientHttpResponse.java: -------------------------------------------------------------------------------- 1 | package demo.ms.fallback; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | import org.springframework.http.HttpHeaders; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.http.client.ClientHttpResponse; 11 | 12 | public class SimpleClientHttpResponse implements ClientHttpResponse { 13 | 14 | private final HttpStatus status; 15 | 16 | public SimpleClientHttpResponse(HttpStatus status) { 17 | super(); 18 | this.status = status; 19 | } 20 | 21 | @Override 22 | public HttpStatus getStatusCode() throws IOException { 23 | return status; 24 | } 25 | 26 | @Override 27 | public int getRawStatusCode() throws IOException { 28 | return status.value(); 29 | } 30 | 31 | @Override 32 | public String getStatusText() throws IOException { 33 | return status.getReasonPhrase(); 34 | } 35 | 36 | @Override 37 | public void close() { 38 | } 39 | 40 | @Override 41 | public InputStream getBody() throws IOException { 42 | return new ByteArrayInputStream("fallback".getBytes()); 43 | } 44 | 45 | @Override 46 | public HttpHeaders getHeaders() { 47 | HttpHeaders headers = new HttpHeaders(); 48 | headers.setContentType(MediaType.APPLICATION_JSON); 49 | return headers; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /api-gateway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 8080 5 | 6 | spring: 7 | application: 8 | name: api-gateway 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /api-gateway/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /api-gateway/src/test/java/demo/ms/ApiGatewayApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class ApiGatewayApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /auth-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/auth-service.jar ./auth-service.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "auth-service.jar"] -------------------------------------------------------------------------------- /auth-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | auth-service 7 | auth-service 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-config 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-bus-amqp 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-netflix-eureka-client 39 | 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-netflix-hystrix 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-netflix-hystrix-stream 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-stream-rabbit 52 | 53 | 54 | 55 | org.springframework.cloud 56 | spring-cloud-starter-zipkin 57 | 58 | 59 | org.springframework.amqp 60 | spring-rabbit 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | ${project.name} 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /auth-service/src/main/java/demo/ms/AuthServiceApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; 6 | 7 | @SpringBootApplication 8 | @EnableCircuitBreaker 9 | public class AuthServiceApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(AuthServiceApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /auth-service/src/main/java/demo/ms/domain/User.java: -------------------------------------------------------------------------------- 1 | package demo.ms.domain; 2 | 3 | import java.util.Map; 4 | 5 | public class User { 6 | 7 | private String username; 8 | 9 | private String password; 10 | 11 | private String email; 12 | 13 | private Map extra; 14 | 15 | public User() { 16 | } 17 | 18 | public User(String username) { 19 | this.username = username; 20 | } 21 | 22 | public String getUsername() { 23 | return username; 24 | } 25 | 26 | public void setUsername(String username) { 27 | this.username = username; 28 | } 29 | 30 | public String getPassword() { 31 | return password; 32 | } 33 | 34 | public void setPassword(String password) { 35 | this.password = password; 36 | } 37 | 38 | public String getEmail() { 39 | return email; 40 | } 41 | 42 | public void setEmail(String email) { 43 | this.email = email; 44 | } 45 | 46 | public Map getExtra() { 47 | return extra; 48 | } 49 | 50 | public void setExtra(Map extra) { 51 | this.extra = extra; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /auth-service/src/main/java/demo/ms/service/UserService.java: -------------------------------------------------------------------------------- 1 | package demo.ms.service; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.Stream; 7 | 8 | import javax.annotation.PostConstruct; 9 | 10 | import org.springframework.stereotype.Service; 11 | 12 | import demo.ms.domain.User; 13 | import demo.ms.util.IPUtils; 14 | 15 | @Service 16 | public class UserService { 17 | 18 | private List users; 19 | 20 | @PostConstruct 21 | public void createUsers() { 22 | users = Stream.of( 23 | new User("albert"), 24 | new User("alex"), 25 | new User("andrew")) 26 | .collect(Collectors.toList()); 27 | } 28 | 29 | public List getUsers() { 30 | return users; 31 | } 32 | 33 | public User getUser(String username) { 34 | return users.stream() 35 | .filter(user -> user.getUsername().equals(username)) 36 | .findFirst() 37 | .map(this::addExtraInfo) 38 | .orElseThrow(() -> new IllegalArgumentException("Can not find user " + username)); 39 | } 40 | 41 | private User addExtraInfo(User user) { 42 | // add service instance info to demo load balancing 43 | Map extra = IPUtils.getHostnameAndAddress(); 44 | user.setExtra(extra); 45 | return user; 46 | } 47 | 48 | public User createUser(User user) { 49 | users.add(user); 50 | return user; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /auth-service/src/main/java/demo/ms/util/IPUtils.java: -------------------------------------------------------------------------------- 1 | package demo.ms.util; 2 | 3 | import java.net.InetAddress; 4 | import java.net.UnknownHostException; 5 | import java.util.LinkedHashMap; 6 | import java.util.Map; 7 | 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class IPUtils { 12 | 13 | private static final Logger log = LoggerFactory.getLogger(IPUtils.class); 14 | 15 | public static Map getHostnameAndAddress() { 16 | Map info = new LinkedHashMap<>(); 17 | try { 18 | InetAddress inetAddress = InetAddress.getLocalHost(); 19 | String serverAddress = inetAddress.getHostAddress(); 20 | String serverName = inetAddress.getHostName(); 21 | 22 | info.put("authServiceAddress", serverAddress); 23 | info.put("authServiceName", serverName); 24 | } catch (UnknownHostException e) { 25 | log.error(e.getMessage(), e); 26 | } 27 | return info; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /auth-service/src/main/java/demo/ms/web/controller/ControllerExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package demo.ms.web.controller; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.MediaType; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.ControllerAdvice; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | 10 | @ControllerAdvice 11 | public class ControllerExceptionHandler { 12 | 13 | @ExceptionHandler(Exception.class) 14 | public ResponseEntity handleException(Exception e) { 15 | String body = e.getMessage(); 16 | 17 | HttpHeaders headers = new HttpHeaders(); 18 | headers.setContentType(MediaType.APPLICATION_JSON); 19 | 20 | HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; 21 | 22 | ResponseEntity responseEntity = new ResponseEntity(body, headers, status); 23 | return responseEntity; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /auth-service/src/main/java/demo/ms/web/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package demo.ms.web.controller; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.cloud.context.config.annotation.RefreshScope; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import demo.ms.domain.User; 17 | import demo.ms.service.UserService; 18 | import demo.ms.util.IPUtils; 19 | 20 | @RefreshScope 21 | @RestController 22 | public class UserController { 23 | 24 | private static final Logger log = LoggerFactory.getLogger(UserController.class); 25 | 26 | // to demo @RefreshScope 27 | private String welcomeMsg; 28 | 29 | private UserService userService; 30 | 31 | public UserController(UserService userService, @Value("${welcome.msg}") String welcomeMsg) { 32 | this.userService = userService; 33 | this.welcomeMsg = welcomeMsg; 34 | } 35 | 36 | @GetMapping({"", "/"}) 37 | public Map hello() { 38 | // add service instance info to show which service instance is refreshed 39 | Map info = IPUtils.getHostnameAndAddress(); 40 | info.put("welcomeMessage", welcomeMsg); 41 | return info; 42 | } 43 | 44 | @GetMapping("/users") 45 | public List getUsers() { 46 | return userService.getUsers(); 47 | } 48 | 49 | @GetMapping("/users/{username}") 50 | public User getUser(@PathVariable String username) { 51 | // log is used to demo sleuth: add span and trace IDs to log 52 | log.debug("Get user {}", username); 53 | return userService.getUser(username); 54 | } 55 | 56 | @PostMapping("/users") 57 | public User createUser(@RequestBody User user) { 58 | log.info("Create user {}", user); 59 | return userService.createUser(user); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /auth-service/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 9000 5 | 6 | spring: 7 | application: 8 | name: auth-service 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /auth-service/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /auth-service/src/test/java/demo/ms/AuthServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class AuthServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /build-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | docker build -t 192.168.99.100:5000/config-server ./config-server 6 | docker build -t 192.168.99.100:5000/service-registry ./service-registry 7 | docker build -t 192.168.99.100:5000/turbine-server ./turbine-server 8 | docker build -t 192.168.99.100:5000/monitor-dashboard ./monitor-dashboard 9 | docker build -t 192.168.99.100:5000/auth-service ./auth-service 10 | docker build -t 192.168.99.100:5000/account-service ./account-service 11 | docker build -t 192.168.99.100:5000/cloud-gateway ./cloud-gateway 12 | 13 | docker rmi $(docker images -f "dangling=true" -q) 14 | -------------------------------------------------------------------------------- /cloud-gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/cloud-gateway.jar ./cloud-gateway.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "cloud-gateway.jar"] -------------------------------------------------------------------------------- /cloud-gateway/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | cloud-gateway 7 | cloud-gateway 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-actuator 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-config 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-starter-bus-amqp 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-netflix-eureka-client 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-netflix-hystrix 39 | 40 | 41 | org.springframework.cloud 42 | spring-cloud-netflix-hystrix-stream 43 | 44 | 45 | org.springframework.cloud 46 | spring-cloud-starter-stream-rabbit 47 | 48 | 49 | 50 | org.springframework.cloud 51 | spring-cloud-starter-zipkin 52 | 53 | 54 | org.springframework.amqp 55 | spring-rabbit 56 | 57 | 58 | 59 | org.springframework.cloud 60 | spring-cloud-starter-gateway 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-starter-test 66 | test 67 | 68 | 69 | 70 | 71 | ${project.name} 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /cloud-gateway/src/main/java/demo/ms/CloudGatewayApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.gateway.route.RouteLocator; 6 | import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import reactor.core.publisher.Mono; 12 | 13 | @SpringBootApplication 14 | public class CloudGatewayApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(CloudGatewayApplication.class, args); 18 | } 19 | 20 | @Bean 21 | public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { 22 | return builder.routes() 23 | // shortcut for index page 24 | // it may be better to use a UI microservice 25 | .route("index", p -> p.path("/").or().path("/index.html") 26 | .filters(f -> f.setPath("/static/index.html")) 27 | .uri("lb://cloud-gateway")) 28 | // set larger order to put this route to lower priority, so it wouldn't override other routes 29 | .route("not_found", p -> p.order(1000).path("/static/**").negate() 30 | .filters(f -> f.setPath("/not-found")) 31 | .uri("lb://cloud-gateway")) 32 | .build(); 33 | } 34 | 35 | @RestController 36 | static class WebController { 37 | 38 | @GetMapping("/not-found") 39 | public Mono notFound() { 40 | return Mono.just("Not found!"); 41 | } 42 | 43 | @GetMapping("/auth-service-fallback") 44 | public Mono authServiceFallback() { 45 | return Mono.just("Auth Service Unavailabe!"); 46 | } 47 | 48 | @GetMapping("/account-service-fallback") 49 | public Mono accountServiceFallback() { 50 | return Mono.just("Account Service Unavailabe!"); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cloud-gateway/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 8080 5 | 6 | spring: 7 | application: 8 | name: cloud-gateway 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /cloud-gateway/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /cloud-gateway/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /cloud-gateway/src/test/java/demo/ms/CloudGatewayApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class CloudGatewayApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /config-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/config-server.jar ./config-server.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "config-server.jar"] -------------------------------------------------------------------------------- /config-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | config-server 7 | config-server 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-actuator 20 | 21 | 22 | 23 | 24 | org.springframework.cloud 25 | spring-cloud-config-server 26 | 27 | 28 | 29 | 30 | org.springframework.cloud 31 | spring-cloud-config-monitor 32 | 33 | 34 | 35 | 36 | org.springframework.cloud 37 | spring-cloud-starter-bus-amqp 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | 47 | 48 | ${project.name} 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /config-server/src/main/java/demo/ms/ConfigServerApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.config.server.EnableConfigServer; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | 9 | @SpringBootApplication 10 | @EnableConfigServer 11 | public class ConfigServerApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ConfigServerApplication.class, args); 15 | } 16 | 17 | @Controller 18 | static class WebController { 19 | 20 | @GetMapping({"", "/"}) 21 | public String index() { 22 | return "redirect:/health"; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /config-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | ### Default configuration, these properties should be set with environment variables in docker/Kubernetes. 2 | APP_CONFIG_DIR: /Users/yenchu/git/microservices-demo/config-server/src/main/resources/shared 3 | APP_LOGGING_DIR: /Users/yenchu/log/microservices-demo 4 | SERVICE_REGISTRY_URL: http://127.0.0.1:8761/eureka/ 5 | RABBITMQ_SERVICE_HOST: localhost 6 | RABBITMQ_SERVICE_PORT: 5672 7 | RABBITMQ_SERVICE_USERNAME: admin 8 | RABBITMQ_SERVICE_PASSWORD: amqp 9 | 10 | logging: 11 | file: ${APP_LOGGING_DIR}/${spring.application.name}.log 12 | level: 13 | demo: DEBUG 14 | 15 | spring: 16 | rabbitmq: 17 | host: ${RABBITMQ_SERVICE_HOST} 18 | port: ${RABBITMQ_SERVICE_PORT} 19 | username: ${RABBITMQ_SERVICE_USERNAME} 20 | password: ${RABBITMQ_SERVICE_PASSWORD} 21 | cloud: 22 | config: 23 | server: 24 | native: 25 | search-locations: file:${APP_CONFIG_DIR} 26 | # To override downstream services configuration 27 | overrides: 28 | APP_LOGGING_DIR: ${APP_LOGGING_DIR} 29 | SERVICE_REGISTRY_URL: ${SERVICE_REGISTRY_URL} 30 | spring.rabbitmq.host: ${RABBITMQ_SERVICE_HOST} 31 | spring.rabbitmq.port: ${RABBITMQ_SERVICE_PORT} 32 | spring.rabbitmq.username: ${RABBITMQ_SERVICE_USERNAME} 33 | spring.rabbitmq.password: ${RABBITMQ_SERVICE_PASSWORD} 34 | profiles: 35 | # Using local filesystem for demo 36 | active: native 37 | -------------------------------------------------------------------------------- /config-server/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8888 3 | 4 | spring: 5 | application: 6 | name: config-server 7 | main: 8 | banner-mode: "off" 9 | mvc: 10 | favicon.enabled: false 11 | -------------------------------------------------------------------------------- /config-server/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/account-service.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | instance: 3 | metadata-map: 4 | # The turbine stream cluster it should be aggragated to 5 | cluster: USERS 6 | 7 | feign: 8 | client: 9 | config: 10 | default: 11 | loggerLevel: basic 12 | # To enable Hystrix in Feign 13 | hystrix: 14 | enabled: true 15 | 16 | # Enable retry for Feign client 17 | auth-service: 18 | ribbon: 19 | MaxAutoRetries: 1 20 | MaxAutoRetriesNextServer: 1 21 | ReadTimeout: 1000 22 | 23 | # Make sure the timeout of hystrix is longer then the retry of feign client 24 | hystrix: 25 | command.default.execution.isolation.thread.timeoutInMilliseconds: 5000 26 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/api-gateway.yml: -------------------------------------------------------------------------------- 1 | zuul: 2 | retryable: true 3 | ignoredServices: '*' 4 | host: 5 | connect-timeout-millis: 20000 6 | socket-timeout-millis: 20000 7 | routes: 8 | auth-service: 9 | path: /auth/** 10 | account-service: 11 | path: /acct/** 12 | static: 13 | path: /static/** 14 | 15 | ribbon: 16 | MaxAutoRetries: 1 17 | MaxAutoRetriesNextServer: 1 18 | ReadTimeout: 1000 19 | 20 | hystrix: 21 | command.default.execution.isolation.thread.timeoutInMilliseconds: 5000 22 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | file: ${APP_LOGGING_DIR}/${spring.application.name}.log 3 | level: 4 | demo: DEBUG 5 | 6 | # eureka.client.healthcheck.enabled=true should only be set in application.yml 7 | eureka: 8 | instance: 9 | prefer-ip-address: true 10 | client: 11 | healthcheck: 12 | enabled: true 13 | serviceUrl: 14 | defaultZone: ${SERVICE_REGISTRY_URL} 15 | 16 | # Integrating rabbitmq for turbine stream and push refresh config 17 | # Being overrided by config server 18 | spring: 19 | rabbitmq: 20 | host: ${RABBITMQ_SERVICE_HOST} 21 | port: ${RABBITMQ_SERVICE_PORT} 22 | username: ${RABBITMQ_SERVICE_USERNAME} 23 | password: ${RABBITMQ_SERVICE_PASSWORD} 24 | sleuth: 25 | sampler: 26 | # Recording all spans for demo 27 | probability: 1.0 28 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/auth-service.yml: -------------------------------------------------------------------------------- 1 | # To demo @RefreshScope 2 | welcome.msg: Hello! 3 | 4 | eureka: 5 | instance: 6 | metadata-map: 7 | # The turbine stream cluster it should be aggragated to 8 | cluster: USERS 9 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/cloud-gateway.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | gateway: 4 | discovery: 5 | locator: 6 | enabled: true 7 | routes: 8 | - id: auth_service 9 | uri: lb://auth-service 10 | predicates: 11 | - Path=/auth/** 12 | filters: 13 | # trim prifix path /auth 14 | - RewritePath=/auth/(?.*), /$\{segment} 15 | # retry 2 times for http status NOT_FOUND, INTERNAL_SERVER_ERROR 16 | - name: Retry 17 | args: 18 | retries: 2 19 | statuses: NOT_FOUND, INTERNAL_SERVER_ERROR 20 | # set fallback uri if the backend service cannot be connected 21 | - name: Hystrix 22 | args: 23 | name: auth-api 24 | fallbackUri: forward:/auth-service-fallback 25 | - id: account_service 26 | uri: lb://account-service 27 | predicates: 28 | - Path=/acct/** 29 | filters: 30 | - RewritePath=/acct/(?.*), /$\{segment} 31 | - name: Retry 32 | args: 33 | retries: 2 34 | statuses: NOT_FOUND, INTERNAL_SERVER_ERROR 35 | - name: Hystrix 36 | args: 37 | name: account-api 38 | fallbackUri: forward:/account-service-fallback 39 | # static resources path pattern 40 | webflux.static-path-pattern: /static/** 41 | 42 | # config global Hystrix timeout 43 | hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000 44 | 45 | # just for debug 46 | logging: 47 | level: 48 | org.springframework.cloud.gateway: DEBUG 49 | org.springframework.http.server.reactive: DEBUG 50 | org.springframework.web.reactive: DEBUG 51 | 52 | # demo actuator-related urls 53 | management.endpoints.web.exposure.include: '*' 54 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/monitor-dashboard.yml: -------------------------------------------------------------------------------- 1 | # for demo 2 | turbine: 3 | aggregator: 4 | clusterConfig: USERS -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/service-registry.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | client: 3 | # Disable the client side behaviour in standalone mode 4 | registerWithEureka: false 5 | fetchRegistry: false 6 | serviceUrl: 7 | defaultZone: http://127.0.0.1:8761/eureka/ 8 | -------------------------------------------------------------------------------- /config-server/src/main/resources/shared/turbine-server.yml: -------------------------------------------------------------------------------- 1 | turbine: 2 | aggregator: 3 | clusterConfig: USERS 4 | appConfig: account-service,auth-service 5 | # To find which cluster the apps should be aggragated to 6 | clusterNameExpression: metadata['cluster'] 7 | -------------------------------------------------------------------------------- /config-server/src/test/java/demo/ms/ConfigServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class ConfigServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /deploy-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export APP_CONFIG_DIR=/Users/yenchu/git/microservices-demo/config-server/src/main/resources/shared 6 | export APP_LOGGING_DIR=/Users/yenchu/log/microservices-demo 7 | 8 | docker-compose up -d 9 | -------------------------------------------------------------------------------- /deploy-kubernetes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | kubectl create -f kubernetes/config-secret.yaml 6 | 7 | kubectl create -f kubernetes/env-configmap.yaml 8 | 9 | kubectl create -f kubernetes/config-pv.yaml 10 | 11 | kubectl create -f kubernetes/log-pv.yaml 12 | 13 | kubectl create -f kubernetes/rabbitmq.yaml 14 | 15 | kubectl create -f kubernetes/zipkin.yaml 16 | 17 | kubectl create -f kubernetes/config-server.yaml 18 | 19 | kubectl create -f kubernetes/service-registry.yaml 20 | 21 | kubectl create -f kubernetes/turbine-server.yaml 22 | 23 | kubectl create -f kubernetes/monitor-dashboard.yaml 24 | 25 | kubectl create -f kubernetes/auth-service.yaml 26 | 27 | kubectl create -f kubernetes/account-service.yaml 28 | 29 | kubectl create -f kubernetes/cloud-gateway.yaml 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | rabbitmq: 4 | image: 192.168.99.100:5000/rabbitmq:management 5 | container_name: rabbitmq 6 | hostname: rabbitmq 7 | ports: 8 | - "5672:5672" 9 | - "15672:15672" 10 | environment: 11 | RABBITMQ_DEFAULT_USER: admin 12 | RABBITMQ_DEFAULT_PASS: amqp 13 | 14 | zipkin: 15 | image: 192.168.99.100:5000/zipkin 16 | container_name: zipkin 17 | hostname: zipkin 18 | depends_on: 19 | - rabbitmq 20 | ports: 21 | - "9411:9411" 22 | environment: 23 | RABBIT_ADDRESSES: rabbitmq 24 | RABBIT_USER: admin 25 | RABBIT_PASSWORD: amqp 26 | 27 | config-server: 28 | image: 192.168.99.100:5000/config-server 29 | container_name: config-server 30 | hostname: config-server 31 | restart: always 32 | depends_on: 33 | - rabbitmq 34 | ports: 35 | - "8888:8888" 36 | volumes: 37 | - $APP_CONFIG_DIR:/etc/msdemo 38 | - $APP_LOGGING_DIR:/var/log/msdemo 39 | environment: 40 | APP_CONFIG_DIR: /etc/msdemo 41 | APP_LOGGING_DIR: /var/log/msdemo 42 | SERVICE_REGISTRY_URL: http://service-registry:8761/eureka/ 43 | RABBITMQ_SERVICE_HOST: rabbitmq 44 | RABBITMQ_SERVICE_PORT: 5672 45 | RABBITMQ_SERVICE_USERNAME: admin 46 | RABBITMQ_SERVICE_PASSWORD: amqp 47 | 48 | service-registry: 49 | image: 192.168.99.100:5000/service-registry 50 | container_name: service-registry 51 | hostname: service-registry 52 | restart: always 53 | depends_on: 54 | - config-server 55 | ports: 56 | - "8761:8761" 57 | volumes: 58 | - $APP_LOGGING_DIR:/var/log/msdemo 59 | environment: 60 | CONFIG_SERVER_URL: http://config-server:8888 61 | 62 | turbine-server: 63 | image: 192.168.99.100:5000/turbine-server 64 | container_name: turbine-server 65 | hostname: turbine-server 66 | restart: always 67 | depends_on: 68 | - service-registry 69 | ports: 70 | - "8989:8989" 71 | volumes: 72 | - $APP_LOGGING_DIR:/var/log/msdemo 73 | environment: 74 | CONFIG_SERVER_URL: http://config-server:8888 75 | 76 | monitor-dashboard: 77 | image: 192.168.99.100:5000/monitor-dashboard 78 | container_name: monitor-dashboard 79 | hostname: monitor-dashboard 80 | restart: always 81 | depends_on: 82 | - service-registry 83 | - turbine-server 84 | ports: 85 | - "7979:7979" 86 | volumes: 87 | - $APP_LOGGING_DIR:/var/log/msdemo 88 | environment: 89 | CONFIG_SERVER_URL: http://config-server:8888 90 | 91 | auth-service: 92 | image: 192.168.99.100:5000/auth-service 93 | container_name: auth-service 94 | hostname: auth-service 95 | restart: always 96 | depends_on: 97 | - service-registry 98 | ports: 99 | - "9000:9000" 100 | volumes: 101 | - $APP_LOGGING_DIR:/var/log/msdemo 102 | environment: 103 | CONFIG_SERVER_URL: http://config-server:8888 104 | 105 | account-service: 106 | image: 192.168.99.100:5000/account-service 107 | container_name: account-service 108 | hostname: account-service 109 | restart: always 110 | depends_on: 111 | - service-registry 112 | ports: 113 | - "9010:9010" 114 | volumes: 115 | - $APP_LOGGING_DIR:/var/log/msdemo 116 | environment: 117 | CONFIG_SERVER_URL: http://config-server:8888 118 | 119 | api-gateway: 120 | image: 192.168.99.100:5000/cloud-gateway 121 | container_name: cloud-gateway 122 | hostname: cloud-gateway 123 | restart: always 124 | depends_on: 125 | - service-registry 126 | ports: 127 | - "8080:8080" 128 | volumes: 129 | - $APP_LOGGING_DIR:/var/log/msdemo 130 | environment: 131 | CONFIG_SERVER_URL: http://config-server:8888 132 | -------------------------------------------------------------------------------- /docker/setup-docker-images.txt: -------------------------------------------------------------------------------- 1 | ### RabbitMQ image 2 | docker pull rabbitmq:management 3 | docker tag rabbitmq:management 192.168.99.100:5000/rabbitmq:management 4 | docker push 192.168.99.100:5000/rabbitmq:management 5 | 6 | ### Zipkin image 7 | docker pull openzipkin/zipkin 8 | docker tag openzipkin/zipkin 192.168.99.100:5000/zipkin 9 | docker push 192.168.99.100:5000/zipkin 10 | -------------------------------------------------------------------------------- /kubernetes/account-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: account-service 5 | labels: 6 | app: account-service 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 9010 11 | selector: 12 | app: account-service 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: account-service 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: account-service 23 | template: 24 | metadata: 25 | labels: 26 | app: account-service 27 | spec: 28 | containers: 29 | - name: account-service 30 | image: 192.168.99.100:5000/account-service 31 | ports: 32 | - containerPort: 9010 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/api-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: api-gateway 5 | labels: 6 | app: api-gateway 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 8080 11 | selector: 12 | app: api-gateway 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: api-gateway 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: api-gateway 23 | template: 24 | metadata: 25 | labels: 26 | app: api-gateway 27 | spec: 28 | containers: 29 | - name: api-gateway 30 | image: 192.168.99.100:5000/api-gateway 31 | ports: 32 | - containerPort: 8080 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/auth-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: auth-service 5 | labels: 6 | app: auth-service 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 9000 11 | selector: 12 | app: auth-service 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: auth-service 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: auth-service 23 | template: 24 | metadata: 25 | labels: 26 | app: auth-service 27 | spec: 28 | containers: 29 | - name: auth-service 30 | image: 192.168.99.100:5000/auth-service 31 | ports: 32 | - containerPort: 9000 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/cloud-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: cloud-gateway 5 | labels: 6 | app: cloud-gateway 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 8080 11 | selector: 12 | app: cloud-gateway 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: cloud-gateway 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: cloud-gateway 23 | template: 24 | metadata: 25 | labels: 26 | app: cloud-gateway 27 | spec: 28 | containers: 29 | - name: cloud-gateway 30 | image: 192.168.99.100:5000/cloud-gateway 31 | ports: 32 | - containerPort: 8080 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/config-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: config-pv 5 | labels: 6 | type: local 7 | name: config-pv 8 | spec: 9 | storageClassName: standard 10 | capacity: 11 | storage: 1Gi 12 | accessModes: 13 | - ReadWriteOnce 14 | hostPath: 15 | path: "/Users/yenchu/git/microservices-demo/config-server/src/main/resources/shared" 16 | --- 17 | apiVersion: v1 18 | kind: PersistentVolumeClaim 19 | metadata: 20 | name: config-claim 21 | spec: 22 | storageClassName: standard 23 | selector: 24 | matchLabels: 25 | name: "config-pv" 26 | accessModes: 27 | - ReadWriteOnce 28 | resources: 29 | requests: 30 | storage: 1Gi 31 | -------------------------------------------------------------------------------- /kubernetes/config-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: config-secret 5 | type: Opaque 6 | data: 7 | rabbitmq-user: YWRtaW4= 8 | rabbitmq-pwd: YW1xcA== 9 | -------------------------------------------------------------------------------- /kubernetes/config-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: config-server 5 | labels: 6 | app: config-server 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 8888 11 | selector: 12 | app: config-server 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: config-server 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: config-server 23 | template: 24 | metadata: 25 | labels: 26 | app: config-server 27 | spec: 28 | containers: 29 | - name: config-server 30 | image: 192.168.99.100:5000/config-server 31 | ports: 32 | - containerPort: 8888 33 | env: 34 | - name: APP_CONFIG_DIR 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: app-config-dir 39 | - name: APP_LOGGING_DIR 40 | valueFrom: 41 | configMapKeyRef: 42 | name: env-config 43 | key: app-logging-dir 44 | - name: SERVICE_REGISTRY_URL 45 | valueFrom: 46 | configMapKeyRef: 47 | name: env-config 48 | key: service-registry-url 49 | - name: RABBITMQ_SERVICE_HOST 50 | valueFrom: 51 | configMapKeyRef: 52 | name: env-config 53 | key: rabbitmq-host 54 | - name: RABBITMQ_SERVICE_USERNAME 55 | valueFrom: 56 | secretKeyRef: 57 | name: config-secret 58 | key: rabbitmq-user 59 | - name: RABBITMQ_SERVICE_PASSWORD 60 | valueFrom: 61 | secretKeyRef: 62 | name: config-secret 63 | key: rabbitmq-pwd 64 | volumeMounts: 65 | - name: config-vol 66 | mountPath: /etc/msdemo 67 | - name: log-vol 68 | mountPath: /var/log/msdemo 69 | volumes: 70 | - name: config-vol 71 | persistentVolumeClaim: 72 | claimName: config-claim 73 | - name: log-vol 74 | persistentVolumeClaim: 75 | claimName: log-claim 76 | -------------------------------------------------------------------------------- /kubernetes/db-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: db-pv 5 | labels: 6 | type: local 7 | name: db-pv 8 | spec: 9 | storageClassName: standard 10 | capacity: 11 | storage: 5Gi 12 | accessModes: 13 | - ReadWriteOnce 14 | hostPath: 15 | path: "/Users/yenchu/data/mysql" 16 | --- 17 | apiVersion: v1 18 | kind: PersistentVolumeClaim 19 | metadata: 20 | name: db-claim 21 | spec: 22 | storageClassName: standard 23 | selector: 24 | matchLabels: 25 | name: "db-pv" 26 | accessModes: 27 | - ReadWriteOnce 28 | resources: 29 | requests: 30 | storage: 2Gi 31 | 32 | -------------------------------------------------------------------------------- /kubernetes/env-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: env-config 5 | data: 6 | app-config-dir: "/etc/msdemo" 7 | app-logging-dir: "/var/log/msdemo" 8 | config-server-url: http://config-server:8888 9 | service-registry-url: http://service-registry:8761/eureka/ 10 | rabbitmq-host: rabbitmq 11 | -------------------------------------------------------------------------------- /kubernetes/log-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: log-pv 5 | labels: 6 | type: local 7 | name: log-pv 8 | spec: 9 | storageClassName: standard 10 | capacity: 11 | storage: 5Gi 12 | accessModes: 13 | - ReadWriteOnce 14 | hostPath: 15 | path: "/Users/yenchu/log/microservices-demo" 16 | --- 17 | apiVersion: v1 18 | kind: PersistentVolumeClaim 19 | metadata: 20 | name: log-claim 21 | spec: 22 | storageClassName: standard 23 | selector: 24 | matchLabels: 25 | name: "log-pv" 26 | accessModes: 27 | - ReadWriteOnce 28 | resources: 29 | requests: 30 | storage: 2Gi 31 | -------------------------------------------------------------------------------- /kubernetes/monitor-dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: monitor-dashboard 5 | labels: 6 | app: monitor-dashboard 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 7979 11 | selector: 12 | app: monitor-dashboard 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: monitor-dashboard 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: monitor-dashboard 23 | template: 24 | metadata: 25 | labels: 26 | app: monitor-dashboard 27 | spec: 28 | containers: 29 | - name: monitor-dashboard 30 | image: 192.168.99.100:5000/monitor-dashboard 31 | ports: 32 | - containerPort: 7979 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/mysql.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: mysql 5 | labels: 6 | app: mysql 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 3306 11 | selector: 12 | app: mysql 13 | --- 14 | apiVersion: apps/v1beta1 15 | kind: Deployment 16 | metadata: 17 | name: mysql 18 | spec: 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | app: mysql 24 | spec: 25 | containers: 26 | - name: mysql 27 | image: 192.168.99.100:5000/mysql:5.7 28 | ports: 29 | - containerPort: 3306 30 | env: 31 | - name: MYSQL_ROOT_PASSWORD 32 | valueFrom: 33 | secretKeyRef: 34 | name: pwd-secret 35 | key: mysql-root-pwd 36 | volumeMounts: 37 | - name: db-vol 38 | mountPath: /var/lib/mysql 39 | volumes: 40 | - name: db-vol 41 | persistentVolumeClaim: 42 | claimName: db-claim 43 | -------------------------------------------------------------------------------- /kubernetes/rabbitmq.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rabbitmq 5 | labels: 6 | app: rabbitmq 7 | spec: 8 | type: NodePort 9 | ports: 10 | - name: service 11 | port: 5672 12 | targetPort: 5672 13 | - name: management 14 | port: 15672 15 | targetPort: 15672 16 | selector: 17 | app: rabbitmq 18 | --- 19 | apiVersion: apps/v1beta1 20 | kind: Deployment 21 | metadata: 22 | name: rabbitmq 23 | spec: 24 | replicas: 1 25 | template: 26 | metadata: 27 | labels: 28 | app: rabbitmq 29 | spec: 30 | containers: 31 | - name: rabbitmq 32 | image: 192.168.99.100:5000/rabbitmq:management 33 | ports: 34 | - name: service 35 | containerPort: 5672 36 | - name: management 37 | containerPort: 15672 38 | env: 39 | - name: RABBITMQ_DEFAULT_USER 40 | valueFrom: 41 | secretKeyRef: 42 | name: config-secret 43 | key: rabbitmq-user 44 | - name: RABBITMQ_DEFAULT_PASS 45 | valueFrom: 46 | secretKeyRef: 47 | name: config-secret 48 | key: rabbitmq-pwd 49 | -------------------------------------------------------------------------------- /kubernetes/service-registry.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: service-registry 5 | labels: 6 | app: service-registry 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 8761 11 | selector: 12 | app: service-registry 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: service-registry 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: service-registry 23 | template: 24 | metadata: 25 | labels: 26 | app: service-registry 27 | spec: 28 | containers: 29 | - name: service-registry 30 | image: 192.168.99.100:5000/service-registry 31 | ports: 32 | - containerPort: 8761 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/turbine-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: turbine-server 5 | labels: 6 | app: turbine-server 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 8989 11 | selector: 12 | app: turbine-server 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: turbine-server 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: turbine-server 23 | template: 24 | metadata: 25 | labels: 26 | app: turbine-server 27 | spec: 28 | containers: 29 | - name: turbine-server 30 | image: 192.168.99.100:5000/turbine-server 31 | ports: 32 | - containerPort: 8989 33 | env: 34 | - name: CONFIG_SERVER_URL 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: config-server-url 39 | volumeMounts: 40 | - name: log-vol 41 | mountPath: /var/log/msdemo 42 | volumes: 43 | - name: log-vol 44 | persistentVolumeClaim: 45 | claimName: log-claim 46 | -------------------------------------------------------------------------------- /kubernetes/zipkin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: zipkin 5 | labels: 6 | app: zipkin 7 | spec: 8 | type: NodePort 9 | ports: 10 | - port: 9411 11 | selector: 12 | app: zipkin 13 | --- 14 | apiVersion: apps/v1beta2 15 | kind: Deployment 16 | metadata: 17 | name: zipkin 18 | spec: 19 | replicas: 1 20 | selector: 21 | matchLabels: 22 | app: zipkin 23 | template: 24 | metadata: 25 | labels: 26 | app: zipkin 27 | spec: 28 | containers: 29 | - name: zipkin 30 | image: 192.168.99.100:5000/zipkin 31 | ports: 32 | - containerPort: 9411 33 | env: 34 | - name: RABBIT_ADDRESSES 35 | valueFrom: 36 | configMapKeyRef: 37 | name: env-config 38 | key: rabbitmq-host 39 | - name: RABBIT_USER 40 | valueFrom: 41 | secretKeyRef: 42 | name: config-secret 43 | key: rabbitmq-user 44 | - name: RABBIT_PASSWORD 45 | valueFrom: 46 | secretKeyRef: 47 | name: config-secret 48 | key: rabbitmq-pwd 49 | -------------------------------------------------------------------------------- /monitor-dashboard/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/monitor-dashboard.jar ./monitor-dashboard.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "monitor-dashboard.jar"] -------------------------------------------------------------------------------- /monitor-dashboard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | monitor-dashboard 7 | monitor-dashboard 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-webflux 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-actuator 25 | 26 | 27 | 28 | org.springframework.cloud 29 | spring-cloud-starter-config 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-bus-amqp 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-netflix-eureka-client 39 | 40 | 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-netflix-hystrix 45 | 46 | 47 | org.springframework.cloud 48 | spring-cloud-starter-netflix-hystrix-dashboard 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-test 54 | test 55 | 56 | 57 | 58 | 59 | ${project.name} 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /monitor-dashboard/src/main/java/demo/ms/MonitorDashboardApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 8 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.http.codec.ServerSentEvent; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.reactive.function.client.WebClient; 14 | 15 | import reactor.core.publisher.Flux; 16 | 17 | @SpringBootApplication 18 | @EnableHystrixDashboard 19 | public class MonitorDashboardApplication { 20 | 21 | public static void main(String[] args) { 22 | SpringApplication.run(MonitorDashboardApplication.class, args); 23 | } 24 | 25 | @Bean 26 | @LoadBalanced 27 | public WebClient.Builder loadBalancedWebClientBuilder() { 28 | return WebClient.builder(); 29 | } 30 | 31 | @Controller 32 | static class WebController { 33 | 34 | @Autowired 35 | private WebClient.Builder webClientBuilder; 36 | 37 | @Value("${server.port}") 38 | private Integer serverPort; 39 | 40 | @Value("${turbine.aggregator.clusterConfig}") 41 | private String clusterName; 42 | 43 | @GetMapping("/turbine-stream") 44 | public Flux turbineStream() { 45 | return webClientBuilder 46 | .build() 47 | .get() 48 | .uri("http://turbine-server/turbine.stream?cluster=" + clusterName) 49 | .retrieve() 50 | .bodyToFlux(ServerSentEvent.class); 51 | } 52 | 53 | @GetMapping({"", "/"}) 54 | public String index() { 55 | return "redirect:/hystrix/monitor?stream=http://localhost:" + serverPort + "/turbine-stream"; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /monitor-dashboard/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 7979 5 | 6 | spring: 7 | application: 8 | name: monitor-dashboard 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /monitor-dashboard/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /monitor-dashboard/src/test/java/demo/ms/MonitorDashboardApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class MonitorDashboardApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | demo.ms 6 | ms-demo 7 | 0.0.1 8 | pom 9 | 10 | 11 | org.springframework.boot 12 | spring-boot-starter-parent 13 | 2.0.3.RELEASE 14 | 15 | 16 | 17 | 18 | UTF-8 19 | UTF-8 20 | Finchley.RELEASE 21 | 1.8 22 | 23 | 24 | 25 | account-service 26 | api-gateway 27 | cloud-gateway 28 | auth-service 29 | config-server 30 | turbine-server 31 | monitor-dashboard 32 | service-registry 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-dependencies 40 | ${spring-cloud.version} 41 | pom 42 | import 43 | 44 | 45 | 46 | 47 | 48 | 49 | spring-snapshots 50 | Spring Snapshots 51 | http://repo.spring.io/snapshot 52 | 53 | true 54 | 55 | 56 | 57 | spring-milestones 58 | Spring Milestones 59 | http://repo.spring.io/milestone 60 | 61 | false 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /push-images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | docker push 192.168.99.100:5000/config-server 6 | docker push 192.168.99.100:5000/service-registry 7 | docker push 192.168.99.100:5000/turbine-server 8 | docker push 192.168.99.100:5000/monitor-dashboard 9 | docker push 192.168.99.100:5000/auth-service 10 | docker push 192.168.99.100:5000/account-service 11 | docker push 192.168.99.100:5000/cloud-gateway 12 | -------------------------------------------------------------------------------- /service-registry/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/service-registry.jar ./service-registry.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "service-registry.jar"] -------------------------------------------------------------------------------- /service-registry/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | service-registry 7 | service-registry 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-actuator 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-config 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-starter-bus-amqp 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-netflix-eureka-client 34 | 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-starter-netflix-eureka-server 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | 48 | 49 | ${project.name} 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /service-registry/src/main/java/demo/ms/ServiceRegistryApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 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 | @SpringBootApplication 8 | @EnableEurekaServer 9 | public class ServiceRegistryApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(ServiceRegistryApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /service-registry/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 8761 5 | 6 | spring: 7 | application: 8 | name: service-registry 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /service-registry/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /service-registry/src/test/java/demo/ms/RegistryServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class RegistryServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /turbine-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk-alpine 2 | 3 | VOLUME /tmp 4 | 5 | # Set timezone 6 | ENV TIME_ZONE Asia/Taipei 7 | RUN apk --no-cache add \ 8 | tzdata \ 9 | && echo "${TIME_ZONE}" > /etc/timezone \ 10 | && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime 11 | 12 | RUN mkdir /msdemo 13 | WORKDIR /msdemo 14 | COPY ./target/turbine-server.jar ./turbine-server.jar 15 | 16 | CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "turbine-server.jar"] -------------------------------------------------------------------------------- /turbine-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | turbine-server 7 | turbine-server 8 | Spring Cloud Microservices Demo 9 | 10 | 11 | demo.ms 12 | ms-demo 13 | 0.0.1 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-actuator 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-config 25 | 26 | 27 | org.springframework.cloud 28 | spring-cloud-starter-bus-amqp 29 | 30 | 31 | 32 | org.springframework.cloud 33 | spring-cloud-starter-netflix-eureka-client 34 | 35 | 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-netflix-hystrix 40 | 41 | 42 | org.springframework.cloud 43 | spring-cloud-starter-netflix-turbine-stream 44 | 45 | 46 | org.springframework.cloud 47 | spring-cloud-starter-stream-rabbit 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-test 53 | test 54 | 55 | 56 | 57 | 58 | ${project.name} 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-maven-plugin 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /turbine-server/src/main/java/demo/ms/TurbineServerApplication.java: -------------------------------------------------------------------------------- 1 | package demo.ms; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.netflix.turbine.stream.EnableTurbineStream; 6 | 7 | @SpringBootApplication 8 | @EnableTurbineStream 9 | public class TurbineServerApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(TurbineServerApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /turbine-server/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | CONFIG_SERVER_URL: http://127.0.0.1:8888 2 | 3 | server: 4 | port: 8989 5 | 6 | spring: 7 | application: 8 | name: turbine-server 9 | main: 10 | banner-mode: "off" 11 | mvc: 12 | favicon.enabled: false 13 | cloud: 14 | config: 15 | uri: ${CONFIG_SERVER_URL} 16 | fail-fast: true 17 | -------------------------------------------------------------------------------- /turbine-server/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /undeploy-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | docker-compose stop 6 | 7 | docker rm monitor-dashboard turbine-server cloud-gateway account-service auth-service service-registry config-server zipkin rabbitmq 8 | -------------------------------------------------------------------------------- /undeploy-kubernetes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | kubectl delete deploy,svc cloud-gateway 6 | kubectl delete deploy,svc account-service 7 | kubectl delete deploy,svc auth-service 8 | kubectl delete deploy,svc monitor-dashboard 9 | kubectl delete deploy,svc turbine-server 10 | kubectl delete deploy,svc service-registry 11 | kubectl delete deploy,svc config-server 12 | 13 | kubectl delete deploy,svc zipkin 14 | kubectl delete deploy,svc rabbitmq 15 | 16 | kubectl delete pvc config-claim log-claim 17 | kubectl delete pv config-pv log-pv 18 | 19 | kubectl delete cm env-config 20 | kubectl delete secrets config-secret 21 | --------------------------------------------------------------------------------