SpringCloud Hoxton SR8最新版,博文有点长耐心看完不说精通,至少从零搭建没问题了

SpringCloudNetflix

简介

Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, one-time tokens, global locks, leadership election, distributed sessions, cluster state). Coordination of distributed systems leads to boiler plate patterns, and using Spring Cloud developers can quickly stand up services and applications that implement those patterns. They will work well in any distributed environment, including the developer’s own laptop, bare metal data centres, and managed platforms such as Cloud Foundry.


Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的一些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,群集状态)。分布式系统的协调导致了样板式样,并且使用Spring Cloud开发人员可以快速支持实现这些样板的服务和应用程序。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心以及Cloud Foundry等托管平台。

特征

Spring Cloud focuses on providing good out of box experience for typical use cases and extensibility mechanism to cover others.

  • Distributed/versioned configuration

  • Service registration and discovery

  • Routing

  • Service-to-service calls

  • Load balancing

  • Circuit Breakers

  • Global locks

  • Leadership election and cluster state

  • Distributed messaging

Spring Cloud专注于为典型的用例和可扩展性机制(包括其他用例)提供良好的开箱即用体验。

  • 分布式/版本化配置

  • 服务注册和发现

  • 路由

  • 服务到服务的请求

  • 负载均衡

  • 断路器

  • 全局锁

  • 领导选举和集群状态

  • 分布式消息传递

SpringCloud的基础架构

在 Spring Cloud 微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Ngnix),再到达服务网关(Zuul 集群、GateWay集群),然后再到具体的服务。所有服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在 GIT 仓库,方便开发人员随时改配置。

一个简单的架构图大致如下:

SpringCloud基础架构图

创建一个公用的依赖管理

创建一个工程为hello-spring-cloud-dependencies的项目。pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-dependencies</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>hello-spring-cloud-dependencies</name>
    <description>hello-spring-cloud的父工程,管理所有依赖</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
    </parent>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton SR8</spring-cloud.version>
    </properties>
​
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
​
    <build>
        <plugins>
            <!-- Compiler 插件, 设定 JDK 版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>
​
            <!-- 打包 jar 文件时,配置 manifest 文件,加入 lib 包的 jar 依赖 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <addMavenDescriptor>false</addMavenDescriptor>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <configuration>
                            <archive>
                                <manifest>
                                    <!-- Add directory entries -->
                                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                                    <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                                    <addClasspath>true</addClasspath>
                                </manifest>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
​
            <!-- resource -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
            </plugin>
​
            <!-- install -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-install-plugin</artifactId>
            </plugin>
​
            <!-- clean -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
            </plugin>
​
            <!-- dependency -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
            </plugin>
        </plugins>
​
        <pluginManagement>
            <plugins>
                <!-- Java Document Generate -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-javadoc-plugin</artifactId>
                    <executions>
                        <execution>
                            <phase>prepare-package</phase>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
​
                <!-- YUI Compressor (CSS/JS压缩) -->
                <plugin>
                    <groupId>net.alchim31.maven</groupId>
                    <artifactId>yuicompressor-maven-plugin</artifactId>
                    <version>1.5.1</version>
                    <executions>
                        <execution>
                            <phase>prepare-package</phase>
                            <goals>
                                <goal>compress</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <encoding>UTF-8</encoding>
                        <jswarn>false</jswarn>
                        <nosuffix>true</nosuffix>
                        <linebreakpos>30000</linebreakpos>
                        <force>true</force>
                        <includes>
                            <include>**/*.js</include>
                            <include>**/*.css</include>
                        </includes>
                        <excludes>
                            <exclude>**/*.min.js</exclude>
                            <exclude>**/*.min.css</exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
​
        <!-- 资源文件配置 -->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <excludes>
                    <exclude>**/*.java</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
    </build>
​
    <repositories>
        <repository>
            <id>aliyun-repos</id>
            <name>Aliyun Repository</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
​
        <repository>
            <id>sonatype-repos</id>
            <name>Sonatype Repository</name>
            <url>https://oss.sonatype.org/content/groups/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>sonatype-repos-s</id>
            <name>Sonatype Repository</name>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
​
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
​
    <pluginRepositories>
        <pluginRepository>
            <id>aliyun-repos</id>
            <name>Aliyun Repository</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
​
</project>
  • parent:继承了 Spring Boot 的 Parent,表示我们是一个 Spring Boot 工程

  • package:pom,表示该项目仅当做依赖项目,没有具体的实现代码

  • spring-cloud-dependencies:在 properties 配置中预定义了版本号为 Hoxton SR8 ,表示我们的 Spring Cloud 使用的是 Hoxton SR8 版本

  • build:配置了项目所需的各种插件

  • repositories:配置项目下载依赖时的第三方库

在实际开发中,我们所有的项目都会依赖这个 dependencies 项目,整个项目周期中的所有第三方依赖的版本也都由该项目进行管理。

服务的注册与发现

简介

在这里我们简单的创建一个Eureka,Eureka是一个服务注册和发现的模块

创建Eureka注册中心

创建一个工程为hello-spring-cloud-eureka-server的项目。

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
​
    <artifactId>hello-spring-cloud-eureka-server</artifactId>
    <packaging>jar</packaging>
​
    <name>hello-spring-cloud-eureka-server</name>
​
    <dependencies>
        <!-- Spring Boot Begin -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot End -->
​
        <!-- Spring Cloud Begin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <!-- Spring Cloud End -->
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.wujie.hellospringcloudeurekaserver.HelloSpringCloudEurekaServerApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

server:
  port: 8761
​
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #不注册自己
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    enable-self-preservation: false # 关闭自我保护
    eviction-interval-timer-in-ms: 5000 # 每隔5秒进行一次服务列表清理

注释:registerWithEureka 指示此实例是否应在eureka服务器上注册其信息以供他人发现。在某些情况下,您不希望发现实例,而只希望发现其他实例。

在启动类上加上@EnableEurekaServer注解表名开启Eureka服务

@SpringBootApplication
@EnableEurekaServer
public class HelloSpringCloudEurekaServerApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(HelloSpringCloudEurekaServerApplication.class, args);
    }
​
}

操作界面

Eureka是有界面的,我们启动项目。浏览器访问http://localhost:8761即可看见相应的界面如下:

image-20200904105949950

创建服务提供者

简介

当 Client 向 Server 注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka Server 从每个 Client 实例接收心跳消息。 如果心跳超时,则通常将该实例从注册 Server 中删除。

创建Eureka服务提供者

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
​
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
​
    <artifactId>hello-spring-cloud-server-admin</artifactId>
    <packaging>jar</packaging>
​
    <name>hello-spring-cloud-server-admin</name>
    <description>向Eureka服务中心注册,并提供服务</description>
​
    <dependencies>
        <!-- Spring Boot Begin -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot End -->
​
        <!-- Spring Cloud Begin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <!-- Spring Cloud End -->
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.server.admin.HelloSpringCloudServerAdminApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
​
spring:
  application:
    name: hello-spring-cloud-server-admin
server:
  port: 8762

注释:spring.application.name是很重要的配置,这在以后服务与服务之间的调用都会根据这个name来进行。

在启动类上加上@EnableDiscoveryClient注解表明要注册到服务中心。启动工程访问http://localhost:8761

image-20200904111917708

会发现在Eureka中我们已经注册上了我们的服务。

接下来我们访问,http://localhost:8762/hi?message=helloCloud

image-20200904112235449

创建服务消费者

简介

在微服务中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于 http restful 的。Spring cloud 有两种服务调用方式,一种是 ribbon + restTemplate!另一种是 feign,fegin集成ribbon。

我们先基于ribbon来实现消费者!fegin在后面实现。

创建基于ribbon的服务消费者

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-web-admin-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-web-admin-ribbon</name>
    <description>基于ribbon去消费服务</description>
​
​
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
​
    </dependencies>
​
    <build>
        <plugins>
​
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.web.admin.ribbon.HelloSpringCloudWebAdminRibbonApplication
                    </mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
​
</project>

application.xml

spring:
  application:
    name: hello-spring-cloud-web-admin-ribbon
server:
  port: 8763
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

在启动类上加上@EnableDiscoveryClient注解并配置注入RestTemplate,同时通过@LoadBalanced开启负载均衡功能

@SpringBootApplication
@EnableDiscoveryClient
public class HelloSpringCloudWebAdminRibbonApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(HelloSpringCloudWebAdminRibbonApplication.class, args);
    }
​
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

创建Service测试用

@Service
public class AdminService {
    @Autowired
    private RestTemplate restTemplate;
​
    public String sayHi(String message) {
        return restTemplate.getForObject("http://HELLO-SPRING-CLOUD-SERVER-ADMIN/hi?message=" + message, String.class);
    }
}

创建controller测试

@RestController
public class TestController {
    @Autowired
    private AdminService adminService;
​
    @RequestMapping(value = "hi", method = RequestMethod.GET)
    public String sayHi(@RequestParam String message) {
        return adminService.sayHi(message);
    }
}

启动项目访问:http://localhost:8763/hi?message=helloCloud

image-20200904124325875

测试负载均衡

修改消费者的端口号为8764,修改服务提供者的端口号为8763并启动8763.启动8764.启动完毕查看一下Eureka会发现服务提供者有两个实例

image-20200904124903529

再访问http://localhost:8764/hi?message=helloCloud会发现端口号会变动,说明测试成功。

image-20200904124958817

image-20200904125009176

此时的架构应该是如图:

image-20200904132500947

  • 一个Eureka注册中心

  • 两个服务提供者

  • 一个消费者,消费者通过restTemplate调用admin服务提供者。因为开启了负载均衡,所以会轮流的去调用8762和8763

创建基于Fegin的服务消费者

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-web-admin-fegin</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-web-admin-fegin</name>
    <description>创建基于Fegin的消费者</description>
​
​
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.web.admin.fegin.HelloSpringCloudWebAdminFeginApplication
                    </mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
​
</project>

application.yml

spring:
  application:
    name: hello-spring-cloud-web-admin-fegin
​
server:
  port: 8765
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

创建一个client接口

@FeignClient(value = "HELLO-SPRING-CLOUD-SERVER-ADMIN")
public interface AdminClient {
    @RequestMapping(value = "hi", method = RequestMethod.GET)
    String sayHi(@RequestParam(value = "message") String message);
}

@FeignClient注解中,字符串值(上面的“ value”)是一个任意的客户端名称,该名称用于创建Ribbon负载均衡器(请参阅下文以了解Ribbon支持的详细信息)或Spring Cloud LoadBalancer。

创建一个controller测试

@RestController
public class TestController {
    @Autowired
    private AdminClient adminClient;
​
    @RequestMapping(value = "hi", method = RequestMethod.GET)
    public String sayHi(@RequestParam String message) {
        return adminClient.sayHi(message);
    }
}

在启动类上加上@EnableFeignClients注解开启Fegin的功能。启动项目,之后访问http://localhost:8765/hi?message=helloCloud

image-20200904132215275

image-20200904132230522

说明访问成功并且开启了负载均衡

此时的架构图如下:

image-20200904132538039

使用熔断器防止服务雪崩

简介

雪崩效应

在微服务中,我们为了保证服务的高可用,通常会将单个服务通过集群的方式进行部署,然后服务与服务之间通过ribbon+restTemplate或者Fegin的方式来进行调用。但是在实际生产环境中,由于网络原因或者自身的原因,并不能保证服务100%被调用。如果有某个服务出现故障,那调用这个服务的线程将会出现线程阻塞,当有大量的请求进入,容器的线程将会瞬间消失殆尽,导致整个服务宕机。由于微服务之间的依赖性以及相互调用。就会导致整个系统都出现雪崩。

雪崩

当8763服务宕机了,8762就调不通,就会一直阻塞在这里,就会导致8767也调不通,当所有请求都同时进入的时候,8762/8767也会随机宕机。

熔断器

为了解决上面的问题,业界提出了熔断器模型!

Netflix 开源了 Hystrix 组件,实现了熔断器模式,Spring Cloud 对这一组件进行了整合。在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:

熔断器

较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystrix 是 5 秒 20 次) 熔断器将会被打开。熔断器打开后,为了避免连锁故障,通过 fallback 方法可以直接返回一个固定值。

Ribbon中使用熔断器

在之前的ribbon项目pom中增加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

在Service方法上增加@HystrixCommand注解,通过fallbackMethod指定一个方法

@Service
public class AdminService {
    @Autowired
    private RestTemplate restTemplate;
    @HystrixCommand(fallbackMethod = "serviceError")
    public String sayHi(String message) {
        return restTemplate.getForObject("http://HELLO-SPRING-CLOUD-SERVER-ADMIN/hi?message=" + message, String.class);
    }
​
    public String serviceError(String message) {
        return "Hi,your message is :\"" + message + "\" but request error.";
    }
​
}

在启动类上加上@EnableCircuitBreaker注解开启熔断

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class HelloSpringCloudWebAdminRibbonApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(HelloSpringCloudWebAdminRibbonApplication.class, args);
    }
​
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

重新启动ribbon服务,并停止admin服务。访问:http://localhost:8764/hi?message=helloCloud出现如图证明熔断器开启成功

image-20200904152424360

Fegin中使用熔断器

Fegin自带了熔断器,所以我没有不需要在pom文件中增加多余依赖,只需要在application.yml中增加配置

spring:
  application:
    name: hello-spring-cloud-web-admin-fegin
​
server:
  port: 8765
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
#开启熔断支持
feign:
  hystrix:
    enabled: true

修改client的代码如下:

@FeignClient(value = "HELLO-SPRING-CLOUD-SERVER-ADMIN", fallback = AdminClientImpl.class)
public interface AdminClient {
    @RequestMapping(value = "hi", method = RequestMethod.GET)
    String sayHi(@RequestParam(value = "message") String message);
}
@Component
public class AdminClientImpl implements AdminClient {
    @Override
    public String sayHi(String message) {
        return "Hi,your message is :\"" + message + "\" but request error.";
    }
}

fallback指定熔断处理类,并实现这个fegin类,根据业务作出相应的处理。重新启动访问:http://localhost:8765/hi?message=helloCloud

image-20200904153743429

测试成功。

触发熔断器的原因

如果需要知道触发熔断器的原因,则可以使用@FeignClient中的fallbackFactory属性!

继续修改client的代码如下:

@FeignClient(value = "HELLO-SPRING-CLOUD-SERVER-ADMIN", fallbackFactory = AdminClientImpl.class)
public interface AdminClient {
    @RequestMapping(value = "hi", method = RequestMethod.GET)
    String sayHi(@RequestParam(value = "message") String message);
}
​
@Component
public class AdminClientImpl implements FallbackFactory<AdminClient> {
​
    @Override
    public AdminClient create(Throwable throwable) {
        return new AdminClient() {
            @Override
            public String sayHi(String message) {
                return "Hi,your message is :\"" + message + "\" but request error.fallback; reason was: " + throwable.getMessage();
            }
        };
    }
}

注意:与上面的@FeginClient不同这里使用的是fallbackFactory,实现的FallbackFactory<T>

重新启动访问同上地址可以得到如下的地址,当然这里返回的什么可以自行根据业务进行处理

image-20200904154531181

使用熔断器仪表盘进行监控

pom.xml配置文件中加入

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

新建一个配置类,由于SpringBoot2.x的/hystrix.stream不是默认的所以需要手动配置。

@Configuration
public class HystrixDashboardConfiguration {
    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

application.yml新建配置信息

#需要将端口暴露出去,不然404
management:
  endpoints:
    web:
      exposure:
        include: "*"
#Origin parameter: http://localhost:8765/actuator/hystrix.stream is not in the allowed list of proxy host names.  If it should be allowed add it to hystrix.dashboard.proxyStreamAllowList.
#简单点说就是需要把localhost加入到代理所允许的集合里,不然也是访问不了
hystrix:
  dashboard:
    proxy-stream-allow-list: "localhost"

在启动类上需要加上两个注解:

@EnableHystrixDashboard
@EnableCircuitBreaker

重新启动服务后访问:http://localhost:8765/hystrix出现如下界面

image-20200904191815104

输入监控的地址:http://localhost:8765/actuator/hystrix.stream

输入名称:XXX

点击按钮即可进入如下页面

image-20200904191949493

Hystrix Dashboard 界面监控参数

HystrixStream界面说明

总结

HystrixDashboard集成坑比较多,需要小心,并且仔细。本人遇到的一些坑位也说了。望谨慎!

使用网关统一入口访问

简介

路由是微服务架构不可或缺的一部分。例如,/可能被映射到您的Web应用程序,/ api / users被映射到用户服务,而/ api / shop被映射到shop服务。Zuul是Netflix提供的基于JVM的路由器和服务器端负载平衡器。这里先介绍Zuul的使用方式。GateWay的在Alibaba的时候再说。

Netflix将Zuul用于以下用途:(以下翻译来自官网)

  • 认证方式

  • 见解

  • 压力测试

  • 灰度发布测试

  • 动态路由

  • 服务迁移

  • 减载

  • 安全

  • 静态响应处理

  • 主动/主动流量管理

创建路由网关

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-zuul</name>
    <description>创建路由网关</description>
​
​
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
​
​
​
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.zuul.HelloSpringCloudZuulApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
​
</project>

application.yml

spring:
  application:
    name: hello-spring-cloud-zuul
​
​
server:
  port: 8766
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
zuul:
  routes:
    ribbon:
      path: /ribbon/**
      serviceId: HELLO-SPRING-CLOUD-WEB-ADMIN-RIBBON
      stripPrefix: true
    fegin:
      path: /fegin/**
      serviceId: HELLO-SPRING-CLOUD-WEB-ADMIN-FEGIN
      stripPrefix: true

在启动类上加上@EnableZuulProxy注解并启动

@SpringBootApplication
@EnableZuulProxy
public class HelloSpringCloudZuulApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(HelloSpringCloudZuulApplication.class, args);
    }
​
}

分别访问:对fegin和ribbon以及admin返回简单做了修改,显示端口让我们能够清晰的看见确实转发成功

http://localhost:8766/ribbon/hi?message=helloCloud

image-20200904204338029

http://localhost:8766/fegin/hi?message=helloCloud

image-20200904204441360

配置网关路由失败时的回调

当Zuul中给定路由的熔断时,可以通过创建FallbackProvider类型的bean提供回退响应。在此bean中,您需要指定回退用于的路由ID,并提供ClientHttpResponse作为回退返回。以下示例显示了一个相对简单的FallbackProvider实现:

@Component
public class WebAdminRoutesFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        // ServiceId,如果需要所有调用都支持回退,则 return "*" 或 return null
//        return "HELLO-SPRING-CLOUD-WEB-ADMIN-FEGIN";
        return null;
    }
​
    @Override
    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
​
    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            /**
             * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的,
             * 不应该把 api 的 404,500 等问题抛给客户端
             * 网关和 api 服务集群对于客户端来说是黑盒
             * @return
             * @throws IOException
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }
​
            @Override
            public int getRawStatusCode() throws IOException {
                return status.value();
            }
​
            @Override
            public String getStatusText() throws IOException {
                return status.getReasonPhrase();
            }
​
            @Override
            public void close() {
            }
​
            @Override
            public InputStream getBody() throws IOException {
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("message", "无法连接,请检查您的网络");
                return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8"));
            }
​
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                // 和 getBody 中的内容编码一致
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

如果您想为所有路由提供默认回退,则可以创建FallbackProvider类型的Bean,并让getRoute方法返回*或null。

自定义网关路由过滤器

@Component
public class LoginFilter extends ZuulFilter {
    private static final Logger logger = LoggerFactory.getLogger(LoginFilter.class);
    /**
     * 配置过滤类型,有四种不同生命周期的过滤器类型
     * 1. pre:路由之前
     * 2. routing:路由之时
     * 3. post:路由之后
     * 4. error:发送错误调用
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }
    /**
     * 配置过滤的顺序
     * @return
     */
    @Override
    public int filterOrder() {
        return 0;
    }
    /**
     * 配置是否需要过滤:true/需要,false/不需要
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }
    /**
     * 过滤器的具体业务代码
     * @return
     * @throws ZuulException
     */
    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String auth = request.getHeader("auth");
        logger.info("{} >>> {}", request.getMethod(), request.getRequestURL().toString());
        if (!StringUtils.isNotBlank(auth)) {
            logger.warn("权限认证失败");
            context.setSendZuulResponse(false);
            context.setResponseStatusCode(401);
            context.getResponse().setCharacterEncoding("UTF-8");
            context.getResponse().setContentType("text/html;cahrset=UTF-8");
            try {
                context.getResponse().getWriter().write("权限认证失败");
            } catch (IOException e) {
            }
        } else {
            logger.info("OK");
        }
        return null;
    }
}

注意:当返回中文提示时,需要加上

context.getResponse().setCharacterEncoding("UTF-8");
context.getResponse().setContentType("text/html;cahrset=UTF-8");

否则会出现中文乱码

自定义好后,我们重启项目

第一次不加头信息访问:http://localhost:8766/ribbon/hi?message=helloCloud提示权限认证失败

image-20200904211731194

第二次加头信息访问:http://localhost:8766/ribbon/hi?message=helloCloud就返回正确的结果信息

image-20200904211833207

配置中心

简介

Spring Cloud Config为分布式系统中的外部化配置提供服务器端和客户端支持。使用Config Server,您可以在中心位置管理所有环境中应用程序的外部属性。客户端和服务器上的概念与Spring Environment和PropertySource抽象完全相同,因此它们非常适合Spring应用程序,但可以与以任何语言运行的任何应用程序一起使用。在应用程序从开发人员到测试人员再到生产人员的整个部署管道中进行移动时,您可以管理这些环境之间的配置,并确保应用程序具有它们迁移时所需的一切。服务器存储后端的默认实现使用git,因此它可以轻松支持配置环境的标记版本,并且可以通过各种工具来访问这些内容来管理内容。添加替代实现并将其插入Spring配置很容易。

创建配置中心服务端

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-config</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-config</name>
    <description>创建配置文件中心</description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.config.HelloSpringCloudConfigApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
​
</project>

application.yml

spring:
  application:
    name: hello-spring-cloud-config
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/wujiele/respo.git
          search-paths:  reps
          username:
          password:
​
​
server:
  port: 8888
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

相关配置说明,如下:

  • spring.cloud.config.label:配置仓库的分支

  • spring.cloud.config.server.git.uri:配置 Git 仓库地址(GitHub、GitLab、码云 ...)

  • spring.cloud.config.server.git.search-paths:配置仓库路径(存放配置文件的目录)

  • spring.cloud.config.server.git.username:访问 Git 仓库的账号

  • spring.cloud.config.server.git.password:访问 Git 仓库的密码

注意事项:

  • 如果使用 GitLab 作为仓库的话,git.uri 需要在结尾加上 .git,GitHub 则不用

在启动类上加上注解@EnableConfigServer开启配置中心服务

@SpringBootApplication
@EnableConfigServer
public class HelloSpringCloudConfigApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(HelloSpringCloudConfigApplication.class, args);
    }
​
}

启动项目访问:http://localhost:8888/config-client/dev/master如下图:

image-20200904213743949

附:HTTP 请求地址和资源文件映射

创建配置中心客户端

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-config-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-config-client</name>
    <description>创建配置文件客户端</description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.config.client.HelloSpringCloudConfigClientApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
​
</project>

application.yml

spring:
  application:
    name: hello-spring-cloud-config-client
  cloud:
    config:
      uri: http://localhost:8888
      name: config-client
      label: master
      profile: dev
​
server:
  port: 8889
​
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

相关配置说明,如下:

  • spring.cloud.config.uri:配置服务中心的网址

  • spring.cloud.config.name:配置文件名称的前缀

  • spring.cloud.config.label:配置仓库的分支

  • spring.cloud.config.profile

    :配置文件的环境标识

    • dev:表示开发环境

    • test:表示测试环境

    • prod:表示生产环境

注意:配置服务器的默认端口为 8888,如果修改了默认端口,则客户端项目就不能在 application.ymlapplication.properties 中配置 spring.cloud.config.uri,必须在 bootstrap.yml 或是 bootstrap.properties 中配置,原因是 bootstrap 开头的配置文件会被优先加载和配置,切记

创建TestController测试类

@RestController
public class TestController {
    @Value("${wujie}")
    private String name;
​
    @RequestMapping(value = "/hi", method = RequestMethod.GET)
    public String hi() {
        return name;
    }
}

启动类上添加@EnableDiscoveryClient注解开启服务

在启动完成后会看见我们的端口号与配置文件里的不一样了,这是因为我在远程重新配置了端口号

image-20200904220819149

image-20200904220851918

使用新的端口号访问http://localhost:7010/hi

image-20200904220958492

返回与远程配置一样,则证明配置成功。

服务链路追踪

简介

Spring Cloud Sleuth implements a distributed tracing solution for Spring Cloud.

Spring Cloud Sleuth为Spring Cloud实现了分布式跟踪解决方案。

术语

Spring Cloud Sleuth borrows Dapper’s terminology.

Spring Cloud Sleuth借鉴了Dapper的术语。

Span: The basic unit of work. For example, sending an RPC is a new span, as is sending a response to an RPC. Spans are identified by a unique 64-bit ID for the span and another 64-bit ID for the trace the span is a part of. Spans also have other data, such as descriptions, timestamped events, key-value annotations (tags), the ID of the span that caused them, and process IDs (normally IP addresses).

Span:工作的基本单位。例如,发送RPC是新的跨度,就像发送响应到RPC一样。跨度由跨度的唯一64位ID和跨度所属的跟踪的另一个64位ID标识。跨区还具有其他数据,例如描述,带有时间戳的事件,键值批注(标签),引起跨度的跨区ID和进程ID(通常为IP地址)

Trace: A set of spans forming a tree-like structure. For example, if you run a distributed big-data store, a trace might be formed by a PUT request.

Trace:一组形成树状结构的跨度。例如,如果运行分布式大数据存储,则跟踪可能由PUT请求形成。

Annotation: Used to record the existence of an event in time. With Brave instrumentation, we no longer need to set special events for Zipkin to understand who the client and server are, where the request started, and where it ended. For learning purposes, however, we mark these events to highlight what kind of an action took place.

  • cs: Client Sent. The client has made a request. This annotation indicates the start of the span.

  • sr: Server Received: The server side got the request and started processing it. Subtracting the cs timestamp from this timestamp reveals the network latency.

  • ss: Server Sent. Annotated upon completion of request processing (when the response got sent back to the client). Subtracting the sr timestamp from this timestamp reveals the time needed by the server side to process the request.

  • cr: Client Received. Signifies the end of the span. The client has successfully received the response from the server side. Subtracting the cs timestamp from this timestamp reveals the whole time needed by the client to receive the response from the server.

Annotation:用于及时记录事件的存在。使用Brave工具,我们不再需要为Zipkin设置特殊事件来了解客户端和服务器是谁,请求在哪里开始以及在哪里结束。但是,出于学习目的,我们标记这些事件以突出显示发生了哪种操作。

  • cs:客户端已发送。客户提出了要求。此注释指示跨度的开始。

  • sr:收到服务器:服务器端收到请求并开始处理它。从该时间戳减去cs时间戳可揭示网络延迟。

  • ss:服务器已发送。在请求处理完成时添加注释(当响应被发送回客户端时)。从此时间戳中减去sr时间戳可显示服务器端处理请求所需的时间。

  • cr:收到客户。表示跨度结束。客户端已成功收到服务器端的响应。从此时间戳中减去cs时间戳可显示出客户端从服务器接收响应所需的整个时间。

Zipkin简介

ZipKin 是一个开放源代码的分布式跟踪系统,由 Twitter 公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。它的理论模型来自于 Google Dapper 论文。

每个服务向 ZipKin 报告计时数据,ZipKin 会根据调用关系通过 ZipKin UI 生成依赖关系图,显示了多少跟踪请求通过每个服务,该系统让开发者可通过一个 Web 前端轻松的收集和分析数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。

将 Span 和 Trace 在一个系统中使用 Zipkin 注解的过程图形化:

Zipkin 注解的过程

创建 ZipKin 服务端

Zipkin共有三种运行方式,java、docker、以及源码编译。我这里就直接使用java的方式,感兴趣的可以自行去官网查看别的方式:

[Zipkin官网]  https://zipkin.io/pages/quickstart.html 

在SpringBoot 2.0版本以后已经不需要自行搭建zipkin-server,而是直接使用官方提供的编译好的jar包,在终端使用以下命令下载并启动zipkin-server

curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar

使用java命令运行起来以后可以访问http://localhost:9411查看界面

image-20200905104408566

在想要追踪的项目里加上依赖,并在配置文件中加入配置

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
zipkin:
  base-url: http://localhost:9411

然后重启项目,将之前写的接口测试一下,就可以在zipkin中进行查看

image-20200905111628342

image-20200905111658828

image-20200905111741633

至此就代表zipkin配置成功。

监控系统

简介

随着开发周期的推移,项目会不断变大,切分出的服务也会越来越多,这时一个个的微服务构成了错综复杂的系统。对于各个微服务系统的健康状态、会话数量、并发数、服务资源、延迟等度量信息的收集就成为了一个挑战。Spring Boot Admin 应运而生,它正式基于这些需求开发出的一套功能强大的监控管理系统。

Spring Boot Admin provides the following features for registered applications:

  • Show health status

  • Show details, like

    • JVM & memory metrics

    • micrometer.io metrics

    • Datasource metrics

    • Cache metrics

  • Show build-info number

  • Follow and download logfile

  • View jvm system- & environment-properties

  • View Spring Boot Configuration Properties

  • Support for Spring Cloud's postable /env- &/refresh-endpoint

  • Easy loglevel management

  • Interact with JMX-beans

  • View thread dump

  • View http-traces

  • View auditevents

  • View http-endpoints

  • View scheduled tasks

  • View and delete active sessions (using spring-session)

  • View Flyway / Liquibase database migrations

  • Download heapdump

  • Notification on status change (via e-mail, Slack, Hipchat, ...)

  • Event journal of status changes (non persistent)

Spring Boot Admin为注册的应用程序提供以下功能:

  • 显示健康状况

  • 显示详细信息,例如

    • JVM和内存指标

    • micrometer.io 指标

    • 数据源指标

    • 缓存指标

  • 显示构建信息编号

  • 关注并下载日志文件

  • 查看JVM系统和环境属性

  • 查看Spring Boot配置属性

  • 支持Spring Cloud的可发布/ env-&/ refresh-endpoint

  • 轻松的日志级别管理

  • 与JMX-beans交互

  • 查看线程垃圾

  • 查看http痕迹

  • 查看审核事件

  • 查看http端点

  • 查看定时任务

  • 查看和删除活动会话(使用spring-session)

  • 查看Flyway / Liquibase数据库迁移

  • 下载堆内存

  • 状态更改通知(通过电子邮件,Slack,Hipchat等)

  • 状态更改的事件日志(非持久性)

Spring Boot Admin 有两个角色组成,一个是 Spring Boot Admin Server,一个是 Spring Boot Admin Client,本章节将带领大家实现 Spring Boot Admin 的搭建。

Spring Boot Admin服务端

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-admin</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-admin</name>
    <description>SpringBootAdmin服务端</description>
​
​
​
    <dependencies>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- Spring Cloud Begin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <!-- Spring Cloud End -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.admin.HelloSpringCloudAdminApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
​
</project>

application.yml

spring:
  application:
    name: hello-spring-cloud-admin
  zipkin:
    base-url: http://localhost:9411
server:
  port: 8767
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

在启动类上加上@EnableAdminServer注解,然后启动

@SpringBootApplication
@EnableAdminServer
public class HelloSpringCloudAdminApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloSpringCloudAdminApplication.class, args);
    }

}

访问http://localhost:8767即可看见如图说明成功

image-20200905115119910

SpringBootAdmin客户端

pom.xml配置文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wujie</groupId>
        <artifactId>hello-spring-cloud-dependencies</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.wujie</groupId>
    <artifactId>hello-spring-cloud-admin-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hello-spring-cloud-admin-client</name>
    <description>Spring Boot Admin客户端</description>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.wujie.hello.spring.cloud.admin.client.HelloSpringCloudAdminClientApplication
                    </mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

spring:
  application:
    name: hello-spring-cloud-admin-client
  boot:
    admin:
      client:
        url: http://localhost:8767
  zipkin:
    base-url: http://localhost:9411

server:
  port: 8768

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: ALWAYS

注意:

management: endpoints: web: exposure: include: '*' endpoint: health: show-details: ALWAYS

这个是配置在客户端,才可以显示信息。网上很多放在服务端,结果访问就什么信息都没有就像下面这张图

image-20200905123651901

如果配置正确就会是这样的

image-20200905123723835

至此,也就配置成功了。

心得

不管是对于微服务还是其他什么技术,我们只有抱着谦虚的态度去学习,去摸索才能够从中得到自己想要的知识,另外也不要畏惧英文文献,不管在百度还是Google上去搜索的永远都是第二手第三手甚至第N手的资料,官方才是最新的第一手资料,甚至各个版本的都有,但是大多数都是英文,此时我们不能畏惧学习,网上翻译软件如此之多,我们先自己尝试着阅读再去使用翻译软件进行翻译,结合自己的行业的理解,基本上也是八九不离十了。这样做的好处在于我们可以进一步提升自己的英语水平,还可以对文档至少有三遍的记忆(自己先尝试翻译、使用翻译软件翻译、做笔记),也会加深我们对于此技术的认知。

古人云温故而知新可以为师矣

参考资料

[SpringCloud Hoxton SR8官方地址]  https://docs.spring.io/spring-cloud/docs/Hoxton.SR8/reference/html/ 

[git地址]  https://gitee.com/wujiele/hello-spring-cloud-eureka.git 

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值