title: 分布式开发之SpringCloud
苟日新,又日新,日日新。
准备
- springCloud是一个新的微服务框架。(dubbo为RPC框架)
在微服务架构中,包括 服务注册与发现(eureka), 服务消费(ribben/feign), 负载均衡(feign集成ribben,并实现负载), 熔断器(熔断器防止服务雪崩,hystrix, feign自带hystrix只是需要开启), 智能路由(zuul), 配置管理(springcloud config) 等
工程创建
- 创建工程文件夹,用来放所有工程
- NEW DIRECTORY
- 创建子工程,创建pom,创建applicaton.xml
- 目录结构如下:
主项目版本控制
- springBoot声明
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/>
</parent>
- 打包方式
<!--注意打包方式为 pom -->
<packaging>pom</packaging>
- springcloud版本
<properties>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
- 全部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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.itic.spring.cloud.itic.dependencies</groupId>
<artifactId>spring-cloud-itic-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring-cloud-itic-dependencies</name>
<description>Spring Cloud Dependencies</description>
<!--注意打包方式为 pom -->
<packaging>pom</packaging>
<!--添加子模块标识 !!!! -->
<modules>
<!--声明 spring-cloud-eureka 为当前项目 子模块 ,以后有新的子模块添加也要在此添加-->
<!-- <module>spring-cloud-hello-eureka</module>-->
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<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>
</dependency>
</dependencies>
<!-- dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承)
dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖-->
<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>
<!-- Springboot组件: -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
服务注册与发现(Eureka)
1.1、创建服务注册中心
- 引入主项目
<parent>
<groupId>com.itic.spring.cloud.itic.dependencies</groupId>
<artifactId>spring-cloud-itic-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
- 引入spring-cloud-starter-netflix-eureka-server的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
- Application启动类:
package com.itic.spring.cloud.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- yml
# 本身是注册中心,但是也需要注册,这里可以向自己注册
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# 不在页面上显示当前 服务
register-with-eureka: false
# 为true时,可以启动,但报异常:Cannot execute request on any known server
fetch-registry: false
# 当应用上线率过低时,eureka会启动保护机制,默认确定当前服务是开启的,开发 环境中尽量关闭(设置为false)
server:
enable-self-preservation: false
# 服务名 ,很重要 ,后期 config 配置中心会以 服务名为标识找对应的 服务
spring:
application:
name: eureka
# 设置当前项目端口号
server:
port: 8761
- 启动程序,访问http://localhost:8761/
1.2、创建服务提供者
- 引入主项目:
<parent>
<groupId>com.itic.spring.cloud.itic.dependencies</groupId>
<artifactId>spring-cloud-itic-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
- 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>
<groupId>com.itic.spring.cloud.itic.service</groupId>
<artifactId>spring-cloud-itic-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-itic-service</name>
<description></description>
<parent>
<groupId>com.itic.spring.cloud.itic.dependencies</groupId>
<artifactId>spring-cloud-itic-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<!-- 此处指定main方法入口的class -->
<mainClass>com.spring.cloud.itic.service.ServiceApplication</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- Application启动类:
package com.spring.cloud.itic.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableEurekaClient
@RestController
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/hiHome")
public String home(@RequestParam String name) {
return "hhahahah";
}
}
- yml
# 注册到 eureka server 配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8763
spring:
application:
## 需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name
name: eurekaclient
##boot:
##admin:
##client:
## url: http://localhost:8080
服务消费者(Ribbon,Feign)
feign集成了Ribbon,并且默认实现负载均衡
1.1 Feign实现服务消费
- 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>
<groupId>com.itic.spring.cloud.itic.feign</groupId>
<artifactId>spring-cloud-itic-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-itic-feign</name>
<description></description>
<parent>
<groupId>com.itic.spring.cloud.itic.dependencies</groupId>
<artifactId>spring-cloud-itic-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<!-- 此处指定main方法入口的class -->
<mainClass>com.spring.cloud.itic.service.ServiceApplication</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8769
spring:
application:
name: service-feign
sleuth:
sampler:
probability: 1.0 #2.0后percentage改为probability
feign:
hystrix:
enabled: true
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
- 定义一个feign接口
package com.spring.cloud.itic.feign.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "eurekaclient",fallback = SchedualServiceHiHystric.class)
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
public String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
- FeginApplication
package com.spring.cloud.itic.feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient //注册到eureka
@EnableFeignClients //启用Fegin客户端
//@EnableHystrixDashboard //开启熔断器监控
public class FeginApplication {
public static void main(String[] args) {
SpringApplication.run(FeginApplication.class, args);
}
}
熔断器(hystrix)
如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
1.使用熔断器防止服务雪崩
2.feign自带熔断器,需要开启。
- Feign是自带断路器的,在D版本的Spring Cloud之后,它没有默认打开。需要在配置文件中配置打开它,在yml最后一行加入
feign:
hystrix:
enabled: true
- 在FeignClient的SchedualServiceHi接口的注解中加上fallback的指定类就行了
@FeignClient(value = "eurekaclient",fallback = SchedualServiceHiHystric.class)
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
public String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
@Component
public class SchedualServiceHiHystric implements SchedualServiceHi {
@Override
public String sayHiFromClientOne(@RequestParam(value = "name") String name) {
return "sorry :fallback _" + name;
}
}
仪表盘监控
使用熔断器仪表盘监控,就是监控熔断情况
断流器Hystrix不仅提供了微服务的错误与延迟处理机制,还提供了仪表盘用于监控各个微服务程序的健康状态
- 添加健康监控。对于每个Eureka客户端(微服务应用程序),默认采用心跳机制确认健康状态,通过启用actuator
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 新建dashboard项目管理仪表盘监控
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!-- 仪表盘服务程序 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<!-- 程序质量监控服务,也可忽略,但是 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- DashboardApplication.java添加注解
@EnableHystrixDashboard //开启熔断器监控
- 访问
http://localhost:8766/hystrix
- 通过Hystrix Dashboard主页面的文字介绍,我们可以知道,Hystrix Dashboard共支持三种不同的监控方式
☞默认的集群监控:通过URL:http://turbine-hostname:port/turbine.stream开启,实现对默认集群的监控。
☞指定的集群监控:通过URL:http://turbine-hostname:port/turbine.stream?cluster=[clusterName]开启,实现对clusterName集群的监控。
☞单体应用的监控:通过URL:http://hystrix-app:port/hystrix.stream开启,实现对具体某个服务实例的监控。
路由网关(Zuul)
使用路由网关统一访问接口。
zuul 主要功能是路由转发和过滤器, 以及当路由服务可以访问,但是因为网络或其他原因服务访问失败时,可以配置路由失败回调,不将500,404一些服务问题暴漏给客户端。
1.1 路由转发实现
- pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
- application 类入口加入标签
@EnableZuulProxy
- yml配置
# 注册到 eureka server 配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8765
spring:
application:
## 需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name
name: zuul
zuul:
routes:
# api-a:
# path: /api-a/**
# serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
1.2. 过滤器实现
- 新建MyFilter.java
package com.spring.cloud.itic.zipkin.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyFilter extends ZuulFilter {
/**
* filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
* pre:路由之前
* routing:路由之时
* post: 路由之后
* error:发送错误调用
*/
@Override
public String filterType() {
return "pre";
}
//过滤的顺序
@Override
public int filterOrder() {
return 0;
}
//这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
@Override
public boolean shouldFilter() {
return true;
}
//过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
@Override
public Object run() throws ZuulException {
System.out.println("==============================================================");
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
System.out.println("token is empty!!!!");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
System.out.println("ok");
return null;
}
}
分布式配置中心(SpringCloud Config)
服务过多,配置文件统一管理
Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。
1.1 服务端(端口默认8888)
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- Application.java入口加入注解
@EnableConfigServer // springcloud config server的注解
- yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8888
spring:
application:
name: service-config
#feign:
# hystrix:
# enabled: true
cloud:
config:
server:
git:
uri: https://github.com/15130125905/spring-cloud-config.git #git仓库地址,就是刚才创建的git仓库
skipSslValidation: true #跳过校验
basedir: E:///config-center///config #从git仓库拉取到的文件在本地存储的位置,可自行修改或删掉,默认存储在C盘
# bootstrap: true
1.2 客户端
- pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- bootstrap.yml
spring:
application:
# 对应config server所获取的配置文件的 {application}
name: application
cloud:
config:
# 指 config server 地址
uri: http://localhost:8888/
# 指定 config server 里的 {profile}
profile: feignDev
# 指定Git仓库分支,对应config server的{label}
label: master
服务链路追踪(ZipKin)
调用链分析
- 默认端口9411
- pom
<properties>
<zipkin-version>2.11.8</zipkin-version>
</properties>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>${zipkin-version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>${zipkin-version}</version>
</dependency>
- yml
# 注册到 eureka server 配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 9411
spring:
application:
## 需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name
name: zipkin
management:
metrics:
web:
server:
#避免访问zipkin页面报java.lang.IllegalArgumentException
auto-time-requests: false
- Applicatoin入口
@EnableZipkinServer
服务监控(SpringBoot Admin)
状态收集, 健康检查
Spring Boot Admin是用来管理和监视您的Spring Boot应用程序的。应用程序需要注册为客户端,UI由AngularJs开发。
启动顺序
- 注册与发现
- 分布式配置中心
- 服务提供者
- 服务消费者
- API网关
代码
https://github.com/15130125905/springcloud.git