介绍 |
---|
Spring Cloud是一系列技术的有序整合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发。 |
Spring Boot擅长的是集成,把世界上最好的框架集成到自己项目中,Spring Cloud也是一样,把非常流行的微服务的技术整合到一起。 |
Spring Cloud从架构层面上降低了对大型系统构建的要求和难度,使我们以非常低的成本(技术或者硬件)搭建一套高效、分布式、容错的平台,但Spring Cloud也不是没有缺点,小型独立的项目不适合使用。 |
基础架构图
SpringCloud与SpringBoot版本匹配关系
SpringBoot | SpringCloud |
---|---|
1.2.x | Angel版本 |
1.3.x | Brixton版本 |
1.4.x | Camden版本 |
1.5.x | Dalston版本、Edgware |
2.0.x | Finchley版本 |
2.1.x | Greenwich GA版本 (2019年2月发布) |
注册中心 Spring Cloud Eureka
简介:
Eureka解决了第一个问题:服务的管理,注册和发现、状态监管、动态路由。
Eureka负责管理记录服务提供者的信息。服务调用者无需自己寻找服务,Eureka自动匹配服务给调用
者。
Eureka与服务之间通过 心跳 机制进行监控;
原理图
整合注册中心Eureka
1.创建eureka_server的springboot工程。
2.勾选坐标
- Spring Boot DevTools (热部署插件)
- Eureka Server (Eureka服务端)
- 在启动类EurekaServerApplication声明当前应用为Eureka服务使用 @EnableEurekaServer 注解
- 编写配置文件application.yml
# 端口
server:
# 从jvm中拿值,如果没有使用默认配置8001(在VM options中配 -Dport=10086)
port: ${port:10086}
spring:
application:
name: eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId)
# 服务注册中心
eureka:
client:
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
defaultZone: http://127.0.0.1:10086/eureka
# 是否抓取注册列表
fetch-registry: false
# 是否注册服务中心Eureka
register-with-eureka: false
5.启动EurekaServerApplication
6.测试访问地址http://127.0.0.1:10086,如下信息代表访问成功
服务提供者-注册服务中心
1.在服务提供者user_service工程中添加Eureka(客户端)依赖
<!--eureka客户端starter-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--SpringCloud所有依赖管理的坐标-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.在启动类上开启Eureka客户端发现功能 @EnableDiscoveryClient
3.修改配置文件:spring.application.name指定应用名称,作为服务ID使用
server:
# 从jvm中拿值,如果没有使用默认配置8001(在VM options中配 -Dport=8001)
port: ${port:8001}
spring:
application:
name: provider-service # 应用名称,会在Eureka中作为服务的id标识
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://127.0.0.1/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
# mybatis配置
mybatis:
# 扫描接口对应的XML映射文件
mapper-locations: classpath:mapper/*.xml
# 起别名
type-aliases-package: top.zhenghy123.pojo
# 配置eureka
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
4.客户端代码会自动把服务注册到EurekaServer中
5.在Eureka监控页面可以看到服务注册成功信息
服务消费者-注册服务中心
1.在服务消费者spring_cloud_itcast_consumer_service工程中添加Eureka(客户端)依赖
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--SpringCloud所有依赖管理的坐标-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.在启动类开启Eureka客户端 @EnableDiscoveryClient
3.修改配置文件:加入EurekaServer地址
server:
# 从jvm中拿值,如果没有使用默认配置8001(在VM options中配 -Dport=9002)
port: ${port:9002}
spring:
application:
name: provider-service # 应用名称,会在Eureka中作为服务的id标识
# 配置eureka
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
# 配置访问(PROVIDER-SERVICE)服务的负债均衡策略
# RandomRule随机策略
# RoundRobinRule轮询策略
PROVIDER-SERVICE:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
feign:
hystrix:
# 开启feign熔断器的支持
enabled: true
compression:
# 请求压缩
request:
enabled: true
# 起步压缩大小
min-request-size: 1024
# 请求压缩的数据类型
mime-types: text/xml,application/xml,josn
# 响应压缩
response:
enabled: true
4.启动服务,在服务中心查看是否注册成功
消费者通过Eureka访问提供者
什么是Feign:
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
- Feign 采用的是基于接口的注解
- Feign 整合了ribbon
1.添加Feign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.在启动类上加入@EnableFeignClients注解开启Feign的功能
3.定义一个UserService接口,通过@ FeignClient(value=“服务名”,fallback = 指定熔断降级处理类),来指定调用哪个服务。比如在代码中调用了provider-service服务的“/user/findAll”接口
@ FeignClient的fallback属性:fallback指定熔断降级处理类
编写一个类实现此接口在类上加@Component注解即可(下方Feign使用Hystrix实现降级有介绍)
代码如下:
@FeignClient(value = "PROVIDER-SERVICE", fallback = UserServiceImpl.class)
//value为目标服务的ID,fallback指定熔断降级处理类 编写一个类实现此接口在类上加@Component注解即可(下方**Feign使用Hystrix实现降级**有介绍)
public interface UserService {
@RequestMapping("/user/findAll")
List<User> findAll();
}
4.在controller层,注入UserService来消费服务
@RestController
@RequestMapping("test")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private UserService userService;
@RequestMapping("findAll")
public String findAll() {
return restTemplate.getForObject("http://PROVIDER-SERVICE/user/findAll", String.class);
}
@RequestMapping("test")
public List test(){
return userService.findAll();
}
}
Feign使用Hystrix实现降级
服务雪崩
如图-1,如果所有服务A/B/C网络/应用等都正常,同时能够快速的处理请求并返回,服务之间就可以正常的调度。
如图-2,如果A服务出现异常情况(网络异常/内部数据库异常等)无法在指定的时间内返回B服务结果,那么由于C服务会持续的请求B服务,最终导致B服务积压了大量的请求而服务器奔溃不可用。
如图-3,B服务由于A服务的不可用导致自身请求积压而崩溃,所以所有的请求蔓延到下游的C服务,最终也导致C服务不可用崩溃,如图-4所示。
综上所述:服务雪崩就是由于上游(例如A)的服务异常不可用,最终蔓延到下游的所有服务,导致所有相关下游的微服务应用不可用而系统崩溃。
Hystrix简介
Hystrix是由Netfix开源的一个延迟和容错工具库,用于解决远程调用/服务或者第三方库防止级联失败提示系统的可用性和容错性。
1.Maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.修改配置文件
feign:
hystrix:
# 开启feign熔断器的支持
enabled: true
compression:
# 请求压缩
request:
enabled: true
# 起步压缩大小
min-request-size: 1024
# 请求压缩的数据类型
mime-types: text/xml,application/xml,josn
# 响应压缩
response:
enabled: true
3.添加降级类
Feign服务熔断降级其实是通过调度超时就调用本地的方法,而不是一直等待服务返回。Feign调度服务是通过接口+注解方式的,我们需要实现该接口用于实现服务降级调用本地方法。
@Component
public class UserServiceImpl implements UserService {
@Override
public List findAll() {
ArrayList<String> list = new ArrayList<>();
list.add("服务器异常");
return list;
}
}
Feign接口类
主要是通过@FeignClient的fallback属性定义降级后调用那个类的方法,这里方法名称等都是一致才可以。
@FeignClient(value = "PROVIDER-SERVICE", fallback = UserServiceImpl.class)
public interface UserService {
@RequestMapping("/user/findAll")
List<User> findAll();
}
负载均衡 Spring Cloud Ribbon
Ribbon 简介
解决了集群服务中,多个服务高效率访问的问题。
什么是Ribbon?
Ribbon是Netflix发布的负载均衡器,有助于控制HTTP客户端行为。为Ribbon配置服务提供者地址列表
后,Ribbon就可基于负载均衡算法,自动帮助服务消费者请求。
Ribbon默认提供的负载均衡算法:轮询,随机其他…。当然,我们可用自己定义负载均衡算法
实现负载均衡访问用户服务。
如果想要做负载均衡,我们的服务至少2个以上。所有第一步目标
步骤:
- 启动多个user_service服务
修改UserServiceApplication的application.yml配置文件
server:
# 从jvm中拿值,如果没有使用默认配置8001(在VM options中配 -Dport=8001)
port: ${port:8001}
-
编辑应用启动配置
-
复制一份UserServiceApplication
-
启动多个Application应用
-
Eureka服务中心可查看,注册成功.
开启消费者调用负载均衡
Feign和Eureka都默认集成了Ribbon,所以无需引入依赖。
注:这里使用的是Feign默认实现了负载均衡的效果
- 直接在配置文件中配置即可
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
# 配置访问(PROVIDER-SERVICE)服务的负债均衡策略
# RandomRule随机策略
# RoundRobinRule轮询策略
PROVIDER-SERVICE: #对应的服务ID
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
网关 Spring Cloud Gateway
简介
Spring Cloud Gateway 是Spring Cloud团队的一个全新项目,基于Spring
5.0、SpringBoot2.0、 Project Reactor 等技术开发的网关。 旨在为微服务架构提供一种简单有效统一的REST 请求路由管理方 式。 Spring Cloud Gateway 作为SpringCloud生态系统中的网关,目标是替代Netflix
Zuul。Gateway不仅 提供统一路由方式,并且基于Filter链的方式提供网关的基本功能。例如:安全,监控/指标,和限流。
本身也是一个微服务,需要注册到Eureka
网关的核心功能:过滤(权限)、路由
核心概念:
- List item 路由(route):
- 断言Predicate函数:路由转发规则
- 过滤器(Filter):
- 不管是来自客户端的请求,还是服务内部调用。一切对服务的请求都可经过网关。
- 网关实现鉴权、动态路由等等操作。
- Gateway是我们服务的统一入口
搭建网关微服务,实现服务路由分发
实现步骤:
- 创建SpringBoot工程gateway_server
- 勾选starter:网关、Eureka客户端
- 编写基础配置
- 编写路由规则
- 启动网关服务进行测试
实现过程:
-
创建SpringBoot工程gateway_server
-
勾选Starter:网关、Eureka客户端
-
启动引导类加@EnableDiscoveryClient注解开启注册中心Eureka客户端发现
-
编写基础配置
server:
# 从jvm中拿值,如果没有使用默认配置8001(在VM options中配 -Dport=8001)
port: ${port :10010} #模板问题¥需要改成英文 $
spring:
application:
name: api-gatewat # 应用名称,会在Eureka中作为服务的id标识(serviceId)
# 服务注册中心
eureka:
client:
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
defaultZone: http://127.0.0.1:10086/eureka
- 编写路由规则
需要用网关来路由user_service服务,查看服务ip和端口
修改gateway_server的配置文件application.yml,配置网关内容
spring:
cloud:
# 配置网关内容
gateway:
# 路由si(集合)
routes:
# id唯一标识(路由id,可以随便写)
- id: consumer-service-route
# 路由服务地址
uri: http://127.0.0.1:9002
# 断言
predicates:
- Path=/user/**
将符合 path 规则的请求,路由到 uri 参数指定地址。
举例:http://localhost:10010/user/findById?id=1 路由转发到http://localhost:9091/user/findById?id=1
- 启动GatewayApplication进行测试
访问路径中,必须包含路由规则的映射路径/user才会被路由
动态路由
刚才路由规则中,我们把路径对应服务地址写死了!如果服务提供者集群的话,这样做不合理。应该是
根据服务名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由!
- 修改映射配置:通过服务名称获取
因为已经配置了Eureka客户端,可以从Eureka获取服务的地址信息,修改application.yml文
件如下
# 注解版
spring:
cloud:
# 配置网关内容
gateway:
# 路由si(集合可以配置多个服务)
routes:
# id唯一标识(路由id,可以随便写)
- id: consumer-service-route
# 路由服务地址
# uri: http://127.0.0.1:9002
# 采用lb协议,会从Eureka注册中心获取服务请求地址
# 路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务(默认使用随机的策略)
# 规则:lb协议+服务名称
uri: lb://consumer-service
# 断言
predicates:
- Path=/test/**
路由配置中uri所用的协议为lb时,gateway将把user-service解析为实际的主机和端口,并通
过Ribbon进行负载均衡。
- 启动GatewayApplication测试
这次gateway进行路由时,会利用Ribbon进行负载均衡访问。日志中可以看到使用了负载均
衡器。
路由前缀
第一:添加前缀:
在gateway中可以通过配置路由的过滤器PrefixPath 实现映射路径中的前缀添加。可以起到隐藏接口地
址的作用,避免接口地址暴露。
- 配置请求地址添加路径前缀过滤
cloud:
# 配置网关内容
gateway:
# 路由si(集合可以配置多个服务)
routes:
# id唯一标识(路由id,可以随便写)
- id: consumer-service-route
# 采用lb协议,会从Eureka注册中心获取服务请求地址
# 路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务(默认使用随机的策略)
# 规则:lb协议+服务名称
uri: lb://consumer-service
# 断言
predicates:
- Path=/**
# 请求地址添加路径前缀过滤器
filters:
- PrefixPath=/test
- 重启GatewayApplication
- 配置完成的效果:
第二:去除前缀:
在gateway中通过配置路由过滤器StripPrefix,实现映射路径中地址的去除。通过StripPrefix=1来指定
路由要去掉的前缀个数。如:路径/api/user/1将会被路由到/user/1。
- 配置去除路径前缀过滤器
cloud:
# 配置网关内容
gateway:
# 路由si(集合可以配置多个服务)
routes:
# id唯一标识(路由id,可以随便写)
- id: consumer-service-route
# 采用lb协议,会从Eureka注册中心获取服务请求地址
# 路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务(默认使用随机的策略)
# 规则:lb协议+服务名称
uri: lb://consumer-service
# 断言
predicates:
- Path=/api/test/**
# 去除路径前缀过滤器
filters:
- StripPrefix=1 # 写1会去除 /api
- 重启GatewayApplication
- 访问查看效果
过滤器
简介:过滤器作为网关的其中一个重要功能,就是实现请求的鉴权。前面的(路由前缀和去除前缀)章节中的功能也是使用过滤器实现的。
Gateway自带过滤器有几十个,常见自带过滤器有:
过滤器名称 | 说明 |
---|---|
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameters | 对匹配上的请求路由 |
AddResponseHeader | 对从网关返回的响应添加Header |
StripPrefix | 对匹配上的请求路径去除前缀 |
PrefixPath | 对匹配上的请求路径添加前缀 |
详细说明官方链接
使用场景:
- 请求鉴权:如果没有访问权限,直接进行拦截 (最常用)
- 异常处理:记录异常日志
- 服务调用时长统计
过滤器配置
**过滤器类型:**Gateway有两种过滤器
- 局部过滤器:只作用在当前配置的路由上。
- 全局过滤器:作用在所有路由上。
配置全局过滤器:
对输出的响应设置其头部属性名称为i-love,值为yuge
- 修改配置文件
cloud:
# 配置网关内容
gateway:
# 配置全局默认过滤器
default-filters:
# 往响应过滤器中加入信息
- AddResponseHeader=i-love,yuge
自定义全局过滤器
需求:模拟一个登录的校验。基本逻辑:如果请求中有token参数,则认为请求有效,放行。
实现步骤:
- 在gateway_server工程编写全局过滤器类MyGlobalFilter
- 编写业务逻辑代码:判断如果包含token值,则放行请求,如果不包含则拦截
- 访问接口测试,加token和不加token。
实现过程: - 在gateway_server工程编写全局过滤器类MyGlobalFilter
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//1、获取参数中的token,以及token的值
String token = request.getQueryParams().getFirst("token");
//2、如果token的值为空,则拦截
if (StringUtils.isBlank(token)) {
//响应未授权状态码401
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//拦截
return response.setComplete();
}
//放行
return chain.filter(exchange);
}
/**
* 定义过滤器执行顺序
* 返回值越小,越靠前执行
*
* @return
*/
@Override
public int getOrder() {
return 0;//
}
}
- 在全局过滤器里面配上自定义的过滤器即可
cloud:
# 配置网关内容
gateway:
# 配置全局默认过滤器
default-filters:
- MyGlobalFilter
-
访问:http://localhost:10010/api/user/findById?id=1
-
访问:http://localhost:10010/api/user/findById?id=1&token=11