├── exercise
├── 03-netflix
│ ├── eureka-server
│ │ ├── src
│ │ │ ├── test
│ │ │ │ ├── java
│ │ │ │ │ └── demo
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ └── resources
│ │ │ │ │ └── .gitkeep
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── bootstrap.yml
│ │ │ │ └── application.yml
│ │ │ │ └── java
│ │ │ │ └── demo
│ │ │ │ └── EurekaServer.java
│ │ └── pom.xml
│ ├── urlshortener-ui
│ │ ├── src
│ │ │ ├── test
│ │ │ │ └── resources
│ │ │ │ │ └── .gitkeep
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── bootstrap.yml
│ │ │ │ ├── application.yml
│ │ │ │ └── templates
│ │ │ │ │ └── index.html
│ │ │ │ └── java
│ │ │ │ └── demo
│ │ │ │ └── App.java
│ │ └── pom.xml
│ ├── urlshortener
│ │ ├── src
│ │ │ ├── test
│ │ │ │ ├── resources
│ │ │ │ │ └── .gitkeep
│ │ │ │ └── java
│ │ │ │ │ └── demo
│ │ │ │ │ └── UrlShortenerTest.java
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── bootstrap.yml
│ │ │ │ └── application.yml
│ │ │ │ └── java
│ │ │ │ └── demo
│ │ │ │ └── UrlShortener.java
│ │ └── pom.xml
│ ├── hystrix-dashboard
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── resources
│ │ │ │ ├── bootstrap.yml
│ │ │ │ ├── log4j.properties
│ │ │ │ └── application.yml
│ │ │ │ └── java
│ │ │ │ └── hystrixdashboard
│ │ │ │ ├── HystrixDashboardApplication.java
│ │ │ │ └── stream
│ │ │ │ └── MockStreamServlet.java
│ │ ├── README.md
│ │ └── pom.xml
│ └── configserver
│ │ ├── src
│ │ └── main
│ │ │ └── resources
│ │ │ └── bootstrap.yml
│ │ └── pom.xml
├── 01-urlshortener
│ └── urlshortener
│ │ ├── src
│ │ ├── test
│ │ │ ├── resources
│ │ │ │ └── .gitkeep
│ │ │ └── java
│ │ │ │ └── demo
│ │ │ │ └── UrlShortenerTest.java
│ │ └── main
│ │ │ ├── resources
│ │ │ └── application.yml
│ │ │ └── java
│ │ │ └── demo
│ │ │ └── UrlShortener.java
│ │ └── pom.xml
└── 02-distributed-config
│ ├── urlshortener
│ ├── src
│ │ ├── test
│ │ │ ├── resources
│ │ │ │ └── .gitkeep
│ │ │ └── java
│ │ │ │ └── demo
│ │ │ │ └── UrlShortenerTest.java
│ │ └── main
│ │ │ ├── resources
│ │ │ ├── bootstrap.yml
│ │ │ └── application.yml
│ │ │ └── java
│ │ │ └── demo
│ │ │ └── UrlShortener.java
│ └── pom.xml
│ └── configserver
│ ├── src
│ └── main
│ │ └── resources
│ │ └── bootstrap.yml
│ └── pom.xml
├── instruction
├── images
│ ├── logo.png
│ ├── exercise00-01.png
│ ├── exercise00-02.png
│ ├── exercise00-03.png
│ ├── exercise00-04.png
│ ├── exercise01-01.png
│ ├── exercise01-02.png
│ ├── exercise01-03.png
│ ├── exercise02-01.png
│ ├── exercise02-02.png
│ ├── exercise02-03.png
│ ├── exercise02-04.png
│ ├── exercise02-05.png
│ ├── exercise02-06.png
│ ├── exercise02-07.png
│ ├── exercise02-08.png
│ ├── exercise02-09.png
│ ├── exercise02-10.png
│ ├── exercise02-11.png
│ ├── exercise03-01.png
│ ├── exercise03-02.png
│ ├── exercise03-03.png
│ ├── exercise03-04.png
│ ├── exercise03-05.png
│ ├── exercise03-06.png
│ ├── exercise03-07.png
│ ├── exercise03-08.png
│ ├── exercise03-09.png
│ ├── exercise03-10.png
│ ├── exercise03-11.png
│ ├── exercise03-12.png
│ ├── exercise03-13.png
│ ├── exercise03-14.png
│ ├── exercise03-15.png
│ ├── exercise03-16.png
│ ├── exercise03-17.png
│ ├── import-exercise01-01.png
│ ├── import-exercise01-02.png
│ ├── import-exercise01-03.png
│ ├── import-exercise01-04.png
│ ├── import-exercise01-05.png
│ ├── import-exercise01-06.png
│ ├── import-exercise01-07.png
│ ├── import-exercise01-08.png
│ ├── import-exercise01-09.png
│ ├── import-exercise02-01.png
│ ├── import-exercise02-02.png
│ ├── import-exercise03-01.png
│ ├── import-exercise03-02.png
│ ├── system-exercise03-01.png
│ ├── system-exercise03-02.png
│ ├── system-exercise03-03.png
│ ├── system-exercise03-04.png
│ ├── system-exercise03-05.png
│ └── system-exercise03-06.png
├── Makefile
├── make.bat
├── conf.py
└── readme.rst
├── .gitignore
└── .gitmodules
/exercise/03-netflix/eureka-server/src/test/java/demo/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercise/03-netflix/eureka-server/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener-ui/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercise/01-urlshortener/urlshortener/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercise/02-distributed-config/urlshortener/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/instruction/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/logo.png
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: urlshortener
--------------------------------------------------------------------------------
/exercise/03-netflix/eureka-server/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: eureka-service
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: hystrixdashboard
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener-ui/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: urlshortener-ui
--------------------------------------------------------------------------------
/exercise/02-distributed-config/urlshortener/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: urlshortener
--------------------------------------------------------------------------------
/instruction/images/exercise00-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-01.png
--------------------------------------------------------------------------------
/instruction/images/exercise00-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-02.png
--------------------------------------------------------------------------------
/instruction/images/exercise00-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-03.png
--------------------------------------------------------------------------------
/instruction/images/exercise00-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise00-04.png
--------------------------------------------------------------------------------
/instruction/images/exercise01-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise01-01.png
--------------------------------------------------------------------------------
/instruction/images/exercise01-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise01-02.png
--------------------------------------------------------------------------------
/instruction/images/exercise01-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise01-03.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-01.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-02.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-03.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-04.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-05.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-06.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-07.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-08.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-09.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-10.png
--------------------------------------------------------------------------------
/instruction/images/exercise02-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise02-11.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-01.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-02.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-03.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-04.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-05.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-06.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-07.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-08.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-09.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-10.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-11.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-12.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-13.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-14.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-15.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-16.png
--------------------------------------------------------------------------------
/instruction/images/exercise03-17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/exercise03-17.png
--------------------------------------------------------------------------------
/exercise/03-netflix/configserver/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring.cloud.config.server.uri: http://localhost:8080/git/root/config-repo.git
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-01.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-02.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-03.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-04.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-05.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-06.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-07.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-08.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise01-09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise01-09.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise02-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise02-01.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise02-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise02-02.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise03-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise03-01.png
--------------------------------------------------------------------------------
/instruction/images/import-exercise03-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/import-exercise03-02.png
--------------------------------------------------------------------------------
/instruction/images/system-exercise03-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-01.png
--------------------------------------------------------------------------------
/instruction/images/system-exercise03-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-02.png
--------------------------------------------------------------------------------
/instruction/images/system-exercise03-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-03.png
--------------------------------------------------------------------------------
/instruction/images/system-exercise03-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-04.png
--------------------------------------------------------------------------------
/instruction/images/system-exercise03-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-05.png
--------------------------------------------------------------------------------
/instruction/images/system-exercise03-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/making/jjugccc-handson/HEAD/instruction/images/system-exercise03-06.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 | .idea
3 | target
4 | .DS_Store
5 | .classpath
6 | .project
7 | .settings
8 | *-diff.txt
9 | Note.txt
10 | *~
11 | *.iml
--------------------------------------------------------------------------------
/exercise/02-distributed-config/configserver/src/main/resources/bootstrap.yml:
--------------------------------------------------------------------------------
1 | spring.cloud.config.server.uri: http://localhost:8080/git/root/config-repo.git
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "repository"]
2 | path = repository
3 | url = https://github.com/making/repository.git
4 | [submodule "software"]
5 | path = software
6 | url = https://github.com/making/software.git
7 |
--------------------------------------------------------------------------------
/exercise/01-urlshortener/urlshortener/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
2 | spring:
3 | thymeleaf.cache: false
4 | main.show-banner: false
--------------------------------------------------------------------------------
/exercise/02-distributed-config/urlshortener/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
2 | spring:
3 | thymeleaf.cache: false
4 | main.show-banner: false
--------------------------------------------------------------------------------
/exercise/03-netflix/eureka-server/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8761
3 |
4 | eureka:
5 | instance:
6 | hostname: localhost
7 | client:
8 | registerWithEureka: false
9 | fetchRegistry: false
10 | serviceUrl:
11 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=INFO, FILE
2 | log4j.appender.FILE=org.apache.log4j.ConsoleAppender
3 | log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
4 | log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %C:%L [%C{1}] [%M]: %m%n
5 |
6 | log4j.appender.FILE.httpclient=ERROR
7 |
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | info:
2 | component: Hystrix Dashboard
3 |
4 | endpoints:
5 | restart:
6 | enabled: true
7 | shutdown:
8 | enabled: true
9 |
10 | server:
11 | port: 7979
12 |
13 | eureka:
14 | client:
15 | serviceUrl:
16 | defaultZone: http://localhost:8761/eureka/
17 | instance:
18 | hostname: ${APPLICATION_DOMAIN:127.0.0.1}
19 | nonSecurePort: ${server.port}
20 |
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener-ui/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
2 | spring:
3 | thymeleaf.cache: false
4 | main.show-banner: false
5 |
6 | server.port: 9999
7 |
8 | eureka:
9 | client:
10 | serviceUrl:
11 | defaultZone: http://localhost:8761/eureka/
12 | instance:
13 | hostname: ${APPLICATION_DOMAIN:127.0.0.1}
14 | nonSecurePort: ${server.port}
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | # See http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
2 | spring:
3 | thymeleaf.cache: false
4 | main.show-banner: false
5 |
6 | server.port: 8081
7 |
8 | eureka:
9 | client:
10 | serviceUrl:
11 | defaultZone: http://localhost:8761/eureka/
12 | instance:
13 | hostname: ${APPLICATION_DOMAIN:127.0.0.1}
14 | nonSecurePort: ${server.port}
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/README.md:
--------------------------------------------------------------------------------
1 | # Hystrix Dashboard
2 |
3 | Run this app as a normal Spring Boot app and then go to the home page
4 | in a browser. If you run from this project it will be on port 7979
5 | (per the `application.yml`). On the home page is a form where you can
6 | enter the URL for an event stream to monitor, for example (the
7 | customers service running locally):
8 | `http://localhost:9000/hystrix.stream`. Any app that uses
9 | `@EnableHystrix` will expose the stream.
10 |
11 | To aggregate many streams together you can use the
12 | [Turbine sample](https://github.com/spring-cloud-samples/turbine).
13 |
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener-ui/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UrlShortener
6 |
7 |
8 | Shorten! http://goo.gl/aaaaa
9 |
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/exercise/03-netflix/eureka-server/src/main/java/demo/EurekaServer.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
6 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
7 | import org.springframework.context.annotation.ComponentScan;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @EnableAutoConfiguration
11 | @ComponentScan
12 | @RestController
13 | @EnableEurekaServer
14 | @EnableEurekaClient
15 | public class EurekaServer {
16 | public static void main(String[] args) {
17 | SpringApplication.run(EurekaServer.class, args);
18 | }
19 | }
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/src/main/java/hystrixdashboard/HystrixDashboardApplication.java:
--------------------------------------------------------------------------------
1 | package hystrixdashboard;
2 |
3 | import hystrixdashboard.stream.MockStreamServlet;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
6 | import org.springframework.boot.context.embedded.ServletRegistrationBean;
7 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
8 | import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.ComponentScan;
11 | import org.springframework.context.annotation.Configuration;
12 | import org.springframework.stereotype.Controller;
13 | import org.springframework.web.bind.annotation.RequestMapping;
14 |
15 | @Configuration
16 | @ComponentScan
17 | @EnableAutoConfiguration
18 | @Controller
19 | @EnableHystrixDashboard
20 | @EnableEurekaClient
21 | public class HystrixDashboardApplication {
22 |
23 | public static void main(String[] args) {
24 | SpringApplication.run(HystrixDashboardApplication.class, args);
25 | }
26 |
27 | @RequestMapping("/")
28 | public String home() {
29 | return "forward:/hystrix/index.html";
30 | }
31 |
32 | @Bean
33 | public ServletRegistrationBean mockStreamServlet() {
34 | return new ServletRegistrationBean(new MockStreamServlet(), "/mock.stream");
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/exercise/01-urlshortener/urlshortener/src/main/java/demo/UrlShortener.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.apache.commons.validator.routines.UrlValidator;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
7 | import org.springframework.context.annotation.ComponentScan;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.http.ResponseEntity;
10 | import org.springframework.web.bind.annotation.*;
11 |
12 | import java.nio.charset.StandardCharsets;
13 | import java.util.concurrent.ConcurrentHashMap;
14 |
15 | @EnableAutoConfiguration
16 | @ComponentScan
17 | @RestController
18 | public class UrlShortener {
19 | public static void main(String[] args) {
20 | SpringApplication.run(UrlShortener.class, args);
21 | }
22 |
23 | @Value("${urlshorten.url:http://localhost:${server.port:8080}}")
24 | String urlShortenUrl;
25 |
26 | final ConcurrentHashMap urlMap = new ConcurrentHashMap<>();
27 | final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"});
28 |
29 | /**
30 | * curl -v -X POST http://localhost:8080 -d "url=http://google.com"
31 | */
32 | @RequestMapping(value = "/", method = RequestMethod.POST)
33 | ResponseEntity save(@RequestParam String url) {
34 | if (urlValidator.isValid(url)) {
35 | String hash = ""/* TODO (1) URLをハッシュ化。ハッシュアルゴリズムには 32-bit murmur3 algorithm を使用する。 */;
36 | // ヒント: com.google.common.hash.Hashing.murmur3_32()を使う
37 | // TODO (2) urlMapにhashに紐づくURLを追加する。
38 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK);
39 | } else {
40 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
41 | }
42 | }
43 |
44 | /**
45 | * curl -v -X GET http://localhost:8080/58f3ae21
46 | */
47 | @RequestMapping(value = "{hash}", method = RequestMethod.GET)
48 | ResponseEntity> get(@PathVariable String hash) {
49 | String url = urlMap.get(hash);
50 | // 本当はリダイレクトするのだが、今回はレスポンスボディに入れて200を返す
51 | if (url != null) {
52 | return new ResponseEntity<>(url, HttpStatus.OK);
53 | } else {
54 | return new ResponseEntity<>(HttpStatus.NOT_FOUND);
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener/src/test/java/demo/UrlShortenerTest.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import com.jayway.restassured.RestAssured;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.boot.test.IntegrationTest;
9 | import org.springframework.boot.test.SpringApplicationConfiguration;
10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 | import org.springframework.test.context.web.WebAppConfiguration;
12 |
13 | import static com.jayway.restassured.RestAssured.given;
14 | import static org.hamcrest.Matchers.is;
15 |
16 | @RunWith(SpringJUnit4ClassRunner.class)
17 | @SpringApplicationConfiguration(classes = UrlShortener.class)
18 | @WebAppConfiguration
19 | @IntegrationTest({"server.port:0"})
20 | public class UrlShortenerTest {
21 | @Value("${local.server.port}")
22 | int port;
23 |
24 | @Before
25 | public void setUp() {
26 | RestAssured.port = port;
27 | }
28 |
29 | @Test
30 | public void testSaveAndGet() throws Exception {
31 | given().log().all()
32 | .when()
33 | .body("url=http://google.com")
34 | .contentType("application/x-www-form-urlencoded")
35 | .post()
36 | .then()
37 | .log().all()
38 | .statusCode(200)
39 | .body(is("http://localhost:0/58f3ae21"));
40 |
41 | given().log().all()
42 | .when()
43 | .get("/58f3ae21")
44 | .then()
45 | .log().all()
46 | .statusCode(200)
47 | .body(is("http://google.com"));
48 | }
49 |
50 | @Test
51 | public void testInvalidUrl() throws Exception {
52 | given().log().all()
53 | .when()
54 | .body("url=hoge")
55 | .contentType("application/x-www-form-urlencoded")
56 | .post()
57 | .then()
58 | .log().all()
59 | .statusCode(400);
60 | }
61 |
62 | @Test
63 | public void testNotExistHash() throws Exception {
64 | given().log().all()
65 | .when()
66 | .get("/hoge")
67 | .then()
68 | .log().all()
69 | .statusCode(404);
70 | }
71 | }
--------------------------------------------------------------------------------
/exercise/01-urlshortener/urlshortener/src/test/java/demo/UrlShortenerTest.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import com.jayway.restassured.RestAssured;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.boot.test.IntegrationTest;
9 | import org.springframework.boot.test.SpringApplicationConfiguration;
10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 | import org.springframework.test.context.web.WebAppConfiguration;
12 |
13 | import static com.jayway.restassured.RestAssured.given;
14 | import static org.hamcrest.Matchers.is;
15 |
16 | @RunWith(SpringJUnit4ClassRunner.class)
17 | @SpringApplicationConfiguration(classes = UrlShortener.class)
18 | @WebAppConfiguration
19 | @IntegrationTest({"server.port:0"})
20 | public class UrlShortenerTest {
21 | @Value("${local.server.port}")
22 | int port;
23 |
24 | @Before
25 | public void setUp() {
26 | RestAssured.port = port;
27 | }
28 |
29 | @Test
30 | public void testSaveAndGet() throws Exception {
31 | given().log().all()
32 | .when()
33 | .body("url=http://google.com")
34 | .contentType("application/x-www-form-urlencoded")
35 | .post()
36 | .then()
37 | .log().all()
38 | .statusCode(200)
39 | .body(is("http://localhost:0/58f3ae21"));
40 |
41 | given().log().all()
42 | .when()
43 | .get("/58f3ae21")
44 | .then()
45 | .log().all()
46 | .statusCode(200)
47 | .body(is("http://google.com"));
48 | }
49 |
50 | @Test
51 | public void testInvalidUrl() throws Exception {
52 | given().log().all()
53 | .when()
54 | .body("url=hoge")
55 | .contentType("application/x-www-form-urlencoded")
56 | .post()
57 | .then()
58 | .log().all()
59 | .statusCode(400);
60 | }
61 |
62 | @Test
63 | public void testNotExistHash() throws Exception {
64 | given().log().all()
65 | .when()
66 | .get("/hoge")
67 | .then()
68 | .log().all()
69 | .statusCode(404);
70 | }
71 | }
--------------------------------------------------------------------------------
/exercise/02-distributed-config/urlshortener/src/test/java/demo/UrlShortenerTest.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import com.jayway.restassured.RestAssured;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.boot.test.IntegrationTest;
9 | import org.springframework.boot.test.SpringApplicationConfiguration;
10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 | import org.springframework.test.context.web.WebAppConfiguration;
12 |
13 | import static com.jayway.restassured.RestAssured.given;
14 | import static org.hamcrest.Matchers.is;
15 |
16 | @RunWith(SpringJUnit4ClassRunner.class)
17 | @SpringApplicationConfiguration(classes = UrlShortener.class)
18 | @WebAppConfiguration
19 | @IntegrationTest({"server.port:0"})
20 | public class UrlShortenerTest {
21 | @Value("${local.server.port}")
22 | int port;
23 |
24 | @Before
25 | public void setUp() {
26 | RestAssured.port = port;
27 | }
28 |
29 | @Test
30 | public void testSaveAndGet() throws Exception {
31 | given().log().all()
32 | .when()
33 | .body("url=http://google.com")
34 | .contentType("application/x-www-form-urlencoded")
35 | .post()
36 | .then()
37 | .log().all()
38 | .statusCode(200)
39 | .body(is("http://localhost:0/58f3ae21"));
40 |
41 | given().log().all()
42 | .when()
43 | .get("/58f3ae21")
44 | .then()
45 | .log().all()
46 | .statusCode(200)
47 | .body(is("http://google.com"));
48 | }
49 |
50 | @Test
51 | public void testInvalidUrl() throws Exception {
52 | given().log().all()
53 | .when()
54 | .body("url=hoge")
55 | .contentType("application/x-www-form-urlencoded")
56 | .post()
57 | .then()
58 | .log().all()
59 | .statusCode(400);
60 | }
61 |
62 | @Test
63 | public void testNotExistHash() throws Exception {
64 | given().log().all()
65 | .when()
66 | .get("/hoge")
67 | .then()
68 | .log().all()
69 | .statusCode(404);
70 | }
71 | }
--------------------------------------------------------------------------------
/exercise/02-distributed-config/urlshortener/src/main/java/demo/UrlShortener.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.apache.commons.validator.routines.UrlValidator;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8 | import org.springframework.cloud.context.config.annotation.RefreshScope;
9 | import org.springframework.context.annotation.ComponentScan;
10 | import org.springframework.data.redis.core.StringRedisTemplate;
11 | import org.springframework.http.HttpStatus;
12 | import org.springframework.http.ResponseEntity;
13 | import org.springframework.web.bind.annotation.*;
14 |
15 | import java.nio.charset.StandardCharsets;
16 |
17 | @EnableAutoConfiguration
18 | @ComponentScan
19 | @RestController
20 | //@RefreshScope
21 | public class UrlShortener {
22 | public static void main(String[] args) {
23 | SpringApplication.run(UrlShortener.class, args);
24 | }
25 |
26 | @Value("${urlshorten.url:http://localhost:${server.port:8080}}")
27 | String urlShortenUrl;
28 |
29 | @Autowired
30 | StringRedisTemplate redisTemplate;
31 |
32 | final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"});
33 |
34 | /**
35 | * curl -v -X POST http://localhost:8080 -d "url=http://google.com"
36 | */
37 | @RequestMapping(value = "/", method = RequestMethod.POST)
38 | ResponseEntity save(@RequestParam String url) {
39 | System.out.println(url);
40 | if (urlValidator.isValid(url)) {
41 | String hash = com.google.common.hash.Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString();
42 | redisTemplate.opsForValue().set(hash, url);
43 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK);
44 | } else {
45 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
46 | }
47 | }
48 |
49 | /**
50 | * curl -v -X GET http://localhost:8080/58f3ae21
51 | */
52 | @RequestMapping(value = "{hash}", method = RequestMethod.GET)
53 | ResponseEntity> get(@PathVariable String hash) {
54 | String url = redisTemplate.opsForValue().get(hash);
55 | // 本当はリダイレクトするのだが、今回はレスポンスボディに入れて200を返す
56 | if (url != null) {
57 | return new ResponseEntity<>(url, HttpStatus.OK);
58 | } else {
59 | return new ResponseEntity<>(HttpStatus.NOT_FOUND);
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener/src/main/java/demo/UrlShortener.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.apache.commons.validator.routines.UrlValidator;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
8 | import org.springframework.cloud.context.config.annotation.RefreshScope;
9 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
10 | import org.springframework.context.annotation.ComponentScan;
11 | import org.springframework.data.redis.core.StringRedisTemplate;
12 | import org.springframework.http.HttpStatus;
13 | import org.springframework.http.ResponseEntity;
14 | import org.springframework.web.bind.annotation.*;
15 |
16 | import java.nio.charset.StandardCharsets;
17 |
18 | @EnableAutoConfiguration
19 | @ComponentScan
20 | @RestController
21 | @RefreshScope
22 | @EnableEurekaClient
23 | public class UrlShortener {
24 | public static void main(String[] args) {
25 | SpringApplication.run(UrlShortener.class, args);
26 | }
27 |
28 | @Value("${urlshorten.url:http://localhost:${server.port:8080}}")
29 | String urlShortenUrl;
30 |
31 | @Autowired
32 | StringRedisTemplate redisTemplate;
33 |
34 | final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"});
35 |
36 | /**
37 | * curl -v -X POST http://localhost:8080 -d "url=http://google.com"
38 | */
39 | @RequestMapping(value = "/", method = RequestMethod.POST)
40 | ResponseEntity save(@RequestParam String url) {
41 | System.out.println(url);
42 | if (urlValidator.isValid(url)) {
43 | String hash = com.google.common.hash.Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString();
44 | redisTemplate.opsForValue().set(hash, url);
45 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK);
46 | } else {
47 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
48 | }
49 | }
50 |
51 | /**
52 | * curl -v -X GET http://localhost:8080/58f3ae21
53 | */
54 | @RequestMapping(value = "{hash}", method = RequestMethod.GET)
55 | ResponseEntity> get(@PathVariable String hash) {
56 | String url = redisTemplate.opsForValue().get(hash);
57 | // 本当はリダイレクトするのだが、今回はレスポンスボディに入れて200を返す
58 | if (url != null) {
59 | return new ResponseEntity<>(url, HttpStatus.OK);
60 | } else {
61 | return new ResponseEntity<>(HttpStatus.NOT_FOUND);
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/exercise/01-urlshortener/urlshortener/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise01-urlshortener
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | exercise01-urlshortener
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.1.8.RELEASE
18 |
19 |
20 |
21 |
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-redis
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-web
30 |
31 |
32 | com.google.guava
33 | guava
34 | 18.0
35 |
36 |
37 | commons-validator
38 | commons-validator
39 | 1.4.0
40 |
41 |
42 | com.jayway.restassured
43 | rest-assured
44 | 2.3.2
45 | test
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-starter-test
50 | test
51 |
52 |
53 |
54 |
55 | UTF-8
56 | demo.UrlShortener
57 | 1.8
58 |
59 |
60 |
61 |
62 |
63 | org.springframework.boot
64 | spring-boot-maven-plugin
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/exercise/03-netflix/configserver/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise03-configserver
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | exercise03-configserver
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.1.8.RELEASE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.cloud
26 | spring-cloud-starters
27 | 1.0.0.M2
28 | pom
29 | import
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.cloud
37 | spring-cloud-config-server
38 |
39 |
40 |
41 |
42 | UTF-8
43 | org.springframework.cloud.config.server.ConfigServerApplication
44 | 1.8
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-maven-plugin
52 |
53 |
54 |
55 |
56 |
57 |
58 | spring-milestones
59 | Spring Milestones
60 | http://repo.spring.io/milestone
61 |
62 | false
63 |
64 |
65 |
66 | spring-snapshots
67 | Spring Snapshots
68 | http://repo.spring.io/libs-snapshot-local
69 |
70 | true
71 |
72 |
73 |
74 |
75 |
76 | spring-milestones
77 | Spring Milestones
78 | http://repo.spring.io/milestone
79 |
80 | false
81 |
82 |
83 |
84 | spring-snapshots
85 | Spring Snapshots
86 | http://repo.spring.io/libs-snapshot-local
87 |
88 | true
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/exercise/02-distributed-config/configserver/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise02-configserver
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | exercise02-configserver
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.1.8.RELEASE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.cloud
26 | spring-cloud-starters
27 | 1.0.0.M2
28 | pom
29 | import
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.cloud
37 | spring-cloud-config-server
38 |
39 |
40 |
41 |
42 | UTF-8
43 | org.springframework.cloud.config.server.ConfigServerApplication
44 | 1.8
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-maven-plugin
52 |
53 |
54 |
55 |
56 |
57 |
58 | spring-milestones
59 | Spring Milestones
60 | http://repo.spring.io/milestone
61 |
62 | false
63 |
64 |
65 |
66 | spring-snapshots
67 | Spring Snapshots
68 | http://repo.spring.io/libs-snapshot-local
69 |
70 | true
71 |
72 |
73 |
74 |
75 |
76 | spring-milestones
77 | Spring Milestones
78 | http://repo.spring.io/milestone
79 |
80 | false
81 |
82 |
83 |
84 | spring-snapshots
85 | Spring Snapshots
86 | http://repo.spring.io/libs-snapshot-local
87 |
88 | true
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.demo
7 | exercise03-hystrix-dashboard
8 | jar
9 | exercise03-hystrix-dashboard
10 | 1.0.0.BUILD-SNAPSHOT
11 |
12 |
13 | org.springframework.boot
14 | spring-boot-starter-parent
15 | 1.1.8.RELEASE
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-maven-plugin
25 |
26 |
27 |
28 | maven-deploy-plugin
29 |
30 | true
31 |
32 |
33 |
34 |
35 |
36 |
37 | 1.0.0.M2
38 |
39 |
40 |
41 |
42 |
43 | org.springframework.cloud
44 | spring-cloud-starters
45 | ${spring-cloud-config.version}
46 | pom
47 | import
48 |
49 |
50 |
51 |
52 |
53 | org.springframework.cloud
54 | spring-cloud-starter-hystrix-dashboard
55 |
56 |
57 |
58 |
59 |
60 | spring-snapshots
61 | Spring Snapshots
62 | http://repo.spring.io/libs-snapshot-local
63 |
64 | true
65 |
66 |
67 |
68 | spring-milestones
69 | Spring Milestones
70 | http://repo.spring.io/libs-milestone-local
71 |
72 | false
73 |
74 |
75 |
76 | spring-releases
77 | Spring Releases
78 | http://repo.spring.io/libs-release-local
79 |
80 | false
81 |
82 |
83 |
84 |
85 |
86 | spring-snapshots
87 | Spring Snapshots
88 | http://repo.spring.io/libs-snapshot-local
89 |
90 | true
91 |
92 |
93 |
94 | spring-milestones
95 | Spring Milestones
96 | http://repo.spring.io/libs-milestone-local
97 |
98 | false
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/exercise/03-netflix/eureka-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise03-eureka-server
8 | 1.0-SNAPSHOT
9 | jar
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 1.1.8.RELEASE
15 |
16 |
17 |
18 |
19 |
20 |
21 | org.springframework.cloud
22 | spring-cloud-starters
23 | 1.0.0.M2
24 | pom
25 | import
26 |
27 |
28 |
29 |
30 |
31 | UTF-8
32 | demo.EurekaServer
33 | 1.8
34 |
35 |
36 |
37 | org.springframework.cloud
38 | spring-cloud-starter-eureka-server
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 |
47 |
48 | spring-milestones
49 | Spring Milestones
50 | http://repo.spring.io/milestone
51 |
52 | false
53 |
54 |
55 |
56 | spring-snapshots
57 | Spring Snapshots
58 | http://repo.spring.io/libs-snapshot-local
59 |
60 | true
61 |
62 |
63 |
64 |
65 |
66 | spring-milestones
67 | Spring Milestones
68 | http://repo.spring.io/milestone
69 |
70 | false
71 |
72 |
73 |
74 | spring-snapshots
75 | Spring Snapshots
76 | http://repo.spring.io/libs-snapshot-local
77 |
78 | true
79 |
80 |
81 |
82 |
83 |
84 |
85 | org.springframework.boot
86 | spring-boot-maven-plugin
87 |
89 |
90 |
91 |
92 | com.netflix.eureka
93 | eureka-core
94 |
95 |
96 | com.netflix.eureka
97 | eureka-client
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/exercise/03-netflix/hystrix-dashboard/src/main/java/hystrixdashboard/stream/MockStreamServlet.java:
--------------------------------------------------------------------------------
1 | package hystrixdashboard.stream;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import javax.servlet.ServletException;
7 | import javax.servlet.http.HttpServlet;
8 | import javax.servlet.http.HttpServletRequest;
9 | import javax.servlet.http.HttpServletResponse;
10 | import java.io.*;
11 | import java.nio.charset.Charset;
12 |
13 | /**
14 | * Simulate an event stream URL by retrieving pre-canned data instead of going to live servers.
15 | */
16 | public class MockStreamServlet extends HttpServlet {
17 | private static final long serialVersionUID = 1L;
18 | private static final Logger logger = LoggerFactory.getLogger(MockStreamServlet.class);
19 |
20 | public MockStreamServlet() {
21 | super();
22 | }
23 |
24 | /**
25 | * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
26 | */
27 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
28 | String filename = request.getParameter("file");
29 | if (filename == null) {
30 | // default to using hystrix.stream
31 | filename = "hystrix.stream";
32 | } else {
33 | // strip any .. / characters to avoid security problems
34 | filename = filename.replaceAll("\\.\\.", "");
35 | filename = filename.replaceAll("/", "");
36 | }
37 | int delay = 500;
38 | String delayArg = request.getParameter("delay");
39 | if (delayArg != null) {
40 | delay = Integer.parseInt(delayArg);
41 | }
42 |
43 | int batch = 1;
44 | String batchArg = request.getParameter("batch");
45 | if (batchArg != null) {
46 | batch = Integer.parseInt(batchArg);
47 | }
48 |
49 | String data = getFileFromPackage(filename);
50 | String lines[] = data.split("\n");
51 |
52 | response.setContentType("text/event-stream");
53 | response.setCharacterEncoding("UTF-8");
54 |
55 | int batchCount = 0;
56 | // loop forever unless the user closes the connection
57 | for (;;) {
58 | for (String s : lines) {
59 | s = s.trim();
60 | if (s.length() > 0) {
61 | try {
62 | response.getWriter().println(s);
63 | response.getWriter().println(""); // a newline is needed after each line for the events to trigger
64 | response.getWriter().flush();
65 | batchCount++;
66 | } catch (Exception e) {
67 | logger.warn("Exception writing mock data to output.", e);
68 | // most likely the user closed the connection
69 | return;
70 | }
71 | if (batchCount == batch) {
72 | // we insert the delay whenever we finish a batch
73 | try {
74 | // simulate the delays we get from the real feed
75 | Thread.sleep(delay);
76 | } catch (InterruptedException e) {
77 | // ignore
78 | }
79 | // reset
80 | batchCount = 0;
81 | }
82 | }
83 | }
84 | }
85 | }
86 |
87 | private String getFileFromPackage(String filename) {
88 | try {
89 | String file = "/" + this.getClass().getPackage().getName().replace('.', '/') + "/" + filename;
90 | InputStream is = this.getClass().getResourceAsStream(file);
91 | try {
92 | /* this is FAR too much work just to get a string from a file */
93 | BufferedReader in = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
94 | StringWriter s = new StringWriter();
95 | int c = -1;
96 | while ((c = in.read()) > -1) {
97 | s.write(c);
98 | }
99 | return s.toString();
100 | } finally {
101 | is.close();
102 | }
103 | } catch (Exception e) {
104 | throw new RuntimeException("Could not find file: " + filename, e);
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/exercise/02-distributed-config/urlshortener/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise02-urlshortener
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | exercise02-urlshortener
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.1.8.RELEASE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.cloud
26 | spring-cloud-starters
27 | 1.0.0.M2
28 | pom
29 | import
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-redis
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-web
42 |
43 |
44 | org.springframework.cloud
45 | spring-cloud-starter
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-starter-actuator
50 |
51 |
52 | com.google.guava
53 | guava
54 | 18.0
55 |
56 |
57 | commons-validator
58 | commons-validator
59 | 1.4.0
60 |
61 |
62 | com.jayway.restassured
63 | rest-assured
64 | 2.3.2
65 | test
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-starter-test
70 | test
71 |
72 |
73 |
74 |
75 | UTF-8
76 | demo.UrlShortener
77 | 1.8
78 |
79 |
80 |
81 |
82 |
83 | org.springframework.boot
84 | spring-boot-maven-plugin
85 |
86 |
87 |
88 |
89 |
90 | spring-milestones
91 | Spring Milestones
92 | http://repo.spring.io/milestone
93 |
94 | false
95 |
96 |
97 |
98 | spring-snapshots
99 | Spring Snapshots
100 | http://repo.spring.io/libs-snapshot-local
101 |
102 | true
103 |
104 |
105 |
106 |
107 |
108 | spring-milestones
109 | Spring Milestones
110 | http://repo.spring.io/milestone
111 |
112 | false
113 |
114 |
115 |
116 | spring-snapshots
117 | Spring Snapshots
118 | http://repo.spring.io/libs-snapshot-local
119 |
120 | true
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener-ui/src/main/java/demo/App.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
4 | import org.hibernate.validator.constraints.NotEmpty;
5 | import org.hibernate.validator.constraints.URL;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.beans.factory.annotation.Value;
10 | import org.springframework.boot.SpringApplication;
11 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
12 | import org.springframework.cloud.context.config.annotation.RefreshScope;
13 | import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
14 | import org.springframework.cloud.netflix.hystrix.EnableHystrix;
15 | import org.springframework.context.annotation.ComponentScan;
16 | import org.springframework.context.annotation.Configuration;
17 | import org.springframework.http.HttpEntity;
18 | import org.springframework.http.HttpHeaders;
19 | import org.springframework.http.MediaType;
20 | import org.springframework.stereotype.Component;
21 | import org.springframework.stereotype.Controller;
22 | import org.springframework.util.LinkedMultiValueMap;
23 | import org.springframework.util.MultiValueMap;
24 | import org.springframework.validation.BindingResult;
25 | import org.springframework.validation.annotation.Validated;
26 | import org.springframework.web.bind.annotation.ModelAttribute;
27 | import org.springframework.web.bind.annotation.PathVariable;
28 | import org.springframework.web.bind.annotation.RequestMapping;
29 | import org.springframework.web.bind.annotation.RequestMethod;
30 | import org.springframework.web.client.RestTemplate;
31 | import org.springframework.web.servlet.mvc.support.RedirectAttributes;
32 |
33 | import java.io.IOException;
34 |
35 | import static java.util.Collections.singletonMap;
36 |
37 | @EnableAutoConfiguration
38 | @ComponentScan
39 | @Controller
40 | @Configuration
41 | @EnableEurekaClient
42 | @EnableHystrix
43 | public class App {
44 | @Autowired
45 | RemoteCommand remoteCommand;
46 |
47 | public static void main(String[] args) {
48 | SpringApplication.run(App.class, args);
49 | }
50 |
51 | @ModelAttribute
52 | ShortenForm setupForm() {
53 | return new ShortenForm();
54 | }
55 |
56 | @RequestMapping("/")
57 | String home() {
58 | return "index";
59 | }
60 |
61 | @RequestMapping(value = "shorten", method = RequestMethod.POST)
62 | String shorten(@Validated ShortenForm form, BindingResult result, RedirectAttributes attributes) throws IOException {
63 | if (result.hasErrors()) {
64 | return "index";
65 | }
66 | String shortenUrl = remoteCommand.shorten(form.getUrl());
67 | attributes.addFlashAttribute("shortenUrl", shortenUrl);
68 | return "redirect:/";
69 | }
70 |
71 | @RequestMapping(value = "{hash}", method = RequestMethod.GET)
72 | String redirect(@PathVariable String hash) {
73 | String url = remoteCommand.getUrl(hash);
74 | return "redirect:" + url;
75 | }
76 | }
77 |
78 | class ShortenForm {
79 | @NotEmpty
80 | @URL
81 | private String url;
82 |
83 | public void setUrl(String url) {
84 | this.url = url;
85 | }
86 |
87 | public String getUrl() {
88 | return url;
89 | }
90 | }
91 |
92 | @Component
93 | @RefreshScope
94 | class RemoteCommand {
95 | @Autowired
96 | RestTemplate restTemplate;
97 | @Value("${urlshorten.api.url:http://localhost:8081}")
98 | String apiUrl;
99 | Logger logger = LoggerFactory.getLogger(RemoteCommand.class);
100 |
101 | @HystrixCommand(fallbackMethod = "defaultShorten")
102 | public String shorten(String url) {
103 | logger.info("calling shorten {}", url);
104 | HttpHeaders headers = new HttpHeaders();
105 | headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
106 | MultiValueMap body = new LinkedMultiValueMap<>();
107 | body.add("url", url);
108 | HttpEntity> request = new HttpEntity<>(body, headers);
109 | return restTemplate.postForEntity(apiUrl, request, String.class).getBody();
110 | }
111 |
112 | public String defaultShorten(String url) {
113 | logger.info("failed shorten {}", url);
114 | return "failed!";
115 | }
116 |
117 | @HystrixCommand(fallbackMethod = "defaultGetUrl")
118 | public String getUrl(String hash) {
119 | logger.info("calling getUrl {}", hash);
120 | return restTemplate.getForObject(apiUrl + "/{hash}", String.class, singletonMap("hash", hash));
121 | }
122 |
123 | public String defaultGetUrl(String hash) {
124 | logger.info("failed getUrl {}", hash);
125 | return "failed!";
126 | }
127 | }
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise03-urlshortener
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | exercise03-urlshortener
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.1.8.RELEASE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.cloud
26 | spring-cloud-starters
27 | 1.0.0.M2
28 | pom
29 | import
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-redis
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-web
42 |
43 |
44 | org.springframework.cloud
45 | spring-cloud-starter
46 |
47 |
48 | org.springframework.cloud
49 | spring-cloud-starter-eureka
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-actuator
54 |
55 |
56 | com.google.guava
57 | guava
58 | 18.0
59 |
60 |
61 | commons-validator
62 | commons-validator
63 | 1.4.0
64 |
65 |
66 | com.jayway.restassured
67 | rest-assured
68 | 2.3.2
69 | test
70 |
71 |
72 | org.springframework.boot
73 | spring-boot-starter-test
74 | test
75 |
76 |
77 |
78 |
79 | UTF-8
80 | demo.UrlShortener
81 | 1.8
82 |
83 |
84 |
85 |
86 |
87 | org.springframework.boot
88 | spring-boot-maven-plugin
89 |
90 |
91 |
92 |
93 |
94 | spring-milestones
95 | Spring Milestones
96 | http://repo.spring.io/milestone
97 |
98 | false
99 |
100 |
101 |
102 | spring-snapshots
103 | Spring Snapshots
104 | http://repo.spring.io/libs-snapshot-local
105 |
106 | true
107 |
108 |
109 |
110 |
111 |
112 | spring-milestones
113 | Spring Milestones
114 | http://repo.spring.io/milestone
115 |
116 | false
117 |
118 |
119 |
120 | spring-snapshots
121 | Spring Snapshots
122 | http://repo.spring.io/libs-snapshot-local
123 |
124 | true
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/exercise/03-netflix/urlshortener-ui/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | demo
7 | exercise03-urlshortener-ui
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | exercise03-urlshortener-ui
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 1.1.8.RELEASE
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.cloud
26 | spring-cloud-starters
27 | 1.0.0.M2
28 | pom
29 | import
30 |
31 |
32 |
33 |
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-redis
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-web
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-starter-thymeleaf
46 |
47 |
48 | org.springframework.cloud
49 | spring-cloud-starter
50 |
51 |
52 | org.springframework.cloud
53 | spring-cloud-starter-eureka
54 |
55 |
56 | org.springframework.cloud
57 | spring-cloud-starter-hystrix
58 |
59 |
60 | org.springframework.boot
61 | spring-boot-starter-actuator
62 |
63 |
64 | com.google.guava
65 | guava
66 | 17.0
67 |
68 |
69 | commons-validator
70 | commons-validator
71 | 1.4.0
72 |
73 |
74 | com.jayway.restassured
75 | rest-assured
76 | 2.3.2
77 | test
78 |
79 |
80 | org.springframework.boot
81 | spring-boot-starter-test
82 | test
83 |
84 |
85 |
86 |
87 | UTF-8
88 | demo.App
89 | 1.8
90 |
91 |
92 |
93 |
94 |
95 | org.springframework.boot
96 | spring-boot-maven-plugin
97 |
98 |
99 |
100 |
101 |
102 | spring-milestones
103 | Spring Milestones
104 | http://repo.spring.io/milestone
105 |
106 | false
107 |
108 |
109 |
110 | spring-snapshots
111 | Spring Snapshots
112 | http://repo.spring.io/libs-snapshot-local
113 |
114 | true
115 |
116 |
117 |
118 |
119 |
120 | spring-milestones
121 | Spring Milestones
122 | http://repo.spring.io/milestone
123 |
124 | false
125 |
126 |
127 |
128 | spring-snapshots
129 | Spring Snapshots
130 | http://repo.spring.io/libs-snapshot-local
131 |
132 | true
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/instruction/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/TERASOLUNAGlobalFrameworkDevelopmentPolicy"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/instruction/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.http://sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\TERASOLUNAGlobalFrameworkDevelopmentPolicy.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\TERASOLUNAGlobalFrameworkDevelopmentPolicy.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/instruction/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # TERASOLUNA Global Framework Development Policy documentation build configuration file, created by
4 | # sphinx-quickstart on Thu Apr 17 19:06:14 2014.
5 | #
6 | # This file is execfile()d with the current directory set to its
7 | # containing dir.
8 | #
9 | # Note that not all possible configuration values are present in this
10 | # autogenerated file.
11 | #
12 | # All configuration values have a default; values that are commented out
13 | # serve to show the default.
14 |
15 | import sys
16 | import os
17 |
18 | # If extensions (or modules to document with autodoc) are in another directory,
19 | # add these directories to sys.path here. If the directory is relative to the
20 | # documentation root, use os.path.abspath to make it absolute, like shown here.
21 | #sys.path.insert(0, os.path.abspath('.'))
22 |
23 | # -- General configuration ------------------------------------------------
24 |
25 | # If your documentation needs a minimal Sphinx version, state it here.
26 | #needs_sphinx = '1.0'
27 |
28 | # Add any Sphinx extension module names here, as strings. They can be
29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 | # ones.
31 | extensions = [
32 | 'sphinx.ext.pngmath', 'sphinx.ext.todo'
33 | ]
34 |
35 | # Add any paths that contain templates here, relative to this directory.
36 | templates_path = ['_templates']
37 |
38 | # The suffix of source filenames.
39 | source_suffix = '.rst'
40 |
41 | # The encoding of source files.
42 | #source_encoding = 'utf-8-sig'
43 |
44 | # The master toctree document.
45 | master_doc = 'readme'
46 |
47 | # General information about the project.
48 | project = u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書'
49 | copyright = u'2014, making'
50 |
51 | # The version info for the project you're documenting, acts as replacement for
52 | # |version| and |release|, also used in various other places throughout the
53 | # built documents.
54 | #
55 | # The short X.Y version.
56 | version = '1.0.0'
57 | # The full version, including alpha/beta/rc tags.
58 | release = '20141111'
59 |
60 | # The language for content autogenerated by Sphinx. Refer to documentation
61 | # for a list of supported languages.
62 | #language = None
63 |
64 | # There are two options for replacing |today|: either, you set today to some
65 | # non-false value, then it is used:
66 | #today = ''
67 | # Else, today_fmt is used as the format for a strftime call.
68 | #today_fmt = '%B %d, %Y'
69 |
70 | # List of patterns, relative to source directory, that match files and
71 | # directories to ignore when looking for source files.
72 | exclude_patterns = ['_build']
73 |
74 | # The reST default role (used for this markup: `text`) to use for all
75 | # documents.
76 | #default_role = None
77 |
78 | # If true, '()' will be appended to :func: etc. cross-reference text.
79 | #add_function_parentheses = True
80 |
81 | # If true, the current module name will be prepended to all description
82 | # unit titles (such as .. function::).
83 | #add_module_names = True
84 |
85 | # If true, sectionauthor and moduleauthor directives will be shown in the
86 | # output. They are ignored by default.
87 | #show_authors = False
88 |
89 | # The name of the Pygments (syntax highlighting) style to use.
90 | pygments_style = 'sphinx'
91 |
92 | # A list of ignored prefixes for module index sorting.
93 | #modindex_common_prefix = []
94 |
95 | # If true, keep warnings as "system message" paragraphs in the built documents.
96 | #keep_warnings = False
97 |
98 |
99 | # -- Options for HTML output ----------------------------------------------
100 |
101 | # The theme to use for HTML and HTML Help pages. See the documentation for
102 | # a list of builtin themes.
103 | html_theme = 'default'
104 |
105 | # Theme options are theme-specific and customize the look and feel of a theme
106 | # further. For a list of options available for each theme, see the
107 | # documentation.
108 | #html_theme_options = {}
109 |
110 | # Add any paths that contain custom themes here, relative to this directory.
111 | #html_theme_path = []
112 |
113 | # The name for this set of Sphinx documents. If None, it defaults to
114 | # " v documentation".
115 | #html_title = None
116 |
117 | # A shorter title for the navigation bar. Default is the same as html_title.
118 | #html_short_title = None
119 |
120 | # The name of an image file (relative to this directory) to place at the top
121 | # of the sidebar.
122 | #html_logo = None
123 |
124 | # The name of an image file (within the static path) to use as favicon of the
125 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
126 | # pixels large.
127 | #html_favicon = None
128 |
129 | # Add any paths that contain custom static files (such as style sheets) here,
130 | # relative to this directory. They are copied after the builtin static files,
131 | # so a file named "default.css" will overwrite the builtin "default.css".
132 | html_static_path = ['_static']
133 |
134 | # Add any extra paths that contain custom files (such as robots.txt or
135 | # .htaccess) here, relative to this directory. These files are copied
136 | # directly to the root of the documentation.
137 | #html_extra_path = []
138 |
139 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
140 | # using the given strftime format.
141 | #html_last_updated_fmt = '%b %d, %Y'
142 |
143 | # If true, SmartyPants will be used to convert quotes and dashes to
144 | # typographically correct entities.
145 | #html_use_smartypants = True
146 |
147 | # Custom sidebar templates, maps document names to template names.
148 | #html_sidebars = {}
149 |
150 | # Additional templates that should be rendered to pages, maps page names to
151 | # template names.
152 | #html_additional_pages = {}
153 |
154 | # If false, no module index is generated.
155 | #html_domain_indices = True
156 |
157 | # If false, no index is generated.
158 | #html_use_index = True
159 |
160 | # If true, the index is split into individual pages for each letter.
161 | #html_split_index = False
162 |
163 | # If true, links to the reST sources are added to the pages.
164 | #html_show_sourcelink = True
165 |
166 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
167 | #html_show_sphinx = True
168 |
169 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
170 | #html_show_copyright = True
171 |
172 | # If true, an OpenSearch description file will be output, and all pages will
173 | # contain a tag referring to it. The value of this option must be the
174 | # base URL from which the finished HTML is served.
175 | #html_use_opensearch = ''
176 |
177 | # This is the file name suffix for HTML files (e.g. ".xhtml").
178 | #html_file_suffix = None
179 |
180 | # Output file base name for HTML help builder.
181 | htmlhelp_basename = 'ShpinxBlankProject'
182 |
183 |
184 | # -- Options for LaTeX output ---------------------------------------------
185 | language = 'ja'
186 | latex_docclass = {'manual': 'jsbook'}
187 | latex_elements = {
188 | # The paper size ('letterpaper' or 'a4paper').
189 | #'papersize': 'letterpaper',
190 | 'papersize': 'a4paper',
191 | 'classoptions': ',dvipdfmx,openany,oneside,bookmarks=true,bookmarksnumbered=true',
192 | 'preamble': '''
193 | \\definecolor{VerbatimColor}{rgb}{0.95,0.95,0.95}
194 | \\setcounter{tocdepth}{4}
195 | ''',
196 | # The font size ('10pt', '11pt' or '12pt').
197 | #'pointsize': '10pt',
198 |
199 | # Additional stuff for the LaTeX preamble.
200 | #'preamble': '',
201 | }
202 |
203 | # Grouping the document tree into LaTeX files. List of tuples
204 | # (source start file, target name, title,
205 | # author, documentclass [howto, manual, or own class]).
206 | latex_documents = [
207 | ('readme', 'SphinxBlankProject.tex', u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書',
208 | u'making', 'manual'),
209 | ]
210 |
211 | # The name of an image file (relative to this directory) to place at the top of
212 | # the title page.
213 | #latex_logo = None
214 |
215 | # For "manual" documents, if this is true, then toplevel headings are parts,
216 | # not chapters.
217 | #latex_use_parts = False
218 |
219 | # If true, show page references after internal links.
220 | #latex_show_pagerefs = False
221 |
222 | # If true, show URL addresses after external links.
223 | #latex_show_urls = False
224 |
225 | # Documents to append as an appendix to all manuals.
226 | #latex_appendices = []
227 |
228 | # If false, no module index is generated.
229 | #latex_domain_indices = True
230 |
231 |
232 | # -- Options for manual page output ---------------------------------------
233 |
234 | # One entry per manual page. List of tuples
235 | # (source start file, name, description, authors, manual section).
236 | man_pages = [
237 | ('readme', 'SphinxBlankProject', u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書',
238 | [u'making'], 1)
239 | ]
240 |
241 | # If true, show URL addresses after external links.
242 | #man_show_urls = False
243 |
244 |
245 | # -- Options for Texinfo output -------------------------------------------
246 |
247 | # Grouping the document tree into Texinfo files. List of tuples
248 | # (source start file, target name, title, author,
249 | # dir menu entry, description, category)
250 | texinfo_documents = [
251 | ('readme', 'SphinxBlankProject', u'JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書',
252 | u'making', 'SphinxBlankProject', 'One line description of project.',
253 | 'Miscellaneous'),
254 | ]
255 |
256 | # Documents to append as an appendix to all manuals.
257 | #texinfo_appendices = []
258 |
259 | # If false, no module index is generated.
260 | #texinfo_domain_indices = True
261 |
262 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
263 | #texinfo_show_urls = 'footnote'
264 |
265 | # If true, do not generate a @detailmenu in the "Top" node's menu.
266 | #texinfo_no_detailmenu = False
267 |
--------------------------------------------------------------------------------
/instruction/readme.rst:
--------------------------------------------------------------------------------
1 | JJUG CCC Fall 2014 [R5-3] Spring Bootハンズオン~Spring Bootで作るマイクロサービスアーキテクチャ!手順書 #jjug_ccc #ccc_r53
2 |
3 | .. contents:: 目次
4 | :depth: 2
5 |
6 |
7 | 事前準備
8 | ================================================================================
9 | ハンズオン資材は\ http://bit.ly/1uYCKF0\ からダウンロードしてください。
10 |
11 |
12 | 必要なソフトウェアのインストール
13 | --------------------------------------------------------------------------------
14 |
15 | Mac/Windowsユーザー向けに記述しています。Linuxで実施する場合は同等の手順を実施してください。
16 |
17 | Java SE 8
18 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19 | http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html\ からJava SE Development Kit 8u25 (8以上であればおそらくOK)をダウンロードして、
20 | インストールしてください。
21 |
22 | 環境変数\ ``JAVA_HOME``\ の設定と\ ``PATH``\ の追加を必ず行ってください。
23 |
24 | Maven
25 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26 | http://ftp.meisei-u.ac.jp/mirror/apache/dist/maven/maven-3/3.2.3/binaries/apache-maven-3.2.3-bin.tar.gz\ からMavenをダウンロードして、
27 | 展開したディレクトリのbinフォルダを環境変数\ ``PATH``\ に追加してください。
28 |
29 | 尚、(ハンズオン資材のルートフォルダ)/software/apache-maven-3.2.3-bin.tar.gzにダウンロード済みです。
30 |
31 | Git Bash (Windowsの場合)
32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33 | Windowsの場合、
34 |
35 | http://git-scm.com/download/win\ からGitをダウンロードしてインストールしてください。
36 |
37 | 以下で実行するコマンドは全て\ **Gitに付属しているGit Bashを用いて実行してください**\ 。
38 |
39 | jq (オプション)
40 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41 | 必須ではないですが、JSON出力の整形用に\ http://stedolan.github.io/jq/\ をインストールしておくと良いです。
42 |
43 | Redis
44 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45 |
46 | Macの場合は、以下を実行してください。(要:XCode)
47 |
48 | .. code-block:: bash
49 |
50 | $ cd (ハンズオン資材のルートフォルダ)/software/redis-2.8.17
51 | $ tar xzvf redis-2.8.17.tar.gz
52 | $ cd redis-2.8.17
53 | $ make
54 |
55 | Windows 64ビットの場合は、(ハンズオン資材のルートフォルダ)/software/redis-2.8.17/redis-2.8.17.zipを展開してください。
56 |
57 | Windows 32ビットの場合は、(ハンズオン資材のルートフォルダ)/software/redis-2.8.17/edisbin.zipを展開してください。
58 |
59 |
60 |
61 | Gitbucket
62 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63 | https://github.com/takezoe/gitbucket/releases/download/2.5/gitbucket.war\ より、Gitbucketをダウンロードしてください。
64 |
65 | 尚、(ハンズオン資材のルートフォルダ)/software/gitbucket.warにダウンロード済みです。
66 |
67 | IntelliJ IDEA
68 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69 |
70 | https://www.jetbrains.com/idea/download/\ より、IntelliJ IDEA 14のCommunity EditionまたはUltimate Editionをダウンロードしてインストールしてください。
71 |
72 | Spring Tool SuiteやEclipseを使用してもハンズオンを実施できますが、ハンズオンの説明はIntelliJ IDEAを用いて行います。
73 |
74 |
75 | Mavenリポジトリのコピー
76 | --------------------------------------------------------------------------------
77 | ハンズオンはオフライン環境で行います。
78 |
79 | 必要なライブラリをインターネットからダウンロードせず直接Mavenリポジトリにコピーします。
80 |
81 | (ハンズオン資材のルートフォルダ)/repository以下を(ホームディレクトリ)/.m2/repository以下にコピーしてください。
82 |
83 | .. code-block:: bash
84 |
85 | $ cp -rf (ハンズオン資材のルートフォルダ)/repository/* ~/.m2/repository/
86 |
87 | \ ``overwrite /Users/maki/.m2/repository/antlr/antlr/2.7.2/_maven.repositories? (y/n [n])``\ というように上書きするかどうか聞かれる場合は
88 |
89 |
90 | .. code-block:: bash
91 |
92 | $ \cp -rf (ハンズオン資材のルートフォルダ)/repository/* ~/.m2/repository/
93 |
94 | を実行してください。
95 |
96 | 演習の全体像
97 | ================================================================================
98 |
99 | 本演習で「URL短縮サービス」を題材にマイクロサービスアーキテクチャを構築します。
100 |
101 | 最終的に構築するアーキテクチャを以下に示します。
102 |
103 | .. figure:: ./images/exercise00-01.png
104 | :width: 80%
105 |
106 | 演習1ではSpring Bootを用いて単一の「URL短縮サービス」を作成します。
107 |
108 | .. figure:: ./images/exercise00-02.png
109 | :width: 80%
110 |
111 | 演習2ではSpring Cloud Configを用いて「URL短縮サービス」に動的コンフィギュレーションを追加します。
112 |
113 | .. figure:: ./images/exercise00-03.png
114 | :width: 80%
115 |
116 | 演習3では「URL短縮サービス」のUIを追加し、Spring Cloud Netflixを用いて「URL短縮サービス」にマイクロサービスアーキテクチャのための様々なパターンを追加します。
117 |
118 | .. figure:: ./images/exercise00-04.png
119 | :width: 80%
120 |
121 |
122 | 本来は複数のマシンを用いて構築しますが、本演習では1つのマシン上で全てのサービスを起動します。
123 |
124 | 演習1 Spring Bootで「URL短縮サービス」を作る
125 | ================================================================================
126 |
127 | 演習1ではマイクロサービス界でのFizzBuzz問題である、「URL短縮サービス」を作ります。
128 |
129 | 課題1で\ ``ConcurrentHashMap``\ を使った実装。課題2でRedisを使った実装を行います。
130 |
131 | インポートするプロジェクトにほとんどのコードが実装されているので、課題で実装するコードはほんの数行です。
132 |
133 | 演習プロジェクトの作成・インポート
134 | --------------------------------------------------------------------------------
135 |
136 | 新規プロジェクト作成
137 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
138 |
139 | IntelliJ IDEAを開いて「New Project」で新規プロジェクトを作成します。
140 | 以下のように「Empty Project」を選択してください。
141 |
142 | .. figure:: ./images/import-exercise01-01.png
143 | :width: 80%
144 |
145 | 以下の設定値を入力してください。\ **デフォルト値から変更するので注意してください** \ 。
146 |
147 | .. tabularcolumns:: |p{0.30\linewidth}|p{0.70\linewidth}|
148 | .. list-table::
149 | :stub-columns: 1
150 | :widths: 30 70
151 |
152 | * - | Project name
153 | - | jjugccc-handson
154 | * - | Project location
155 | - | (ハンズオン資材のルートフォルダ)/exercise
156 |
157 |
158 | .. figure:: ./images/import-exercise01-02.png
159 | :width: 80%
160 |
161 | 「Project Structure」で以下の設定値を入力してください。
162 |
163 | .. tabularcolumns:: |p{0.30\linewidth}|p{0.70\linewidth}|
164 | .. list-table::
165 | :stub-columns: 1
166 | :widths: 30 70
167 |
168 | * - | Project SDK
169 | - | 1.8
170 | * - | Project language level
171 | - | 8
172 |
173 |
174 | .. figure:: ./images/import-exercise01-03.png
175 | :width: 80%
176 |
177 |
178 | JDKが未設定の場合は、「New」を押してJDKを設定してください。JAVA_HOMEに相当するフォルダを選択すれば良いです。
179 |
180 |
181 | .. figure:: ./images/import-exercise01-04.png
182 | :width: 40%
183 |
184 | 演習プロジェクトのインポート
185 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
186 | 「File」->「Import Module」で演習プロジェクトをインポートします。
187 |
188 | .. figure:: ./images/import-exercise01-05.png
189 | :width: 80%
190 |
191 | 「(ハンズオン資材のルートフォルダ)/exercise/01-urlshortener」を選択してください。
192 |
193 | .. figure:: ./images/import-exercise01-06.png
194 | :width: 80%
195 |
196 | 「Import module from external model」で「Maven」を指定してください。
197 |
198 | .. figure:: ./images/import-exercise01-07.png
199 | :width: 80%
200 |
201 | \ **「Search for projects recursively」と「Import Maven projects automatically」にチェックを入れて**\ 、次に進んでください。
202 |
203 | .. figure:: ./images/import-exercise01-08.png
204 | :width: 80%
205 |
206 | 「Next」を繰り返すと、以下のように演習1用のMavenプロジェクトがインポートされます。
207 |
208 |
209 | .. figure:: ./images/import-exercise01-09.png
210 | :width: 80%
211 |
212 |
213 | 課題1 TODOを埋めてプログラムを完成させてください
214 | --------------------------------------------------------------------------------
215 |
216 | \ ``demo.UrlShortener``\ を編集してください。
217 |
218 | 以下\ ``TODO``\ 部分を埋めてください。
219 |
220 | .. code-block:: java
221 |
222 | @RequestMapping(value = "/", method = RequestMethod.POST)
223 | ResponseEntity save(@RequestParam String url) {
224 | if (urlValidator.isValid(url)) {
225 | String hash = ""/* TODO (1) URLをハッシュ化。ハッシュアルゴリズムには 32-bit murmur3 algorithm を使用する。 */;
226 | // ヒント: com.google.common.hash.Hashing.murmur3_32()を使う
227 | // TODO (2) urlMapにhashに紐づくURLを追加する。
228 | return new ResponseEntity<>(urlShortenUrl + "/" + hash, HttpStatus.OK);
229 | } else {
230 | return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
231 | }
232 | }
233 |
234 | \ ``UrlShortener``\ クラスを右クリックして、\ ``Run UrlShortener.main()``\ をクリックしてください。
235 |
236 | .. figure:: ./images/exercise01-01.png
237 | :width: 80%
238 |
239 |
240 | 以下のようにも実行できます。
241 |
242 | .. code-block:: bash
243 |
244 | $ cd (ハンズオン資材のルートフォルダ)/exercise/01-urlshortener
245 | $ mvn spring-boot:run -f urlshortener/pom.xml
246 |
247 | 以下の結果が返るか確認してください。
248 |
249 | .. code-block:: bash
250 |
251 | $ curl -X POST http://localhost:8080 -d "url=http://google.com"
252 | http://localhost:8080/58f3ae21
253 | $ curl -X GET http://localhost:8080/58f3ae21
254 | http://google.com
255 |
256 |
257 | \ ``UrlShortenerTest``\ クラスを右クリックして、\ ``Run UrlShortenerTest``\ をクリックしてください。
258 |
259 | .. figure:: ./images/exercise01-02.png
260 | :width: 80%
261 |
262 | テストが成功したら課題1は完了です。
263 |
264 | .. figure:: ./images/exercise01-03.png
265 | :width: 80%
266 |
267 | テストは以下のようにも実行できます。
268 |
269 | .. code-block:: bash
270 |
271 | $ mvn test -f urlshortener/pom.xml
272 |
273 | 課題2 Redisを使ってConcurrentHashMap使用部分を書き換えましょう
274 | --------------------------------------------------------------------------------
275 | 次に\ ``ConcurrentHashMap``\ の部分をRedisを使用するように書き換えます。
276 | Spring BootによるAutoconfigurationでいかに簡単にRedis (Spring Data Redis)を使用できるか体験します。
277 |
278 |
279 | Redisの起動
280 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
281 |
282 | Macの場合
283 |
284 | .. code-block:: bash
285 |
286 | $ cd (ハンズオン資材のルートフォルダ)/software/redis-2.8.17
287 | $ ./src/redis-server
288 | [34286] 09 Nov 05:27:07.455 # Warning: no config file specified, using the default config. In order to specify a config file use ./src/redis-server /path/to/redis.conf
289 | [34286] 09 Nov 05:27:07.457 * Increased maximum number of open files to 10032 (it was originally set to 2560).
290 | _._
291 | _.-``__ ''-._
292 | _.-`` `. `_. ''-._ Redis 2.8.17 (00000000/0) 64 bit
293 | .-`` .-```. ```\/ _.,_ ''-._
294 | ( ' , .-` | `, ) Running in stand alone mode
295 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
296 | | `-._ `._ / _.-' | PID: 34286
297 | `-._ `-._ `-./ _.-' _.-'
298 | |`-._`-._ `-.__.-' _.-'_.-'|
299 | | `-._`-._ _.-'_.-' | http://redis.io
300 | `-._ `-._`-.__.-'_.-' _.-'
301 | |`-._`-._ `-.__.-' _.-'_.-'|
302 | | `-._`-._ _.-'_.-' |
303 | `-._ `-._`-.__.-'_.-' _.-'
304 | `-._ `-.__.-' _.-'
305 | `-._ _.-'
306 | `-.__.-'
307 |
308 | [34286] 09 Nov 05:27:07.465 # Server started, Redis version 2.8.17
309 | [34286] 09 Nov 05:27:07.466 * DB loaded from disk: 0.001 seconds
310 | [34286] 09 Nov 05:27:07.466 * The server is now ready to accept connections on port 6379
311 |
312 | Windowsの場合、redis-server.exeを実行してください。
313 |
314 |
315 | ソースコードの修正
316 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
317 |
318 | 以下の3カ所を書き換えてください。
319 |
320 | .. code-block:: java
321 |
322 | final ConcurrentHashMap urlMap = new ConcurrentHashMap<>();
323 | // ↓
324 | @Autowired StringRedisTemplate redisTemplate;
325 |
326 | に書き換えてください。
327 |
328 | .. code-block:: java
329 |
330 | urlMap.putIfAbsent(hash, url);
331 | // ↓
332 | redisTemplate.opsForValue().set(hash, url);
333 |
334 | に書き換えてください。
335 |
336 |
337 | .. code-block:: java
338 |
339 | String url = urlMap.get(hash);
340 | // ↓
341 | String url = redisTemplate.opsForValue().get(hash);
342 |
343 | に書き換えてください。
344 |
345 |
346 | 書き換えた後に、課題1同様にテストが通れば課題2も完了です。
347 |
348 | 起動したアプリケーションは終了しておいてください。Redisは起動したままにしてください。
349 |
350 | 演習2 Spring Cloud Configで動的コンフィギュレーション
351 | ================================================================================
352 | 演習2ではSpring Cloud Configを使った動的コンフィギュレーションを体験します。
353 |
354 |
355 | 演習2で扱うシステムのアーキテクチャ図を以下に示します。
356 |
357 | .. figure:: ./images/exercise02-01.png
358 | :width: 40%
359 |
360 | Config Clientとして演習1で作成した「URL短縮サービス」を使用し、Config Server(作成済み)から設定を取得します。
361 |
362 | Config ServerはデフォルトでGithubに接続しますが、今回はオフライン環境で実施するため、ローカルに立ち上げたGitbucketを使用します。
363 |
364 | 演習プロジェクトのインポート
365 | --------------------------------------------------------------------------------
366 | 「File」->「Import Module」で演習プロジェクトをインポートします。
367 | 「(ハンズオン資材のルートフォルダ)/exercise/02-distributed-config」を選択してください。
368 |
369 | .. figure:: ./images/import-exercise02-01.png
370 | :width: 80%
371 |
372 | .. figure:: ./images/import-exercise02-02.png
373 | :width: 80%
374 |
375 | * configserverはConfig Serverを設定したプロジェクトです。
376 | * urlshortenerは演習1にConfig Clientの依存関係を追加したプロジェクトです。
377 |
378 | どちらも既に設定済みで、新規にコーディングする必要はありません。
379 |
380 | Gitbucketの起動 & Config Repositoryの作成
381 | --------------------------------------------------------------------------------
382 |
383 | Gibucketを起動しましょう。8080番ポートを使用するので、このポートを使用しているアプリがあれば終了してください。
384 |
385 | .. code-block:: bash
386 |
387 | $ cd (ハンズオン資材のルートフォルダ)/software
388 | $ java -jar gitbucket.war
389 |
390 | http://localhost:8080\ にアクセスしユーザー名/パスワードともに「root」でログインしてください。
391 |
392 | .. figure:: ./images/exercise02-02.png
393 | :width: 80%
394 |
395 | 右上のメニューから「New repository」をクリックしてください。
396 |
397 | .. figure:: ./images/exercise02-03.png
398 | :width: 80%
399 |
400 | Repository nameに「config-repo」を入力し、「Initialize this repository with a README」にチェックを入れ、「Create repository」をクリックしてください。
401 |
402 | .. figure:: ./images/exercise02-04.png
403 | :width: 80%
404 |
405 | これでConfig Respositoryが作成できました。
406 |
407 | .. figure:: ./images/exercise02-05.png
408 | :width: 80%
409 |
410 | 動作確認用のコンフィギュレーションを作成しましょう。レポジトリ名の右に「+」マークをクリックしてください。
411 |
412 | .. figure:: ./images/exercise02-06.png
413 | :width: 80%
414 |
415 |
416 | ファイル名を「foo.properties」にし、以下の内容を記入し、「Commit changes」をクリックしてください。
417 |
418 | .. code-block:: properties
419 |
420 | foo: 123456
421 | bar: abcdef
422 |
423 | .. figure:: ./images/exercise02-07.png
424 | :width: 80%
425 |
426 | もう一つファイルを作成します。
427 | ファイル名を「foo-development.properties」にし、以下の内容を記入し、「Commit changes」をクリックしてください。
428 |
429 | .. code-block:: properties
430 |
431 | foo: Hello!
432 |
433 | .. figure:: ./images/exercise02-08.png
434 | :width: 80%
435 |
436 | Config Server起動
437 | --------------------------------------------------------------------------------
438 |
439 | 「configserver」の\ ``bootstrap.yml``\ に以下の設定が行われていることを確認してください。
440 |
441 | .. code-block:: yaml
442 |
443 | spring.cloud.config.server.uri: http://localhost:8080/git/root/config-repo.git
444 |
445 | 以下のコマンドでConfig Serverを起動してください。
446 |
447 | .. code-block:: bash
448 |
449 | $ cd (ハンズオン資材のルートフォルダ)/exercise/02-distributed-config
450 | $ mvn spring-boot:run -f configserver/pom.xml
451 |
452 | 動作確認しましょう。
453 |
454 | .. code-block:: bash
455 |
456 | $ curl http://localhost:8888/admin/env
457 |
458 | 以下ではjqを使って整形した結果を示します。
459 |
460 |
461 | .. code-block:: bash
462 |
463 | $ curl http://localhost:8888/admin/env | jq .
464 | {
465 | "defaultProperties": {
466 | "spring.config.name": "configserver"
467 | },
468 | "applicationConfig: [classpath:/bootstrap.yml]": {
469 | "spring.cloud.config.server.uri": "http://localhost:8080/git/root/config-repo.git"
470 | },
471 | "applicationConfig: [classpath:/configserver.yml]": {
472 | "management.context_path": "/admin",
473 | "spring.application.name": "configserver",
474 | "server.port": 8888,
475 | "info.component": "Config Server",
476 | "spring.jmx.default_domain": "cloud.config.server"
477 | },
478 | // ... 省略
479 | }
480 |
481 | \ ``spring.cloud.config.server.uri``\ が反映されていることを確認してください。
482 |
483 | 次にコンフィギュレーションを取得します。app名はfoo、profile名はdefaultにします。
484 |
485 | .. code-block:: bash
486 |
487 | $ curl http://localhost:8888/foo/default
488 |
489 | 以下ではjqを使って整形した結果を示します。
490 |
491 | .. code-block:: bash
492 |
493 | $ curl http://localhost:8888/foo/default | jq .
494 | {
495 | "propertySources": [
496 | {
497 | "source": {
498 | "foo": "123456",
499 | "bar": "abcdef"
500 | },
501 | "name": "http://localhost:8080/git/root/config-repo.git/foo.properties"
502 | }
503 | ],
504 | "label": "master",
505 | "name": "default"
506 | }
507 |
508 | 次にprofileを変更して取得しましょう。
509 |
510 | .. code-block:: bash
511 |
512 | $ curl http://localhost:8888/foo/development
513 |
514 |
515 | 以下ではjqを使って整形した結果を示します。
516 |
517 | .. code-block:: bash
518 |
519 | $ curl http://localhost:8888/foo/development | jq .
520 | {
521 | "propertySources": [
522 | {
523 | "source": {
524 | "foo": "Hello!"
525 | },
526 | "name": "http://localhost:8080/git/root/config-repo.git/foo-development.properties"
527 | },
528 | {
529 | "source": {
530 | "foo": "123456",
531 | "bar": "abcdef"
532 | },
533 | "name": "http://localhost:8080/git/root/config-repo.git/foo.properties"
534 | }
535 | ],
536 | "label": "master",
537 | "name": "development"
538 | }
539 |
540 | \ ``foo-development.properties``\ で上書きしていることが分かります。
541 |
542 |
543 | 「URL短縮サービス」向けのコンフィギュレーション作成
544 | --------------------------------------------------------------------------------
545 |
546 | 同様に、URL短縮サービス向けのコンフィギュレーションを「urlshortener.yml」に作成します。設定内容は以下の通りです。
547 |
548 | .. code-block:: yaml
549 |
550 | urlshorten:
551 | url: http://localhost:${server.port}
552 | spring:
553 | redis:
554 | host: localhost # server host
555 | password: # server password
556 | port: 6379 # connection port
557 | pool:
558 | max-idle: 8 # pool settings ...
559 | min-idle: 0
560 | max-active: 8
561 | max-wait: -1
562 | endpoints.restart:
563 | enabled: true
564 |
565 | .. figure:: ./images/exercise02-09.png
566 | :width: 80%
567 |
568 |
569 | 動作確認しましょう。(Config Serverの再起動は不要です)
570 |
571 | .. code-block:: bash
572 |
573 | $ curl http://localhost:8888/urlshortener/default
574 |
575 |
576 | 以下ではjqを使って整形した結果を示します。
577 |
578 | .. code-block:: bash
579 |
580 | $ curl http://localhost:8888/urlshortener/default | jq .
581 | {
582 | "propertySources": [
583 | {
584 | "source": {
585 | "spring.redis.pool.max-idle": 8,
586 | "spring.redis.password": "",
587 | "spring.redis.host": "localhost",
588 | "spring.redis.port": 6379,
589 | "urlshorten.url": "http://localhost:${server.port}",
590 | "endpoints.restart.enabled": true,
591 | "spring.redis.pool.max-active": 8,
592 | "spring.redis.pool.min-idle": 0,
593 | "spring.redis.pool.max-wait": -1
594 | },
595 | "name": "http://localhost:8080/git/root/config-repo.git/urlshortener.yml"
596 | }
597 | ],
598 | "label": "master",
599 | "name": "default"
600 | }
601 |
602 | Git上の変更が即反映されています。
603 |
604 |
605 | 「URL短縮サービス」(Config Client)の起動
606 | --------------------------------------------------------------------------------
607 |
608 | 次にConfig Clientとして、「URL短縮サービス」を起動します。
609 |
610 | インポートしたプロジェクト(exercise/02-distributed-config/urlshortener)のpom.xmlに以下の依存関係が追加されていることを確認してください。
611 |
612 | .. code-block:: xml
613 |
614 |
615 | org.springframework.cloud
616 | spring-cloud-starter
617 |
618 |
619 | org.springframework.boot
620 | spring-boot-starter-actuator
621 |
622 |
623 | また、urlshortenerのbootstrap.ymlに
624 |
625 | .. code-block:: yaml
626 |
627 | spring:
628 | application:
629 | name: urlshortener
630 |
631 | が設定されていることを確認してください。
632 |
633 | 「URL短縮サービス」を起動しましょう。8080番ポートは既に起動しているので、プログラム引数に\ ``--server.port=8081``\ をつけて8081番ポートで起動します。
634 |
635 | .. code-block:: bash
636 |
637 | $ cd (ハンズオン資材のルートフォルダ)/exercise/02-distributed-config
638 | $ mvn spring-boot:run -f urlshortener/pom.xml -Drun.arguments="--server.port=8081"
639 |
640 |
641 | 演習1同様に以下のリクエストを送ってください。(ポート名が変更されていることに気をつけてください)
642 |
643 | .. code-block:: bash
644 |
645 | $ curl -X POST http://localhost:8081 -d "url=http://google.com"
646 | http://localhost:8081/58f3ae21
647 | $ curl -X GET http://localhost:8081/58f3ae21
648 | http://google.com
649 |
650 | 次にConfig Server(urlshortener.yml)の値を変えましょう。
651 |
652 | http://localhost:8080/root/config-repo/blob/master/urlshortener.yml\ にアクセスし、「Edit」ボタンをクリックしてください。
653 |
654 | .. figure:: ./images/exercise02-10.png
655 | :width: 80%
656 |
657 | \ ``urlshorten.url``\ を\ ``http://localhost:9999``\ に変更して「Commit changes」をクリックしてください。(\ **この設定は演習3で使用します**\ )。
658 |
659 | .. figure:: ./images/exercise02-11.png
660 | :width: 80%
661 |
662 | 変更を反映する前に、Config Client上のプロパティを確認しましょう。
663 |
664 | .. code-block:: bash
665 |
666 | $ curl -X GET http://localhost:8081/env/urlshorten.url
667 | http://localhost:8081
668 |
669 |
670 | 次にConfig Clientをrefreshします。
671 |
672 | .. code-block:: bash
673 |
674 | $ curl -X POST http://localhost:8081/refresh
675 | ["urlshorten.url"]
676 | $ curl -X GET http://localhost:8081/env/urlshorten.url
677 | http://localhost:9999
678 |
679 | 変更が反映されました。しかし、以下の通りDI済みのプロパティに再DIはされていません。
680 |
681 | .. code-block:: bash
682 |
683 | $ curl -X POST http://localhost:8081 -d "url=http://google.com"
684 | http://localhost:8081/58f3ae21
685 |
686 | 今度はConfig Clientをrestartします。
687 |
688 | .. code-block:: bash
689 |
690 | $ curl -X POST http://localhost:8081/restart
691 | {"message":"Restarting"}
692 |
693 | restart後は、最新のプロパティで再DIされていることがわかります。
694 |
695 | .. code-block:: bash
696 |
697 | $ curl -X POST http://localhost:8081 -d "url=http://google.com"
698 | http://localhost:9999/58f3ae21
699 |
700 |
701 | 課題3 「URL短縮サービス」(Config Client)をRefreshスコープに変更
702 | --------------------------------------------------------------------------------
703 | 「URL短縮サービス(\ ``UrlShortener``\ クラス)」へのプロパティインジェクション反映をrefreshで行えるように、
704 | \ ``UrlShortener``\ クラスをRefreshスコープに変更してください。
705 |
706 | .. code-block:: java
707 |
708 | @EnableAutoConfiguration
709 | @ComponentScan
710 | @RestController
711 | @RefreshScope // ここを追加
712 | public class UrlShortener {
713 | // 略
714 | }
715 |
716 | \ ``mvn spring-boot:run``\ で起動した「URL短縮サービス」をCtrl+Cで終了して、再度実行してください。
717 |
718 |
719 | .. code-block:: bash
720 |
721 | $ mvn spring-boot:run -f urlshortener/pom.xml -Drun.arguments="--server.port=8081"
722 |
723 | 今回は以下のようにEnvエンドポイントにPOSTすることでコンフィギュレーションを変更しましょう。
724 |
725 |
726 | .. code-block:: bash
727 |
728 | $ curl -X POST http://localhost:8081/env -d urlshorten.url=http://127.0.0.1:9999
729 | {"urlshorten.url":"http://127.0.0.1:9999"}
730 |
731 | 再度、refreshを行い、もう一度「URL短縮サービス」へリクエストを送りましょう。
732 |
733 | .. code-block:: bash
734 |
735 | $ curl -X POST http://localhost:8081/refresh
736 | []
737 | $ curl -X POST http://localhost:8081 -d "url=http://google.com"
738 | http://127.0.0.1:9999/58f3ae21
739 |
740 | restartすることなく、アプリケーションにプロパティが反映されたことがわかります。
741 |
742 | Config Server、Config ClientともにCtrl+Cで終了してください。(Gitbucket, Redisは起動したままにしてください。)
743 |
744 | 演習3 Spring Cloud Netflixでマイクロサービスアーキテクチャ構築
745 | ================================================================================
746 | 演習3ではSpring Cloud Netflixを使った様々なパターンを体験します。
747 |
748 |
749 | 演習3で扱うシステムのアーキテクチャ図を以下に示します。
750 |
751 | .. figure:: ./images/exercise03-01.png
752 | :width: 80%
753 |
754 | 演習プロジェクトのインポート
755 | --------------------------------------------------------------------------------
756 | 「File」->「Import Module」で演習プロジェクトをインポートします。
757 | 「(ハンズオン資材のルートフォルダ)/exercise/03-netflix」を選択してください。
758 |
759 | .. figure:: ./images/import-exercise03-01.png
760 | :width: 80%
761 |
762 | .. figure:: ./images/import-exercise03-02.png
763 | :width: 80%
764 |
765 | * configserverはConfig Serverを設定したプロジェクトです。演習2と同じです。
766 | * eureka-serverはService DiscoveryであるEurekaを起動するプロジェクトです。ダッシュボードも提供します。
767 | * hystrix-dashboardはHystrixのダッシュボードを提供するプロジェクトです。
768 | * urlshortenerは演習2にConfig Clientの依存関係を追加したプロジェクトです。
769 | * urlshortener-uiは「URL短縮サービス」の画面です。\ ``RestClient``\ とClient LoadbalancerのRibboを使ってurlshortenerにアクセスします。
770 |
771 | どれも既に設定済みで、新規にコーディングする必要はありません。上から順番に起動します。
772 |
773 | 演習2で起動したGitbucketが必要ですので、終了してしまった場合は再び実行してください。
774 |
775 |
776 |
777 | Config Serverの起動
778 | --------------------------------------------------------------------------------
779 |
780 | .. figure:: ./images/system-exercise03-01.png
781 | :width: 80%
782 |
783 | 演習2同様に、以下のコマンドでConfig Serverを起動してください。
784 |
785 | .. code-block:: bash
786 |
787 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix
788 | $ mvn spring-boot:run -f configserver/pom.xml
789 |
790 |
791 | Service Discovery (Eureka Server)の起動
792 | --------------------------------------------------------------------------------
793 |
794 | .. figure:: ./images/system-exercise03-02.png
795 | :width: 80%
796 |
797 | 以下のコマンドでEureka Serverを起動してください。
798 |
799 | .. code-block:: bash
800 |
801 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix
802 | $ mvn spring-boot:run -f eureka-server/pom.xml
803 |
804 | http://localhost:8761/\ でEureka Serverのダッシュボードにアクセスできます。
805 |
806 |
807 | .. figure:: ./images/exercise03-02.png
808 | :width: 80%
809 |
810 | 現時点ではEureka Serverに登録されているインスタンスはありません。
811 |
812 | Circuit Breaker Monitor (Hystrix Dashboard)の起動
813 | --------------------------------------------------------------------------------
814 |
815 | .. figure:: ./images/system-exercise03-03.png
816 | :width: 80%
817 |
818 | 以下のコマンドでHystrix Dashboardを起動してください。
819 |
820 | .. code-block:: bash
821 |
822 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix
823 | $ mvn spring-boot:run -f hystrix-dashboard/pom.xml
824 |
825 | 起動後、30秒経ったら\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。
826 |
827 | .. figure:: ./images/exercise03-03.png
828 | :width: 80%
829 |
830 | Hystrix DashboardがEurekaに登録されたことが分かります(アーキテクチャ図に記されていませんが、Circuit Breaker MonitorからService Discoveryへの線相当です)。
831 |
832 | ではHystrix Dashboardにアクセスしましょう。http://localhost:7979\ にアクセスしてください。
833 |
834 | .. figure:: ./images/exercise03-04.png
835 | :width: 80%
836 |
837 | 中央の入力フォームにはHystrixを利用したサービスの情報を取得するためのevent streamのURLを指定することで、
838 | そのサービスをモニタリングすることができます。
839 |
840 | まだHystrixを利用したサービスがないため、ここではデモ用のMock Streamを使用します。http://localhost:7979/mock.stream\ を入力して、「Monitor Stream」をクリックしてください。
841 |
842 |
843 | .. figure:: ./images/exercise03-05.png
844 | :width: 80%
845 |
846 | Hystrixのイベントをモニタリングできます。
847 |
848 | .. figure:: ./images/exercise03-06.png
849 | :width: 80%
850 |
851 | 後ほど「URL短縮サービス」のevent streamをモニタリングします。
852 |
853 |
854 | 「URL短縮サービス」の起動
855 | --------------------------------------------------------------------------------
856 |
857 |
858 | .. figure:: ./images/system-exercise03-04.png
859 | :width: 80%
860 |
861 | 次に演習1から使い続けている「URL短縮サービス」を起動します。
862 |
863 | 後ほどこの「URL短縮サービス」を3台起動します。Eurekaに別hostnameとして認識させるため、あらかじめ/etc/hostsに以下の設定を追加しておきます。
864 |
865 | .. code-block:: bash
866 |
867 | 127.0.0.1 urlshortener1 urlshortener2 urlshortener3
868 |
869 | 尚、演習2のurlshortenに対して、以下の変更を加えています。
870 |
871 | \ ``UrlShortener``\ クラスがEurekaのクライアントになるために\ ``@EnableEurekaClient``\ を追加しています。
872 |
873 | .. code-block:: java
874 |
875 | @EnableAutoConfiguration
876 | @ComponentScan
877 | @RestController
878 | @RefreshScope
879 | @EnableEurekaClient // 追加
880 | public class UrlShortener {
881 | // 略
882 | }
883 |
884 | application.ymlにEurekaに関する情報を追加しています。
885 |
886 | .. code-block:: yaml
887 |
888 | eureka:
889 | client:
890 | serviceUrl:
891 | defaultZone: http://localhost:8761/eureka/
892 | instance:
893 | hostname: ${APPLICATION_DOMAIN:127.0.0.1}
894 | nonSecurePort: ${server.port}
895 |
896 |
897 | それでは「URL短縮サービス」を起動しましょう。portとEurekaに登録するhostnameを指定します。
898 |
899 | .. code-block:: bash
900 |
901 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix
902 | $ mvn spring-boot:run -f urlshortener/pom.xml \
903 | -Drun.arguments="--server.port=8081,--eureka.instance.hostname=urlshortener1"
904 |
905 | 起動後、30秒経ったら\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。
906 |
907 | .. figure:: ./images/exercise03-07.png
908 | :width: 80%
909 |
910 | urlshortenerがEurekaに登録されたことが分かります。
911 |
912 |
913 | 「URL短縮サービス」UIの起動
914 | --------------------------------------------------------------------------------
915 | 最後のサービスとして「URL短縮サービス」UIを起動します。
916 |
917 | .. figure:: ./images/system-exercise03-05.png
918 | :width: 80%
919 |
920 | 起動する前にUI用のコンフィギュレーションを作成します。
921 |
922 | \ `Config Repository `_\ にアクセスして、urlshortener-ui.ymlを作成し、以下の内容を記述してください。
923 |
924 |
925 | .. code-block:: yaml
926 |
927 | urlshorten.api.url: http://urlshortener
928 | endpoints.restart:
929 | enabled: true
930 |
931 |
932 | .. figure:: ./images/exercise03-08.png
933 | :width: 80%
934 |
935 |
936 | UIを9999番ポートで起動します。
937 |
938 | .. code-block:: bash
939 |
940 | $ mvn spring-boot:run -f urlshortener-ui/pom.xml -Drun.arguments="--server.port=9999"
941 |
942 |
943 | 起動後、30秒経ったら\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。
944 |
945 | .. figure:: ./images/exercise03-09.png
946 | :width: 80%
947 |
948 | urlshortener-uiがEurekaに登録されたことが分かります。
949 |
950 | それでは\ http://localhost:9999\ にアクセスしましょう。
951 |
952 | .. figure:: ./images/exercise03-10.png
953 | :width: 80%
954 |
955 | url入力フォームに「http://google.com」を入力して、送信ボタンをクリックしましょう。
956 |
957 | .. figure:: ./images/exercise03-11.png
958 | :width: 80%
959 |
960 | バックエンドの「URL短縮サービス」が呼ばれて短縮URLが表示されます。
961 |
962 | .. figure:: ./images/exercise03-12.png
963 | :width: 80%
964 |
965 | 表示されたURLをクリックすると\ http://google.com\ へリダイレクトされます。
966 |
967 | urlshorten-uiではHystrix + Ribbonを使用して、urlshortenのサービスをcallしています。
968 |
969 | Hystrixのevent streamは\ http://localhost:9999/hystrix.stream\ でアクセスできます。
970 |
971 |
972 | .. figure:: ./images/exercise03-13.png
973 | :width: 80%
974 |
975 | \ `Hystrix Dashboard `_\ に\ http://localhost:9999/hystrix.stream\ を入力してモニタリングしてみましょう。
976 |
977 |
978 | .. figure:: ./images/exercise03-14.png
979 | :width: 80%
980 |
981 | UIからサービスを呼び出すとモニタリング画面に反映されます。
982 |
983 | .. figure:: ./images/exercise03-15.png
984 | :width: 80%
985 |
986 | 「URL短縮サービス」のスケールアウト
987 | --------------------------------------------------------------------------------
988 |
989 | .. figure:: ./images/system-exercise03-06.png
990 | :width: 80%
991 |
992 | 最後に「URL短縮サービス」をあと2インスタンス起動し、Ribbonによるロードバランシングを体験しましょう。
993 |
994 | 早速、「URL短縮サービス」を起動しましょう。
995 |
996 | インスタンス2はport: 8082,hostname: urlshortener2で起動します。
997 |
998 | .. code-block:: bash
999 |
1000 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix
1001 | $ mvn spring-boot:run -f urlshortener/pom.xml \
1002 | -Drun.arguments="--server.port=8082,--eureka.instance.hostname=urlshortener2"
1003 |
1004 | インスタンス3はport: 8083,hostname: urlshortener3で起動します。
1005 |
1006 | .. code-block:: bash
1007 |
1008 | $ cd (ハンズオン資材のルートフォルダ)/exercise/03-netflix
1009 | $ mvn spring-boot:run -f urlshortener/pom.xml \
1010 | -Drun.arguments="--server.port=8083,--eureka.instance.hostname=urlshortener3"
1011 |
1012 |
1013 | ノード2、ノード3起動後30秒経ったら、\ `Eureka Serverのダッシュボード `_\ にアクセスしてください。
1014 |
1015 | .. figure:: ./images/exercise03-16.png
1016 | :width: 80%
1017 |
1018 | urlshortenerサービスに対して3つのインスタンスが登録されました。
1019 |
1020 |
1021 | UIではRibbonを利用することで、特定のインスタンスにアクセスしているわけではなく、\ ``http://urlshortener``\ というようにサービス名に対してアクセスしており、
1022 | ラウンドロビンのロードバランシングが行われます。
1023 |
1024 | いまの作りだと、どのインスタンスでURL短縮が行われているか分からないので、以下のような設定変更を行いましょう。
1025 |
1026 | .. code-block:: bash
1027 |
1028 | curl -X POST http://localhost:8081/env -d "urlshorten.url=http://localhost:\${server.port}"
1029 | curl -X POST http://localhost:8081/refresh
1030 | curl -X POST http://localhost:8082/env -d "urlshorten.url=http://localhost:\${server.port}"
1031 | curl -X POST http://localhost:8082/refresh
1032 | curl -X POST http://localhost:8083/env -d "urlshorten.url=http://localhost:\${server.port}"
1033 | curl -X POST http://localhost:8083/refresh
1034 |
1035 | これでUIから何度もリクエストを送ると、各インスタンスが順番に使用されていることが分かります。
1036 |
1037 | .. figure:: ./images/exercise03-17.png
1038 | :width: 80%
1039 |
1040 | どれかのインスタンスを落としたり、復旧させたりして何が起こるか試してみてください。
1041 |
1042 | .. note::
1043 |
1044 | 単位時間辺りのエラー発生回数がしきい値を超えるとCiruitがOpen状態になり、一定時間ずっとエラーを返すようになります。
1045 |
1046 |
1047 | まとめ
1048 | ================================================================================
1049 |
1050 | 本演習を通じて以下の内容を学びました。
1051 |
1052 | * 演習1ではSpring Bootを使って簡単にマイクロサービスを作成する方法を学びました。また数行でRedisに対応する方法も学びました。
1053 | * 演習2ではSpring Cloud Configを使って動的コンフィギュレーションの行い方を学びました。
1054 | * 演習3ではSpring Cloud Netflixを使ってマイクロサービスアーキテクチャにおけるいくつかのパターンを実現しました。
1055 |
1056 | さらなる学習には\ `Spring CloudのReference `_\ を参照してください。
1057 |
--------------------------------------------------------------------------------