diff --git a/02-customer/pom.xml b/02-customer/pom.xml
index 7e44fb8..c7adb41 100644
--- a/02-customer/pom.xml
+++ b/02-customer/pom.xml
@@ -12,6 +12,18 @@
02-customer
+
+ org.springframework.cloud
+ spring-cloud-starter-zipkin
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bus-amqp
+
org.springframework.cloud
spring-cloud-starter-stream-rabbit
@@ -20,7 +32,10 @@
org.springframework.cloud
spring-cloud-starter-netflix-hystrix-dashboard
-
+
+ org.springframework.cloud
+ spring-cloud-config-client
+
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
diff --git a/02-customer/src/main/java/cn/zyjblogs/controller/CustomerController.java b/02-customer/src/main/java/cn/zyjblogs/controller/CustomerController.java
index be5c4fc..06632f5 100644
--- a/02-customer/src/main/java/cn/zyjblogs/controller/CustomerController.java
+++ b/02-customer/src/main/java/cn/zyjblogs/controller/CustomerController.java
@@ -10,11 +10,19 @@ import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
@RestController
+@RefreshScope
public class CustomerController {
+
+ @Value("${env}")
+ private String env;
+
+
+
@Value("${version}")
private String version;
@@ -32,6 +40,14 @@ public class CustomerController {
@Autowired
private OtherServiceClient otherServiceClient;
+
+
+ @GetMapping("/env")
+ public String env() {
+ return env;
+ }
+
+
@GetMapping("/version")
public String version() throws InterruptedException {
Thread.sleep(3000);
diff --git a/02-customer/src/main/resources/application.yml b/02-customer/src/main/resources/application.yml
deleted file mode 100644
index af70386..0000000
--- a/02-customer/src/main/resources/application.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-server:
- port: 8080
-#指定Eureka服务地址
-eureka:
- client:
- service-url:
- defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
- #每隔多久去更新一下本地的注册表缓存信息
- registry-fetch-interval-seconds: 30
- instance:
- #心跳间隔
- lease-renewal-interval-in-seconds: 30
- #多久没法送,就认为你宕机了
- lease-expiration-duration-in-seconds: 90
-
-#指定具体服务的负载均衡策略
-SEARCH: #编写服务名称
- ribbon:
-# NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
- NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
-
-#feign和hystrix主件整合
-feign:
- hystrix:
- enabled: true
-
-version: v1
-#指定服务名称
-spring:
- application:
- name: CUSTOMER-${version}
- rabbitmq:
- port: 5672
- username: test
- password: test
- virtual-host: /test
-hystrix:
- dashboard:
- proxy-stream-allow-list: localhost
\ No newline at end of file
diff --git a/02-customer/src/main/resources/bootstrap.yml b/02-customer/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..1fcdd67
--- /dev/null
+++ b/02-customer/src/main/resources/bootstrap.yml
@@ -0,0 +1,40 @@
+#指定Eureka服务地址
+eureka:
+ client:
+ service-url:
+ defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
+
+
+
+
+version: v1
+#指定服务名称
+spring:
+ application:
+ name: CUSTOMER-${version}
+ cloud:
+ config:
+ discovery:
+ enabled: true
+ service-id: CONFIG
+ profile: dev
+ sleuth:
+ sampler:
+ probability: 1 #百分之多少的sleuth信息需要输出到zipkin
+ zipkin:
+ base-url: http://127.0.0.1:9411/ #指定zipkin的地址
+ sender:
+ type: rabbit
+
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "*"
+
+#CONFIG - CUSTOMER-v1-dev.yml
+
+logging:
+ level:
+ org.springframework.web.servlet.DispatcherServlet: DEBUG
\ No newline at end of file
diff --git a/03-search/pom.xml b/03-search/pom.xml
index cde1b87..225ecfd 100644
--- a/03-search/pom.xml
+++ b/03-search/pom.xml
@@ -11,6 +11,10 @@
03-search
+
+ org.springframework.cloud
+ spring-cloud-starter-zipkin
+
org.projectlombok
lombok
diff --git a/03-search/src/main/resources/application.yml b/03-search/src/main/resources/application.yml
index 5041b70..cd88be9 100644
--- a/03-search/src/main/resources/application.yml
+++ b/03-search/src/main/resources/application.yml
@@ -26,4 +26,13 @@ spring:
myMessage:
consumer:
acknowledgeMode: MANUAL
+ sleuth:
+ sampler:
+ probability: 1 #百分之多少的sleuth信息需要输出到zipkin
+ zipkin:
+ base-url: http://127.0.0.1:9411/ #指定zipkin的地址
+
+logging:
+ level:
+ org.springframework.web.servlet.DispatcherServlet: DEBUG
\ No newline at end of file
diff --git a/07-config/pom.xml b/07-config/pom.xml
index 516bf49..f5d0695 100644
--- a/07-config/pom.xml
+++ b/07-config/pom.xml
@@ -11,10 +11,27 @@
07-config
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bus-amqp
+
org.springframework.boot
spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-config-server
+
\ No newline at end of file
diff --git a/07-config/src/main/java/cn/zyjblogs/ConfigApplication.java b/07-config/src/main/java/cn/zyjblogs/ConfigApplication.java
index ba3b305..b6e1e2c 100644
--- a/07-config/src/main/java/cn/zyjblogs/ConfigApplication.java
+++ b/07-config/src/main/java/cn/zyjblogs/ConfigApplication.java
@@ -2,8 +2,14 @@ package cn.zyjblogs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.cloud.config.server.EnableConfigServer;
+
+import javax.servlet.annotation.WebFilter;
@SpringBootApplication
+@EnableConfigServer
+@ServletComponentScan("cn.zyjblogs.filter")
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class,args);
diff --git a/07-config/src/main/java/cn/zyjblogs/filter/UrlFilter.java b/07-config/src/main/java/cn/zyjblogs/filter/UrlFilter.java
new file mode 100644
index 0000000..7a5b8ca
--- /dev/null
+++ b/07-config/src/main/java/cn/zyjblogs/filter/UrlFilter.java
@@ -0,0 +1,62 @@
+package cn.zyjblogs.filter;
+
+
+import javax.servlet.*;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletRequest;
+
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+@WebFilter("/*")
+public class UrlFilter implements Filter {
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
+ String url=httpServletRequest.getRequestURI();
+ System.out.println(url);
+ if(!url.endsWith("/actuator/bus-refresh")){
+ filterChain.doFilter(servletRequest,servletResponse);
+ return;
+ }
+ String body=(httpServletRequest).toString();
+ System.out.println("original body: "+ body);
+ RequestWrapper requestWrapper=new RequestWrapper(httpServletRequest);
+ filterChain.doFilter(requestWrapper,servletResponse);
+ }
+ private class RequestWrapper extends HttpServletRequestWrapper {
+ public RequestWrapper(HttpServletRequest request) {
+ super(request);
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ byte[] bytes = new byte[0];
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
+ ServletInputStream servletInputStream = new ServletInputStream() {
+ @Override
+ public int read() throws IOException {
+ return byteArrayInputStream.read();
+ }
+
+ @Override
+ public boolean isFinished() {
+ return byteArrayInputStream.read() == -1 ? true : false;
+ }
+
+ @Override
+ public boolean isReady() {
+ return false;
+ }
+
+ @Override
+ public void setReadListener(ReadListener listener) {
+
+ }
+ };
+ return servletInputStream;
+ }
+ }
+}
diff --git a/07-config/src/main/resources/application.yml b/07-config/src/main/resources/application.yml
index 04dd7c2..70ef0ad 100644
--- a/07-config/src/main/resources/application.yml
+++ b/07-config/src/main/resources/application.yml
@@ -1,5 +1,5 @@
server:
- port: 81
+ port: 10000
eureka:
client:
@@ -9,8 +9,23 @@ eureka:
#指定服务名称
spring:
application:
- name: OTHER-SERVICE #other-service
-
-# 指定代理的第三方服务
-sidecar:
- port: 7001
+ name: CONFIG #other-service
+ cloud:
+ config:
+ server:
+ git:
+ basedir: E:\config # 本地仓库地址
+ username: xxxxx@xxxxx.com #远程仓库的用户名
+ password: xxxxx #远程仓库的密码
+ uri: https://gitee.com/zyjblog/config-resp.git
+ rabbitmq:
+ virtual-host: /test
+ host: localhost
+ username: test
+ password: test
+ port: 5672
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "*"
diff --git a/README.en.md b/README.en.md
deleted file mode 100644
index a2aeb41..0000000
--- a/README.en.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# springcloud
-
-#### Description
-{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Instructions
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Contribution
-
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
-
-
-#### Gitee Feature
-
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
diff --git a/README.md b/README.md
index 2930838..fc07f8d 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,2031 @@
-# springcloud
+# 微服务框架
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+[toc]
-#### 软件架构
-软件架构说明
+## 一、SpringCloud介绍
+
+### 1.1 微服务架构
+
+> https://martinfowler.com/articles/microservices.html
+>
+> 微服务架构提出者:马丁福勒
+
+> 简而言之,微服务体系结构[样式 [1\]](https://martinfowler.com/articles/microservices.html#footnote-etymology)是一种将单个应用程序开发为一组小型服务的方法,每个应用程序在自己的进程中运行,并与轻量级机制(通常是 HTTP 资源 API)通信。这些服务围绕业务功能构建,可通过全自动部署机制独立部署。这些服务的集中管理最少,可能以不同的编程语言编写,并使用不同的数据存储技术。
+
+### 1.2 SpringCloud介绍
+
+> SpringCloud是微服务架构落地的一套技术栈
+>
+> SpringCloud中的大多数技术都是基于Netflix公司的技术进行第二次开发。
+>
+> 1、SpringCloud的中文社区网站:http://springcloud.cn/
+>
+> 2、SpringCloud的中文网 :http://springcloud.cc/
+>
+> 八个技术点:
+>
+> 1、Eureka - 服务的注册与发现
+>
+> 2、Robbn - 服务之间的负载均衡
+>
+> 3、Feign - 服务之间的通讯
+>
+> 4、Hystrix - 服务的线程隔离及其熔断器
+>
+> 5、Zuul - 服务网关
+>
+> 6、Stream - 实现MQ的使用
+>
+> 7、Config - 动态配置
+>
+> 8、 Sleuth - 服务追踪
+
+## 二、服务的注册与发现-Eureka
+
+### 2.1 引言
+
+> Eureka就是帮助我们维护所有服务的信息,以便服务之间的相互调用
+
+![1605019839175](images/1605019839175.png)
+
+### 2.2 Eureka的快速入门
+
+#### 2.2.1 创建EurekaServer
+
+> 1、创建一个父工程,并且在父工程中指定SpringCloud版本,并且将packaing修改为pom
+
+```xml
+pom
+
+
+ 1.8
+ Hoxton.SR8
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring.cloud-version}
+ pom
+ import
+
+
+
+```
+
+> 2、创建eureka的server,创建springboot工程,并且导入依赖,再启动类中添`@EnableEurekaServer`注解和编写yml文件
+
+> 2.1、导入依赖
+
+```xml
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-server
+
+
+```
+
+> 2.2、启动类添加注解
+
+```java
+@EnableEurekaServer
+@SpringBootApplication
+public class EurekaApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(EurekaApplication.class,args);
+ }
+}
+```
+
+> 2.3 编写yml配置文件
+
+```yml
+server:
+ port: 8761
+
+eureka:
+ instance:
+ hostname: localhost
+ client:
+ #当前的eureka是单机版的
+ registerWithEureka: false
+ fetchRegistry: false
+ serviceUrl:
+ defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
+```
-#### 安装教程
-1. xxxx
-2. xxxx
-3. xxxx
+#### 2.2.2 创建EurekaClient
-#### 使用说明
+> 1、创建Maven工程,修改为SpringBoot
-1. xxxx
-2. xxxx
-3. xxxx
+> 2、导入依赖
-#### 参与贡献
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+```
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
+> 3、在启动类上添加注解`@EnableEurekaClient`
+
+```java
+@EnableEurekaClient
+@SpringBootApplication
+public class CustomerApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(CustomerApplication.class,args);
+ }
+}
+```
+
+> 4、编写配置文件
+
+```yml
+#指定Eureka服务地址
+eureka:
+ client:
+ service-url:
+ defaultZone: http://localhost:8761/eureka
+
+#指定服务名称
+spring:
+ application:
+ name: CUSTOMER
+```
+
+![image-20201111095518705](images/image-20201111095518705.png)
+
+#### 2.2.3 测试Eureka
+
+> 1、创建了一个Search搜索模块,并且注册到Eureka
+
+> 2、使用EurekaClient的对象获取服务信息
+
+```java
+@Autowired
+ private EurekaClient eurekaClient;
+```
+
+> 3、创建RestTemplate
+
+```java
+@Configuration
+public class RestTemplateConfig {
+
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+}
+```
+
+> 4、使用RestTemplate调用
+
+```java
+@GetMapping("/customer")
+public String customer() {
+ //1. 通过eurekaClient 获取到SEARCH服务的信息
+ InstanceInfo info = eurekaClient.getNextServerFromEureka("SEARCH", false);
+ //2. 获取到访问的地址
+ String url = info.getHomePageUrl();
+ System.out.println(url);
+ //3. 通过restTemplate访问
+ String result = restTemplate.getForObject(url + "/search", String.class);
+ //4. 返回
+ return result;
+}
+```
+
+### 2.3 Eureka的安全性
+
+> 实现Eureka认证
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+```
+
+> 2、编写配置类
+
+```java
+@EnableWebSecurity
+class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ //忽略掉/eureka/**路径
+ http.csrf().ignoringAntMatchers("/eureka/**");
+ super.configure(http);
+ }
+}
+```
+
+> 3、编写配置文件,配置用户密码
+
+```yml
+#指定用户名密码
+spring:
+ security:
+ user:
+ name: root
+ password: root
+```
+
+> 4、其他服务想注册到Eureka上需要添加用户名密码
+
+```yml
+#指定Eureka服务地址
+eureka:
+ client:
+ service-url:
+ defaultZone: http://用户名:密码@localhost:8761/eureka
+```
+
+### 2.4 Eureka的高可用性
+
+> 如果程序正在运行,突然Eureka宕机了
+>
+> 1、如果调用方访问过一次被调用方,Eureka的宕机就不会影响到功能
+>
+> 2、如果调用方没有访问过被调用方,Eureka的宕机就会造成当前功能的不可用到功能
+
+> 搭建Eureka高可用
+
+![image-20201111110217411](images/image-20201111110217411.png)
+
+> 1、准备多态Eureka
+>
+> 采用了复制的方式,删除iml和target文件,并且修改pom.xml中的项目名称,再给负公差添加module
+
+> 2、让服务注册到多台Eureka上
+
+```yml
+server:
+ port: 8761
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://root:root@localhost:8762/eureka/
+```
+
+```yml
+server:
+ port: 8762
+
+eureka:
+ client:
+ serviceUrl:
+ defaultZone: http://root:root@localhost:8761/eureka/
+
+```
+
+> 3、让多台Eureka之间相互通讯
+
+```yml
+eureka:
+ client:
+ #当前的eureka是单机版的 false单机版 true集群
+ registerWithEureka: true
+ fetchRegistry: true
+ serviceUrl:
+ defaultZone: http://root:root@localhost:8761/eureka/
+```
+
+### 2.5 Eureka的细节
+
+>1、EurekaClient启动时,讲自己的信息注册到EurekaServer上,EurekaServer就会储存EurekaClient的注册信息。
+
+> 2、当EurekaClient调用服务时,本地没有注册信息的缓存时,去EurekaServer中获取注册信息
+
+> 3、EurekaClient会通过心跳的方式去和EurekaServer进行连接。(默认30s EurekaClient就会发送一次心跳请求,如果超过了90s还没有发送心跳信息的话,EurekaSevrer就认为你宕机了,将当前的EurekaClient从注册表中移除)
+
+```yml
+eureka:
+ instance:
+ lease-renewal-interval-in-seconds: 30 #心跳间隔
+ lease-expiration-duration-in-seconds: 90 #多久没法送,就认为你宕机了
+```
+
+> 4、EurekaClient会每个30s去EurekaServer中去更新本地的注册表
+
+```yml
+eureka:
+ client:
+ #每隔多久去更新一下本地的注册表缓存信息
+ registry-fetch-interval-seconds: 30
+```
+
+> 5、Eureka的自我保护机制,统计15分钟内,如果一个服务的心跳发送比例低于85%,EurekaServer就会开启自我保护机制
+>
+> 1、不会从EurekaServer中去移除长时间没有收到心跳的服务
+>
+> 2、EurekaServer还是可以正常提供服务的
+>
+> 3、网络稳定时,EurekaServer才会开始将自己的信息被其他节点同步过去
+
+![image-20201111132523595](images/image-20201111132523595.png)
+
+```yml
+eureka:
+ # Eureka保护机制配置
+ server:
+ #true 开启 false关闭
+ enable-self-preservation: true
+```
+
+> 6、CAP定理,C-一致性 A-可用性 P-分区容错性,这三个特新在分布是环境下,只能满足2个,而且分区容错性在分布式环境下,时必须要满足的。只能在AC之间进行权衡。
+>
+> 1、如果选择CP,保证了一致性,可能会造成你系统在一定时间内是不可以的,如果你同步数据的时间比较长,造成的损失就越大。
+>
+> 2、如果选择AP的效果,高可用的集群,Eureka集群是无中心,Eureka即便宕机几个也不会影响系统的使用,不需要重新去枚举一个master,也会导致一定时间内数据是不一致。
+
+## 三、服务间的负载均衡-Robbin
+
+### 3.1 引言
+
+> Robbin是帮助我们实现服务和服务负载均衡
+>
+> 客户端负载均衡:customer客户端模块,将2个Search模块信息全部拉取到本地的缓存,在customer中自己做一个负载均衡的策略,选中某一个服务。
+>
+> 服务端负载均衡:在注册中心中,直接根据你指定的负载均衡策略,帮你选中一个指定的服务器信息,并返回。
+
+![image-20201111134022333](images/image-20201111134022333.png)
+
+### 3.2 Robbin的快速入门
+
+> 1、启动两个Search模块
+
+> 2、在customer导入robbin依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-ribbon
+
+```
+
+> 3、配置整合RestTemplate和Robbin
+
+```java
+@Configuration
+public class RestTemplateConfig {
+
+ @Bean
+ @LoadBalanced
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+}
+```
+
+> 4、在customer中去访问Search
+
+```java
+@GetMapping("/customer")
+public String customer() {
+ String result = restTemplate.getForObject("http://SEARCH/search", String.class);
+ //4. 返回
+ return result;
+}
+```
+
+### 3.3 Robbin配置负载均衡策略
+
+> 1、负载均衡策略
+>
+> 1. RandomRule:随机策略
+> 2. RoundRobbinRule:轮询策略
+> 3. WeightedResponseTimeRule:默认会采用轮询的策略,后续会根据服务的响应时间,自动给你分配权重
+> 4. BestAvailableRule:根据被调用方并发数最小的去分配
+
+> 2、采用注解的形式
+
+```java
+@Bean
+public IRule robbinRule() {
+ return new RandomRule();
+}
+```
+
+> 3、配置文件去指定负载均衡的策略(推荐)
+
+```yml
+#指定具体服务的负载均衡策略
+SEARCH: #编写服务名称
+ ribbon:
+ NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #具体负载均衡使用的类
+```
+
+## 四、服务间的调用-Feign
+
+### 4.1 引言
+
+> Feign可以帮助我们实现面向接口编程,就直接调用其他服务,简化开发。
+
+### 4.2 Feign的快速入门
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+```
+
+> 2、添加一个注解`@EnableFeignClients`
+
+```java
+@EnableEurekaClient
+@SpringBootApplication
+@EnableFeignClients
+public class CustomerApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(CustomerApplication.class,args);
+ }
+}
+```
+
+> 3、创建一个接口,并且和search模块做映射
+
+```java
+//指定服务名称
+@FeignClient("SEARCH")
+public interface SearchClient {
+
+ //value -> 目标服务的请求路径,method -> 映射请求方式
+ @RequestMapping(value = "/search",method = RequestMethod.GET)
+ public String search();
+}
+```
+
+> 4、测试使用
+
+```java
+@Autowired
+private SearchClient searchClient;
+
+@GetMapping("/customer")
+public String customer() {
+ String result = searchClient.search();
+ return result;
+}
+```
+
+### 4.3 Feign的传递参数方式
+
+> 1、注意事项
+>
+> 1. 如果你传递的参数,比较复杂时,默认会采用POST的请求方式。
+> 2. 传递单个参数时,推荐使用@PathVariable,如果传递的单个参数比较多,这里也可以采用@RequestParam,不要省略value属性
+> 3. 传递对象信息时,统一采用json的方式,添加@RequestBody
+> 4. Client接口必须采用@RequestMapping
+
+> 2、在Search模块下准备三个接口
+
+```java
+@GetMapping("/search/{id}")
+public Customer findById(@PathVariable Integer id) {
+ return new Customer(1, "zhangsan", 23);
+}
+
+@GetMapping("/getCustomer")
+public Customer getcustomer(@RequestParam Integer id, @RequestParam String name) {
+ return new Customer(id, name, 23);
+}
+
+@PostMapping("save")
+public Customer save(@RequestBody Customer customer) {
+ return customer;
+}
+```
+
+> 3、封装Customer模块的Controller
+
+```java
+@GetMapping("/customer/{id}")
+public Customer findById(@PathVariable Integer id) {
+ return searchClient.findById(id);
+}
+
+@GetMapping("/getCustomer")
+public Customer getcustomer(@RequestParam Integer id, @RequestParam String name) {
+ return searchClient.getcustomer(id,name);
+}
+
+@GetMapping("save")
+public Customer save(Customer customer) {
+ return searchClient.save(customer);
+}
+```
+
+> 4、再封装Client接口
+
+```java
+@RequestMapping(value = "/search/{id}",method = RequestMethod.GET)
+Customer findById(@PathVariable(value = "id") Integer id);
+
+@RequestMapping(value = "/getCustomer",method = RequestMethod.GET)
+Customer getcustomer(@RequestParam(value = "id") Integer id, @RequestParam(value = "name") String name);
+
+@RequestMapping(value = "save",method = RequestMethod.GET) //会自动转化成POST请求 405
+Customer save(@RequestBody Customer customer);
+```
+
+> 5、测试
+
+![image-20201111155409030](images/image-20201111155409030.png)
+
+### 4.4 Feign的Fallback
+
+> Fallback可以帮助我们在使用Feign去调用另一个服务时,如果出现了问题,走服务降级,返回一个错误的数据,避免功能因为一个服务出现问题,全部失效
+
+> 1、创建一个POJO类,实现Client接口
+
+```java
+@Component
+public class SearchClientFallBack implements SearchClient {
+ @Override
+ public String search() {
+ return "出现问题了";
+ }
+
+ @Override
+ public Customer findById(Integer id) {
+ return null;
+ }
+
+ @Override
+ public Customer getcustomer(Integer id, String name) {
+ return null;
+ }
+
+ @Override
+ public Customer save(Customer customer) {
+ return null;
+ }
+}
+```
+
+> 2、修改Client接口中的注解,添加一个属性
+
+```java
+@FeignClient(value = "SEARCH",fallback = SearchClientFallBack.class)
+```
+
+> 3、添加一个配置文件。
+
+```yml
+#feign和hystrix主件整合
+feign:
+ hystrix:
+ enabled: true
+```
+
+> 调用方无法知道具体的错误信息是什么,通过FallBackFactory的方式去实现这个功能
+
+> 1、FallBackFactory基于fallback
+
+> 2、创建一个POJO类,实现FallBackFactory
+
+```java
+@Component
+public class SearchClientFallBackFactory implements FallbackFactory {
+
+ @Autowired
+ private SearchClientFallBack searchClientFallBack;
+ @Override
+ public SearchClient create(Throwable throwable) {
+ throwable.printStackTrace();
+ return searchClientFallBack;
+ }
+}
+```
+
+> 3、修改Client接口中的属性
+
+```java
+@FeignClient(value = "SEARCH",
+ fallbackFactory = SearchClientFallBackFactory.class
+)
+```
+
+## 五、服务的隔离及熔断器-Hystrix
+
+### 5.1 引言
+
+![image-20201111162630381](images/image-20201111162630381.png)
+
+### 5.2 降级机制实现
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-hystrix
+
+```
+
+> 2、添加一个注解`@EnableCircuitBreaker`
+
+```java
+@EnableEurekaClient
+@SpringBootApplication
+@EnableCircuitBreaker
+public class CustomerApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(CustomerApplication.class,args);
+ }
+}
+```
+
+> 3、针对某一个接口去编写他的降级方法
+
+```java
+@GetMapping("/customer/{id}")
+@HystrixCommand(fallbackMethod ="findByIdFallBack" )
+public Customer findById(@PathVariable Integer id) {
+ int i = 1/0;
+ return searchClient.findById(id);
+}
+
+//findById的降级方法,方法描述要与接口一致
+public Customer findByIdFallBack(Integer id) {
+ return new Customer(-1,"",0);
+}
+```
+
+> 4、在接口上添加注解
+
+```java
+@HystrixCommand(fallbackMethod ="findByIdFallBack" )
+```
+
+> 5、测试
+
+![image-20201111164239591](images/image-20201111164239591.png)
+
+### 5.3 线程隔离
+
+> 如果使用Tomcat的线程池去接收用户的请求,使用当前线程去执行其他服务的功能,如果某一个服务出现了故障,导致tomcat的线程大量的堆积,导致tomcat无法处理其他业务功能。
+>
+> 1、Hystrix线程池(默认),接收用户请求采用tomcat的线程池,执行业务代码,调用其他服务时,采用Hystrix的线程池。
+>
+> 2、信号量,使用的还是Tomcat的线程池,帮助我们取关了Tomcat的线程池
+
+> 1、Hystrix的线程池的配置(具体的配置属性需要去查看HystrixCommandProperties类) [wiki](https://github.com/Netflix/Hystrix/wiki/Configuration)
+>
+> 1. 线程隔离策略:name = `hystrix.command.default.execution.isolation.strategy`,value=`THREAD`,`SEMAPHORE `
+> 2. 指定超时时间(针对线程池):name= `hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds` ,value =`1000`
+> 3. 是否开启超时时间配置:name=`hystrix.command.default.execution.timeout.enabled`,value=`true`
+> 4. 超时之后是否中断线程:name=`hystrix.command.default.execution.isolation.thread.interruptOnTimeout`,value=`true`
+> 5. 取消任务之后是否中断线程:name=`hystrix.command.default.execution.isolation.thread.interruptOnCancel`,value=false
+
+> 2、信号量配置信息
+>
+> 1. 线程隔离策略:name = `hystrix.command.default.execution.isolation.strategy`,value=`THREAD`,`SEMAPHORE `
+> 2. 指定信号量的最大并发请求数:name=`hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests`,value=`10`
+
+### 5.4 断路器
+
+#### 5.4.1 断路器介绍
+
+> 如果调用指定服务时,如果说这个服务的失败率达到你输入的阈值麻将断路器从closed状态,转变为open状态,指定服务时无法被访问的,如果你访问就直接走fallback方法,在一定时间内,open状态会再次转变为half open状态,允许一个请求发送到我指定服务,如果成功,则转变为closed,如果失败,服务再次转变为open状态,会再次循环到hald open,直到专路器回到一个closed状态。
+
+![image-20201111181650929](images/image-20201111181650929.png)
+
+#### 5.4.2 配置断路器的监控界面
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-hystrix-dashboard
+
+```
+
+> 2、在启动类中添加注解`@EnableHystrixDashboard`
+
+```java
+@EnableEurekaClient
+@SpringBootApplication
+@EnableFeignClients
+@EnableCircuitBreaker
+@EnableHystrixDashboard
+@ServletComponentScan("cn.zyjblogs.servlet")
+public class CustomerApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(CustomerApplication.class,args);
+ }
+}
+```
+
+> 3、配置一个Servlet路径,指定上Hystrix的Servlet
+
+```java
+@WebServlet("/hystrix.stream")
+public class HystrixServlet extends HystrixMetricsStreamServlet {
+}
+```
+
+> 4、在启动类上添加扫描Servlet的注解`@ServletComponentScan("cn.zyjblogs.servlet")`
+
+> 5、配置文件
+
+```yml
+hystrix:
+ dashboard:
+ proxy-stream-allow-list: localhost
+```
+
+> 6、测试
+
+直接访问: http://host:port/hystrix.stream
+
+![1605103421256](images/1605103421256.png)
+
+> 在当前位置输入映射好的servlet路径
+
+![1605103436874](images/1605103436874.png)
+
+#### 5.4.3 配置断路器的属性
+
+> 断路器的属性(10秒之内)
+>
+> 1. 断路器的开关:name=` hystrix.command.default.circuitBreaker.enabled `,value=`true`
+> 2. 失败阈值的总请求数:name=` hystrix.command.default.circuitBreaker.requestVolumeThreshold `,value=`20`
+> 3. 请求总数失败率达到%多少时打开断路器:name=` hystrix.command.default.circuitBreaker.errorThresholdPercentage `,value=`50`
+> 4. 断路器open状态后,多少秒是拒绝请求的:name=` hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds `value=`5000`
+> 5. 强制让服务拒绝请求:name=` hystrix.command.default.circuitBreaker.forceOpen `,value=`false`
+> 6. 强制让服务接收请求:name=` hystrix.command.default.circuitBreaker.forceClosed `,value=`false`
+
+> 具体配置方式
+
+```java
+@GetMapping("/customer/{id}")
+@HystrixCommand(fallbackMethod ="findByIdFallBack",commandProperties = {
+ @HystrixProperty(name = "circuitBreaker.enabled",value="true"),
+ @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value="10"),
+ @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value="70"),
+ @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value="5000")
+})
+```
+
+### 5.5 请求缓存
+
+#### 5.5.1 请求缓存的介绍
+
+> 1、请求缓存的声明周期是一次请求
+>
+> 2、请求缓存是缓存当前线程中的一个方法,将方法参数作为key,方法的返回结果作为value
+>
+> 3、在一次请求中,目标方法被调用过一次以后就都会被缓存
+
+![1605104987931](images/1605104987931.png)
+
+#### 5.5.2 请求缓存的实现
+
+> 1、创建一个Service,在Service中调用Search服务
+
+```java
+@Service
+public class CustomerService {
+
+ @Autowired
+ private SearchClient searchClient;
+
+ @CacheResult
+ @HystrixCommand(commandKey = "findById")
+ public Customer findById(@CacheKey Integer id) throws InterruptedException {
+ return searchClient.findById(id);
+ }
+
+ @CacheRemove(commandKey = "findById")
+ @HystrixCommand
+ public void clearFindById(@CacheKey Integer id) {
+ System.out.println("findById缓存被清空");
+ }
+
+}
+```
+
+> 2、使用请求缓存的注解`@CacheResult` `CacheRemove`
+>
+> 1. @CacheResult:帮助我们缓存当前方法的返回结果(必须配合@HystrixCommand使用)
+> 2. @CacheRemove:帮助我们清除某一个缓存信息(基于commandKey)
+> 3. @CacheKey:指定那个方法参数作为缓存标识
+
+> 3、修改Search模块的结果返回值
+
+```java
+ return new Customer(id, name, (int) (Math.random() * 100000));
+```
+
+> 4、编写Filter,去构建HystrixRequestContext
+
+```java
+@WebFilter("/*")
+public class HystrixRequestContextFilter implements Filter {
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HystrixRequestContext.initializeContext();
+ filterChain.doFilter(servletRequest,servletResponse);
+ }
+}
+```
+
+> 5、修改Controller
+
+```java
+@Autowired
+private CustomerService customerService;
+
+ @GetMapping("/customer/{id}")
+ @HystrixCommand(
+ fallbackMethod = "findByIdFallBack",
+ commandProperties = {
+ @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
+ @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
+ @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "70"),
+ @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
+ })
+ public Customer findById(@PathVariable Integer id) throws InterruptedException {
+ System.out.println(Thread.currentThread().getName());
+ if (id == 1) {
+ int i = 1 / 0;
+ }
+ //一下为添加内容
+ System.out.println(customerService.findById(id));
+ System.out.println(customerService.findById(id));
+ customerService.clearFindById(id);
+ System.out.println(customerService.findById(id));
+ System.out.println(customerService.findById(id));
+ customerService.clearFindById(id);
+
+ return searchClient.findById(id);
+ }
+```
+
+> 6、测试
+
+![1605106759891](images/1605106759891.png)
+
+## 六、服务的网关-Zuul
+
+### 6.1 引言
+
+> 1、客户端维护大量的ip和port信息,直接访问指定服务
+>
+> 2、认证和授权操作,需要在每一个模块中添加认证和授权操作
+>
+> 3、项目迭代,服务拆分,服务要合并,需要客户端镜像大量的变化
+>
+> 4、统一的把安全性校验都放在Zuul中
+
+![网关](images/image-20201112110708902.png)
+
+### 6.2 Zuul的快速入门
+
+> 1、创建Maven项目,修改SpringBoot
+
+> 2、导入依赖
+
+```xml
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-zuul
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+```
+
+> 3、添加注解`@EnableZuulProxy` `@EnableEurekaClient`
+
+```java
+@SpringBootApplication
+@EnableEurekaClient
+@EnableZuulProxy
+public class ZuulApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(ZuulApplication.class,args);
+ }
+}
+```
+
+> 4、编写配置文件
+
+```yml
+server:
+ port: 80
+#指定Eureka服务地址
+eureka:
+ client:
+ service-url:
+ defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
+
+#指定服务名称
+spring:
+ application:
+ name: ZUUL
+```
+
+> 5、测试
+
+![image-20201112113609523](images/image-20201112113609523.png)
-#### 特技
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+### 6.3 Zuul常用配置信息
+
+#### 6.3.1 Zuul的监控界面
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+```
+
+> 2、编写配置文件
+
+```yml
+#查看zuul的监考界面(开发时,配置为*,上线,不要配置)
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "*"
+```
+
+> 3、直接访问:`http://localhost/actuator/routes`
+
+![image-20201112132217601](images/image-20201112132217601.png)
+
+#### 6.3.2 忽略服务设置
+
+```yml
+# zuul的配置
+zuul:
+ #基于服务名忽略服务,无法查看,如果要忽略全部的服务,"*",默认配置的全部路径都会被忽略掉(自定义服务配置,通过这种方式是无法忽略的)
+ ignored-services: eureka
+ #监考界面依然可以查看,在访问的时候,404无法访问
+ ignored-patterns: /**/search/**
+```
+
+#### 6.3.3 自定义服务配置
+
+```yml
+# zuul的配置
+zuul:
+ #基于服务名忽略服务,无法查看,如果要忽略全部的服务,"*",默认配置的全部路径都会被忽略掉(自定义服务配置,通过这种方式是无法忽略的)
+ ignored-services: "*"
+ #监考界面依然可以查看,在访问的时候,404无法访问
+ ignored-patterns: /**/search/**
+ # 指定自定义服务(方式一,key(服务名):value(路径))
+# routes:
+# search: /ss/**
+# customer: /cc/**
+ # 指定自定义服务(方式二)
+ routes:
+ kehu: #自定义名称
+ path: /cc/** # 映射路径
+ serviceId: customer
+```
+
+#### 6.3.4 灰度发布
+
+> 1、添加一个配置类
+
+```java
+@Configuration
+public class ZuulConfig {
+ @Bean
+ public PatternServiceRouteMapper serviceRouteMapper() {
+ return new PatternServiceRouteMapper(
+ "(?^.+)-(?v.+$)",
+ "${version}/${name}");
+ //服务名-v版本
+ // /v版本/路径
+ }
+}
+```
+
+> 2、准备一个服务,提供2个版本
+
+```yml
+version: v1
+
+#指定服务名称
+spring:
+ application:
+ name: CUSTOMER-${version}
+```
+
+
+
+> 3、修改Zuul的配置
+
+```yml
+zuul:
+ #基于服务名忽略服务,无法查看,如果需要用到-v的方式,一定要忽略掉
+# ignored-services: "*"
+```
+
+> 4、修改CustomerController
+
+```java
+@Value("${version}")
+ private String version;
+
+@GetMapping("/version")
+ public String version() {
+ return version;
+ }
+```
+
+
+
+> 5、测试
+
+![image-20201112135410803](images/image-20201112135410803.png)
+
+![image-20201112135429409](images/image-20201112135429409.png)
+
+### 6.4 Zuul的过滤器执行流程
+
+> 客户端请求发送到Zuul服务商,首先通过PreFilter,如果正常放行,会把请求再次转发给RoutingFilter,请求转发到一个指定的服务,在指定的服务响应一个结果之后,再次走一个PostFilter的过滤器链,最终将响应信息返回给客户端。
+
+![image-20201112140312697](images/image-20201112140312697.png)
+
+### 6.5 Zuul过滤器入门
+
+> 1、创建POJO类,继承ZuulFilter
+
+```java
+@Component
+public class ZuulFilterTest extends ZuulFilter {
+
+}
+
+```
+
+> 2、指定当前过滤器的类型
+
+```java
+@Override
+public String filterType() {
+ return FilterConstants.PRE_TYPE;
+}
+```
+
+> 3、指定过滤器的执行顺序
+
+```java
+@Override
+public int filterOrder() {
+ return FilterConstants.PRE_DECORATION_FILTER_ORDER -1;
+}
+```
+
+> 4、配置是否启用
+
+```java
+@Override
+public boolean shouldFilter() {
+ //开启当前过滤器
+ return true;
+}
+```
+
+> 5、指定过滤器中的具体业务代码
+
+```java
+@Override
+public Object run() throws ZuulException {
+ System.out.println("prefix过滤器已经执行~~~");
+ return null;
+}
+```
+
+> 6、测试
+
+![image-20201112141523371](images/image-20201112141523371.png)
+
+### 6.6 PreFilter实现token校验
+
+> 1、准备访问路径,请求参数专递token
+
+`http://localhost/v1/customer/version?token=123`
+
+> 2、创建AuthenticationFilter
+
+```java
+@Component
+public class AuthenticationFilter extends ZuulFilter{
+ @Override
+ public String filterType() {
+ return FilterConstants.PRE_TYPE;
+ }
+
+ @Override
+ public int filterOrder() {
+ return FilterConstants.PRE_DECORATION_FILTER_ORDER - 2;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ return true;
+ }
+
+ @Override
+ public Object run() throws ZuulException {
+ //...
+ return null;
+ }
+}
+```
+
+> 3、在run方法中编写具体的业务逻辑代码
+
+```java
+@Override
+ public Object run() throws ZuulException {
+ System.out.println("AuthenticationFilter执行了");
+ //1. 获取Request对象
+ RequestContext requestContext = RequestContext.getCurrentContext();
+ HttpServletRequest request = requestContext.getRequest();
+ //2. 获取token参数
+ String token = request.getParameter("token");
+ //3.对比token
+ if (token == null || !"123".equalsIgnoreCase(token)) {
+ System.out.println("token校验失败");
+ //4. token校验失败,直接响应数据
+ requestContext.setSendZuulResponse(false);
+ requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
+ }
+ return null;
+ }
+```
+
+> 4、测试
+
+![image-20201112145449105](images/image-20201112145449105.png)
+
+### 6.7 Zuul的降级
+
+> 1、创建POJO类,实现接口FallbackProvider
+
+```java
+@Component
+public class ZuulFallBack implements FallbackProvider {
+
+}
+
+```
+
+> 2、重写两个方法
+
+```java
+@Override
+public String getRoute() {
+ //代表指定全部出现问题的服务,都走这个降级方法
+ return "*";
+}
+
+@Override
+public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
+ System.out.println("降级的服务"+route);
+ cause.printStackTrace();
+ return new ClientHttpResponse() {
+ @Override
+ public HttpHeaders getHeaders() {
+ //指定响应头信息
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ return headers;
+ }
+
+ @Override
+ public InputStream getBody() throws IOException {
+ //给用户响应的信息
+ String msg = "当前服务" + route + "出现问题!!!";
+ return new ByteArrayInputStream(msg.getBytes());
+ }
+
+ @Override
+ public HttpStatus getStatusCode() throws IOException {
+ //指定具体的HttpStatus
+ return HttpStatus.INTERNAL_SERVER_ERROR;
+ }
+
+ @Override
+ public int getRawStatusCode() throws IOException {
+ //返回状态码
+ return HttpStatus.INTERNAL_SERVER_ERROR.value();
+ }
+
+ @Override
+ public String getStatusText() throws IOException {
+ //指定错误信息
+ return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
+ }
+
+ @Override
+ public void close() {
+
+ }
+ };
+}
+```
+
+> 3、测试
+
+![image-20201112151632111](images/image-20201112151632111.png)
+
+### 6.8 Zuul动态路由
+
+> 1、创建一个过滤器
+
+```java
+@Component
+public class DynamicRoutingFilter extends ZuulFilter{
+
+}
+```
+
+> 2、在run方法中编辑业务逻辑
+
+```java
+@Override
+public String filterType() {
+ return FilterConstants.PRE_TYPE;
+}
+
+@Override
+public int filterOrder() {
+ return FilterConstants.PRE_DECORATION_FILTER_ORDER +3;
+}
+
+@Override
+public boolean shouldFilter() {
+ return true;
+}
+
+@Override
+public Object run() throws ZuulException {
+ System.out.println("DynamicRoutingFilter执行了");
+ //1、获取Request对象
+ RequestContext context = RequestContext.getCurrentContext();
+ HttpServletRequest request = context.getRequest();
+
+ //2、获取参数,redisKey
+ String redisKey = request.getParameter("redisKey");
+
+ //3、直接判断
+ if (redisKey != null && redisKey.equalsIgnoreCase("customer")) {
+ System.out.println("DynamicRoutingFilter执行了路由到了/customer");
+ //http://localhost:8080/customer
+ context.put(FilterConstants.SERVICE_ID_KEY,"customer-v1");
+ context.put(FilterConstants.REQUEST_URI_KEY,"/customer");
+ } else if (redisKey != null && redisKey.equalsIgnoreCase("search")) {
+ System.out.println("DynamicRoutingFilter执行了路由到了/search/1");
+ //http://localhost:8081/search
+ context.put(FilterConstants.SERVICE_ID_KEY,"search");
+ context.put(FilterConstants.REQUEST_URI_KEY,"/search/1");
+ }
+ return null;
+}
+```
+
+> 3、测试
+
+![image-20201112155707148](images/image-20201112155707148.png)
+
+![image-20201112155652269](images/image-20201112155652269.png)
+
+## 七、多语言支持-Sidecar
+
+### 7.1 引言
+
+> 在SpringCloud的项目中,需要接入一些非java程序,第三方接口,无法接入eureka,hystrix,feign等组件。启动一个代理的微服务,代理微服务去和非java的程序或第三方接口交流,通过代理的非服务去计入SpringCloud的相关组件。
+
+![image-20201112160258956](images/image-20201112160258956.png)
+
+### 7.2 Sidecar实现
+
+> 1、创建一个第三方的服务
+
+`创建一个SpringBoot工程,并且添加一个Controller`
+
+> 2、创建Maven工程,修改为SpringBoot
+
+> 2、导入以来
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-netflix-sidecar
+
+```
+
+> 4、添加注解`@EnableSidecar`
+
+> 5、编写配置文件
+
+```yml
+server:
+ port: 81
+
+eureka:
+ client:
+ service-url:
+ defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
+
+#指定服务名称
+spring:
+ application:
+ name: OTHER-SERVICE #other-service
+
+# 指定代理的第三方服务
+sidecar:
+ port: 7001
+```
+
+> 6、通过customer通过Feign的方式调用第三方服务
+
+![image-20201112165325023](images/image-20201112165325023.png)
+
+## 八、服务间消息传递-Stream
+
+### 8.1 引言
+
+> 用于构建消息驱动微服务的框架(在下面方便起见也叫它Stream框架),该框架在Spring Boot的基础上整合了Spring Integration来连接消息代理中间件(RabbitMQ,Kafka等)。它支持多个消息中间件的自定义配置,同时吸收了这些消息中间件的部分概念,例如持久化订阅、消费者分组,和分区等概念。使用Stream框架,我们不必关系如何连接各个消息代理中间件,也不必关系消息的发送与接收,只需要进行简单的配置就可以实现这些功能了,可以让我们更敏捷的进行开发主体业务逻辑了。
+>
+> **Spring Cloud Stream框架的组成部分:**
+>
+> 1. Stream框架自己的应用模型;
+> 2. 绑定器抽象层,可以与消息代理中间件进行绑定,通过绑定器的API,可实现插件式的绑定器。
+> 3. 持久化订阅的支持。
+> 4. 消费者组的支持。
+> 5. Topic分区的支持。
+
+![image-20201112165641574](images/20190325232054468.png)
+
+### 8.2 Stream快速入门
+
+> 1、启动RabbitMQ
+
+> 2、消费者-导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-stream-rabbit
+
+```
+
+> 3、消费者-配置文件
+
+```yml
+spring:
+ rabbitmq:
+ port: 5672
+ username: test
+ password: test
+ virtual-host: /test
+```
+
+> 4、消费者-监听的队列
+
+```java
+public interface StreamClient {
+ @Input("myMessage")
+ SubscribableChannel input();
+}
+```
+
+```java
+@Component
+@EnableBinding(StreamClient.class)
+public class StreamReceiver {
+ @StreamListener("myMessage")
+ public void msg(Object msg) {
+ System.out.println("接收到消息" + msg);
+ }
+}
+```
+
+> 5、启动类添加注解`@EnableBinding(StreamClient.class)`
+
+> 6、生产者-导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-stream-rabbit
+
+```
+
+> 7、生产者-配置文件
+
+```yml
+spring:
+ rabbitmq:
+ port: 5672
+ username: test
+ password: test
+ virtual-host: /test
+```
+
+> 8、生产者-发布消息
+
+```java
+public interface StreamClient {
+ @Output("myMessage")
+ MessageChannel output();
+}
+```
+
+```java
+@RestController
+public class MessageController {
+ @Autowired
+ private StreamClient streamClient;
+ @GetMapping("/send")
+ public String send() {
+ streamClient.output().send(MessageBuilder.withPayload("Hello Stream").build());
+ return "消息发送成功!!";
+ }
+}
+```
+
+> 9、启动类添加注解`@EnableBinding(StreamClient.class)`
+
+> 10、测试访问`http://localhost:8080/send`
+
+![image-20201113093057704](images/image-20201113093057704.png)
+
+![image-20201112173342142](images/image-20201112173342142.png)
+
+### 8.3 Stream重复消费问题
+
+> 只需要添加一个配置,指定消费者组
+
+```yml
+spring:
+ cloud:
+ stream:
+ binders:
+ myMessage: #队列名称
+ group: customer #消费者组
+```
+
+### 8.4 Stream的消费者手动ack
+
+> 1、编写配置
+
+```yml
+#指定服务名称
+spring:
+ cloud:
+ stream:
+ #实现手动ack
+ rabbit:
+ bindings:
+ myMessage:
+ consumer:
+ acknowledgeMode: MANUAL
+```
+
+> 2、修改消费端方法
+
+```java
+@Component
+@EnableBinding(StreamClient.class)
+public class StreamReceiver {
+ @StreamListener("myMessage")
+ public void msg(Object msg,
+ @Header(name = AmqpHeaders.CHANNEL) Channel channel,
+ @Header(name = AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws IOException {
+ System.out.println("接收到消息" + msg);
+ channel.basicAck(deliveryTag,false);
+ }
+}
+```
+
+## 九、服务的动态配置-Config
+
+### 9.1 引言
+
+> 1、配置文件分散在不同项目中的,不方便去维护。
+>
+> 2、配置文件的安全问题。
+>
+> 3、修改配置文件,无法立即生效。
+
+![image-20201112180552221](images/image-20201112180552221.png)
+
+### 9.2 搭建Config-Server
+
+> 1、创建Maven工程,修改SpringBoot
+
+> 2、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+ org.springframework.cloud
+ spring-cloud-config-server
+
+```
+
+> 3、添加注解`@EnableConfigServer`
+
+```java
+@SpringBootApplication
+@EnableConfigServer
+public class ConfigApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(ConfigApplication.class,args);
+ }
+}
+```
+
+> 4、编写配置文件(git)
+
+```yml
+spring:
+ cloud:
+ config:
+ server:
+ git:
+ basedir: E:\config # 本地仓库地址
+ username: xxxxxx@xxxx.com #远程仓库的用户名
+ password: xxxxxxxx #远程仓库的密码
+ uri: https://gitee.com/zyjblog/config-resp.git
+```
+
+> 5、测试(例子:`http://localhost:82/master/customer-xxx.yml ` (master可以省略))
+>
+> 访问方式如下:
+>
+> `/{application}/{profile}[/{label}]`
+> `/{application}-{profile}.yml`
+> `/{label}/{application}-{profile}.yml`
+>
+> `/{application}-{profile}.properties`
+> `/{label}/{application}-{profile}.properties`
+
+![image-20201113094055276](images/image-20201113094055276.png)
+
+### 9.3、修改Customer连接Config
+
+> 1、导入依赖
+
+```yml
+
+ org.springframework.cloud
+ spring-cloud-config-client
+
+```
+
+> 2、修改配置文件
+
+```java
+#指定Eureka服务地址
+eureka:
+ client:
+ service-url:
+ defaultZone: http://root:root@localhost:8761/eureka,http://root:root@localhost:8762/eureka
+
+version: v1
+#指定服务名称
+spring:
+ application:
+ name: CUSTOMER-${version}
+ cloud:
+ config:
+ discovery:
+ enabled: true
+ service-id: CONFIG
+ profile: dev
+```
+
+> 3、修改配置文件名称`application.yml`改为`bootstrap.yml`
+
+> 4、测试测试发布消息到RabbMQ
+
+![image-20201113102316294](images/image-20201113102316294.png)
+
+### 9.4 实现动态配置
+
+#### 9.4.1 实现原理
+
+![image-20201113103152115](images/image-20201113103152115.png)
+
+#### 9.4.2 服务连接RabbitMQ
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-bus-amqp
+
+```
+
+> 2、编写配置文件
+
+```yml
+spring:
+ rabbitmq:
+ virtual-host: /test
+ host: localhost
+ username: test
+ password: test
+ port: 5672
+```
+
+> 3、测试
+
+![image-20201113104134210](images/image-20201113104134210.png)
+
+#### 9.4.3 实现手动刷新
+
+> 1、导入依赖(两个服务config和customer均添加)
+
+```xml
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+```
+
+> 2、编写配置文件(两个服务config和customer均添加)
+
+```yml
+management:
+ endpoints:
+ web:
+ exposure:
+ include: "*"
+```
+
+> 3、为customer添加一个controller,添加注解`@RefreshScope`
+
+```java
+@RestController
+@RefreshScope
+public class CustomerController {
+
+ @Value("${env}")
+ private String env;
+
+ @GetMapping("/env")
+ public String env() {
+ return env;
+ }
+}
+```
+
+> 4、测试
+>
+> 1. CONFIG在gitee修改之后,自动拉取最新的配置信息。
+> 2. 其他模块需要更新的话,手动发送一个POST请求:http://localhost:10000/actuator/bus-refresh ,不重启项目,即可获取最新的配置信息
+
+#### 9.4.4 内网穿透
+
+> 1、内网穿透官网:http://www.ngrok.cc/
+
+> 2、注册登录
+
+> 3、购买免费隧道,并配置
+
+![image-20201113113814310](images/image-20201113113814310.png)
+
+> 4、下载客户端,并复制隧道id,点击运行客户端,复制到客户端中
+
+![image-20201113114050201](images/image-20201113114050201.png)
+
+> 5、测试访问是否成功
+
+![image-20201113114131931](images/image-20201113114131931.png)
+
+#### 9.4.5 实现自动刷新配置
+
+> 1、配置Gitee中的WebHooks
+
+![image-20201113120733998](images/image-20201113120733998.png)
+
+> 2、给Config添加一个过滤器UrlFilter
+
+```java
+
+@WebFilter("/*")
+public class UrlFilter implements Filter {
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
+ String url=httpServletRequest.getRequestURI();
+ System.out.println(url);
+ if(!url.endsWith("/actuator/bus-refresh")){
+ filterChain.doFilter(servletRequest,servletResponse);
+ return;
+ }
+ String body=(httpServletRequest).toString();
+ System.out.println("original body: "+ body);
+ RequestWrapper requestWrapper=new RequestWrapper(httpServletRequest);
+ filterChain.doFilter(requestWrapper,servletResponse);
+ }
+ private class RequestWrapper extends HttpServletRequestWrapper {
+ public RequestWrapper(HttpServletRequest request) {
+ super(request);
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ byte[] bytes = new byte[0];
+ ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
+ ServletInputStream servletInputStream = new ServletInputStream() {
+ @Override
+ public int read() throws IOException {
+ return byteArrayInputStream.read();
+ }
+
+ @Override
+ public boolean isFinished() {
+ return byteArrayInputStream.read() == -1 ? true : false;
+ }
+
+ @Override
+ public boolean isReady() {
+ return false;
+ }
+
+ @Override
+ public void setReadListener(ReadListener listener) {
+
+ }
+ };
+ return servletInputStream;
+ }
+ }
+}
+```
+
+> 3、添加注解`@ServletComponentScan("cn.zyjblogs.filter")`
+
+```java
+@SpringBootApplication
+@EnableConfigServer
+@ServletComponentScan("cn.zyjblogs.filter")
+public class ConfigApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(ConfigApplication.class,args);
+ }
+}
+```
+
+> 3、测试
+
+![image-20201113121035781](images/image-20201113121035781.png)
+
+## 十、服务的追踪-Sleuth
+
+### 10.1 引言
+
+> 在整个微服务架构中,微服务很多,一个请求可能需要调用很多很多的服务,最终才能完成一个功能,如果说,整个功能出现了问题,在这么多的服务中,如何区定位到问题的所在点,出现问题的原因是什么。
+>
+> 1、Sleuth可以获取得到整个服务链路的信息
+>
+> 2、Zipkin通过图形化界面去看到信息。
+>
+> 3、Sleuth将日志信息存储到数据库中
+
+![image-20201113131325996](images/image-20201113131325996.png)
+
+### 10.2 Sleuth使用
+
+> 1、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-sleuth
+
+```
+
+> 2、编写配置文件
+
+```yml
+logging:
+ level:
+ org.springframework.web.servlet.DispatcherServlet: DEBUG
+```
+
+> 3、测试
+>
+> SEARCH:服务名称
+>
+> 012 总链路id
+>
+> b0e:当前服务的链路id
+>
+> false:不会将当前的日志信息,输出到其他系统中
+
+![image-20201113133658258](images/image-20201113133658258.png)
+
+![image-20201113133713872](images/image-20201113133713872.png)
+
+![image-20201113133745985](images/image-20201113133745985.png)
+
+### 10.3 Zipkin的使用
+
+> 1、搭建Zipkin的web工程 https://zipkin.io/pages/quickstart
+>
+> 1. docker安装Zipkin
+
+1、使用docker pull拉取
+
+```bash
+docker pull openzipkin/zipkin
+```
+
+2、使用docker-compose
+
+```yml
+version: "3.1"
+services:
+ zipkin:
+ image: daocloud.io/daocloud/zipkin:latest
+ restart: always
+ container_name: zipkin
+ ports:
+ - 9411:9411
+```
+
+```bash
+docker-compose up -d #启动
+docker-compose down #关闭
+```
+
+> 2、导入依赖
+
+```xml
+
+ org.springframework.cloud
+ spring-cloud-starter-zipkin
+
+```
+
+> 3、编写配置文件
+
+```yml
+spring:
+ sleuth:
+ sampler:
+ probability: 1 #百分之多少的sleuth信息需要输出到zipkin
+ zipkin:
+ base-url: http://127.0.0.1:9411/ #指定zipkin的地址
+```
+
+> 4、测试
+
+![image-20201113153443738](images/image-20201113153443738.png)
+
+### 10.4 整合RabbitMQ
+
+> 1、导入RabbitMQ依赖(zipkin中已经依赖了RabbitMQ了)
+
+> 2、修改配置文件
+
+```yml
+spring:
+ zipkin:
+ sender:
+ type: rabbit
+```
+
+
+
+> 3、修改Zipkin信息
+
+```yml
+version: "3.1"
+services:
+ zipkin:
+ image: daocloud.io/daocloud/zipkin:latest
+ #image: docker.io/openzipkin/zipkin:latest
+ restart: always
+ container_name: zipkin
+ ports:
+ - 9411:9411
+ environment:
+ - RABBIT_ADDRESSES=10.27.10.123:5672 #本地ipv4地址:端口
+ - RABBIT_USER=test
+ - RABBIT_PASSWORD=test
+ - RABBIT_VIRTUAL_HOST=/test
+```
+
+> 3、测试
+
+![image-20201113160658535](images/image-20201113160658535.png)
+
+![image-20201113160724897](images/image-20201113160724897.png)
+
+### 10.5 Zipkin存储数据到ES
+
+> 1、重新修改zipkin的文件yml文件
+
+```yml
+version: "3.1"
+services:
+ zipkin:
+ image: daocloud.io/daocloud/zipkin:latest
+ #image: docker.io/openzipkin/zipkin:latest
+ restart: always
+ container_name: zipkin
+ ports:
+ - 9411:9411
+ environment:
+ - RABBIT_ADDRESSES=10.27.10.123:5672
+ - RABBIT_USER=test
+ - RABBIT_PASSWORD=test
+ - RABBIT_VIRTUAL_HOST=/test
+ - STORAGE_TYPE=elasticsearch
+ - ES_HOSTS=http://10.27.10.123:9200
+```
+
+> 2、安装Es
+
+```bash
+ #拉取镜像
+ docker pull elasticsearch
+ #启动参数
+ docker run --name es1_6.6.0 \
+-p 9200:9200 \
+-p 9300:9300 \
+-e ES_JAVA_OPTS="-Xms256m -Xmx256m" \
+-v /d/elasticsearch/config/es1.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
+-v /d/elasticsearch/data/es1:/usr/share/elasticsearch/data \
+-v /d/elasticsearch/logs/es1:/usr/share/elasticsearch/logs \
+-d 13aa43015aa1
+```
+
+> 3、安装kibana
+
+```bash
+#拉取kibana
+docker pull kibana
+#启动参数
+docker run --name kibana6.6.0 -e ELASTICSEARCH_URL=http://10.27.10.123:9200 -p 5601:5601 -d dfc685453eaa
+```
+
+创建索引
+
+![image-20201113164636525](images/image-20201113164636525.png)
+
+> 4、重启zipkin后数据未丢失
+
+```bash
+docker-compose restart
+```
+
+> 5、测试
+
+![image-20201113164930192](images/image-20201113164930192.png)
+
+## 十一、完整SpringCloud架构图
+
+![image-20201113165759501](images/image-20201113165759501.png)
\ No newline at end of file
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..41c6a33
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,16 @@
+version: "3.1"
+services:
+ zipkin:
+ image: daocloud.io/daocloud/zipkin:latest
+ #image: docker.io/openzipkin/zipkin:latest
+ restart: always
+ container_name: zipkin
+ ports:
+ - 9411:9411
+ environment:
+ - RABBIT_ADDRESSES=10.27.10.123:5672
+ - RABBIT_USER=test
+ - RABBIT_PASSWORD=test
+ - RABBIT_VIRTUAL_HOST=/test
+ - STORAGE_TYPE=elasticsearch
+ - ES_HOSTS=http://10.27.10.123:9200
diff --git a/images/1605019839175.png b/images/1605019839175.png
new file mode 100644
index 0000000..e1411d9
Binary files /dev/null and b/images/1605019839175.png differ
diff --git a/images/1605103387256.png b/images/1605103387256.png
new file mode 100644
index 0000000..6e45ee5
Binary files /dev/null and b/images/1605103387256.png differ
diff --git a/images/1605103421256.png b/images/1605103421256.png
new file mode 100644
index 0000000..ab5338e
Binary files /dev/null and b/images/1605103421256.png differ
diff --git a/images/1605103436874.png b/images/1605103436874.png
new file mode 100644
index 0000000..9289fa0
Binary files /dev/null and b/images/1605103436874.png differ
diff --git a/images/1605104987931.png b/images/1605104987931.png
new file mode 100644
index 0000000..2929462
Binary files /dev/null and b/images/1605104987931.png differ
diff --git a/images/1605106759891.png b/images/1605106759891.png
new file mode 100644
index 0000000..064849f
Binary files /dev/null and b/images/1605106759891.png differ
diff --git a/images/1605106760380.png b/images/1605106760380.png
new file mode 100644
index 0000000..064849f
Binary files /dev/null and b/images/1605106760380.png differ
diff --git a/images/20190325232054468.png b/images/20190325232054468.png
new file mode 100644
index 0000000..97301f4
Binary files /dev/null and b/images/20190325232054468.png differ
diff --git a/images/image-20201111095513133.png b/images/image-20201111095513133.png
new file mode 100644
index 0000000..b7a2bd4
Binary files /dev/null and b/images/image-20201111095513133.png differ
diff --git a/images/image-20201111095518705.png b/images/image-20201111095518705.png
new file mode 100644
index 0000000..b7a2bd4
Binary files /dev/null and b/images/image-20201111095518705.png differ
diff --git a/images/image-20201111110217411.png b/images/image-20201111110217411.png
new file mode 100644
index 0000000..5306ef6
Binary files /dev/null and b/images/image-20201111110217411.png differ
diff --git a/images/image-20201111132523595.png b/images/image-20201111132523595.png
new file mode 100644
index 0000000..9101fa2
Binary files /dev/null and b/images/image-20201111132523595.png differ
diff --git a/images/image-20201111134022333.png b/images/image-20201111134022333.png
new file mode 100644
index 0000000..b85b2f7
Binary files /dev/null and b/images/image-20201111134022333.png differ
diff --git a/images/image-20201111155409030.png b/images/image-20201111155409030.png
new file mode 100644
index 0000000..bf42f7f
Binary files /dev/null and b/images/image-20201111155409030.png differ
diff --git a/images/image-20201111162630381.png b/images/image-20201111162630381.png
new file mode 100644
index 0000000..0af3393
Binary files /dev/null and b/images/image-20201111162630381.png differ
diff --git a/images/image-20201111164239591.png b/images/image-20201111164239591.png
new file mode 100644
index 0000000..ffcba13
Binary files /dev/null and b/images/image-20201111164239591.png differ
diff --git a/images/image-20201111181650929.png b/images/image-20201111181650929.png
new file mode 100644
index 0000000..b3cc0c7
Binary files /dev/null and b/images/image-20201111181650929.png differ
diff --git a/images/image-20201112110708902.png b/images/image-20201112110708902.png
new file mode 100644
index 0000000..cb885ca
Binary files /dev/null and b/images/image-20201112110708902.png differ
diff --git a/images/image-20201112113609523.png b/images/image-20201112113609523.png
new file mode 100644
index 0000000..6bb1079
Binary files /dev/null and b/images/image-20201112113609523.png differ
diff --git a/images/image-20201112132217601.png b/images/image-20201112132217601.png
new file mode 100644
index 0000000..07f1bb6
Binary files /dev/null and b/images/image-20201112132217601.png differ
diff --git a/images/image-20201112135410803.png b/images/image-20201112135410803.png
new file mode 100644
index 0000000..ad3ad3c
Binary files /dev/null and b/images/image-20201112135410803.png differ
diff --git a/images/image-20201112135429409.png b/images/image-20201112135429409.png
new file mode 100644
index 0000000..0337a84
Binary files /dev/null and b/images/image-20201112135429409.png differ
diff --git a/images/image-20201112140037987.png b/images/image-20201112140037987.png
new file mode 100644
index 0000000..f45e209
Binary files /dev/null and b/images/image-20201112140037987.png differ
diff --git a/images/image-20201112140312697.png b/images/image-20201112140312697.png
new file mode 100644
index 0000000..4961ba2
Binary files /dev/null and b/images/image-20201112140312697.png differ
diff --git a/images/image-20201112141523371.png b/images/image-20201112141523371.png
new file mode 100644
index 0000000..be46fa2
Binary files /dev/null and b/images/image-20201112141523371.png differ
diff --git a/images/image-20201112145449105.png b/images/image-20201112145449105.png
new file mode 100644
index 0000000..aa8cf94
Binary files /dev/null and b/images/image-20201112145449105.png differ
diff --git a/images/image-20201112151632111.png b/images/image-20201112151632111.png
new file mode 100644
index 0000000..77b1062
Binary files /dev/null and b/images/image-20201112151632111.png differ
diff --git a/images/image-20201112155652269.png b/images/image-20201112155652269.png
new file mode 100644
index 0000000..c3e3791
Binary files /dev/null and b/images/image-20201112155652269.png differ
diff --git a/images/image-20201112155707148.png b/images/image-20201112155707148.png
new file mode 100644
index 0000000..09c4d00
Binary files /dev/null and b/images/image-20201112155707148.png differ
diff --git a/images/image-20201112160258956.png b/images/image-20201112160258956.png
new file mode 100644
index 0000000..e4f9801
Binary files /dev/null and b/images/image-20201112160258956.png differ
diff --git a/images/image-20201112165325023.png b/images/image-20201112165325023.png
new file mode 100644
index 0000000..48c03dd
Binary files /dev/null and b/images/image-20201112165325023.png differ
diff --git a/images/image-20201112165641574.png b/images/image-20201112165641574.png
new file mode 100644
index 0000000..eddb97a
Binary files /dev/null and b/images/image-20201112165641574.png differ
diff --git a/images/image-20201112173342142.png b/images/image-20201112173342142.png
new file mode 100644
index 0000000..686019f
Binary files /dev/null and b/images/image-20201112173342142.png differ
diff --git a/images/image-20201112180552221.png b/images/image-20201112180552221.png
new file mode 100644
index 0000000..c4ccc0e
Binary files /dev/null and b/images/image-20201112180552221.png differ
diff --git a/images/image-20201113093057704.png b/images/image-20201113093057704.png
new file mode 100644
index 0000000..355bb35
Binary files /dev/null and b/images/image-20201113093057704.png differ
diff --git a/images/image-20201113094055276.png b/images/image-20201113094055276.png
new file mode 100644
index 0000000..3c012c8
Binary files /dev/null and b/images/image-20201113094055276.png differ
diff --git a/images/image-20201113102316294.png b/images/image-20201113102316294.png
new file mode 100644
index 0000000..c2ee8d0
Binary files /dev/null and b/images/image-20201113102316294.png differ
diff --git a/images/image-20201113103152115.png b/images/image-20201113103152115.png
new file mode 100644
index 0000000..c59adcc
Binary files /dev/null and b/images/image-20201113103152115.png differ
diff --git a/images/image-20201113104134210.png b/images/image-20201113104134210.png
new file mode 100644
index 0000000..6baef9b
Binary files /dev/null and b/images/image-20201113104134210.png differ
diff --git a/images/image-20201113113814310.png b/images/image-20201113113814310.png
new file mode 100644
index 0000000..d308de5
Binary files /dev/null and b/images/image-20201113113814310.png differ
diff --git a/images/image-20201113114050201.png b/images/image-20201113114050201.png
new file mode 100644
index 0000000..049b15b
Binary files /dev/null and b/images/image-20201113114050201.png differ
diff --git a/images/image-20201113114131931.png b/images/image-20201113114131931.png
new file mode 100644
index 0000000..1d46212
Binary files /dev/null and b/images/image-20201113114131931.png differ
diff --git a/images/image-20201113120733998.png b/images/image-20201113120733998.png
new file mode 100644
index 0000000..46db1c8
Binary files /dev/null and b/images/image-20201113120733998.png differ
diff --git a/images/image-20201113121001855.png b/images/image-20201113121001855.png
new file mode 100644
index 0000000..72ebe65
Binary files /dev/null and b/images/image-20201113121001855.png differ
diff --git a/images/image-20201113121035781.png b/images/image-20201113121035781.png
new file mode 100644
index 0000000..30648be
Binary files /dev/null and b/images/image-20201113121035781.png differ
diff --git a/images/image-20201113131325996.png b/images/image-20201113131325996.png
new file mode 100644
index 0000000..97773e0
Binary files /dev/null and b/images/image-20201113131325996.png differ
diff --git a/images/image-20201113133658258.png b/images/image-20201113133658258.png
new file mode 100644
index 0000000..2f9f5ef
Binary files /dev/null and b/images/image-20201113133658258.png differ
diff --git a/images/image-20201113133713872.png b/images/image-20201113133713872.png
new file mode 100644
index 0000000..7a17d7b
Binary files /dev/null and b/images/image-20201113133713872.png differ
diff --git a/images/image-20201113133745985.png b/images/image-20201113133745985.png
new file mode 100644
index 0000000..5d2bd78
Binary files /dev/null and b/images/image-20201113133745985.png differ
diff --git a/images/image-20201113153443738.png b/images/image-20201113153443738.png
new file mode 100644
index 0000000..b043c0a
Binary files /dev/null and b/images/image-20201113153443738.png differ
diff --git a/images/image-20201113160658535.png b/images/image-20201113160658535.png
new file mode 100644
index 0000000..53b612a
Binary files /dev/null and b/images/image-20201113160658535.png differ
diff --git a/images/image-20201113160724897.png b/images/image-20201113160724897.png
new file mode 100644
index 0000000..1b03a81
Binary files /dev/null and b/images/image-20201113160724897.png differ
diff --git a/images/image-20201113164636525.png b/images/image-20201113164636525.png
new file mode 100644
index 0000000..e6368cc
Binary files /dev/null and b/images/image-20201113164636525.png differ
diff --git a/images/image-20201113164930192.png b/images/image-20201113164930192.png
new file mode 100644
index 0000000..c7e9ee7
Binary files /dev/null and b/images/image-20201113164930192.png differ
diff --git a/images/image-20201113165759501.png b/images/image-20201113165759501.png
new file mode 100644
index 0000000..4138e0a
Binary files /dev/null and b/images/image-20201113165759501.png differ