介绍
spring cloud 是一系列框架的集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。
- eureka 微服务治理,服务注册和发现
- ribbon 负载均衡、请求重试
- hystrix 断路器,服务降级、熔断
- feign ribbon + hystrix 集成,并提供声明式客户端
- hystrix dashboard 和 turbine hystrix 数据监控
- zuul API 网关,提供微服务的统一入口,并提供统一的权限验证
- config 配置中心
- bus 消息总线, 配置刷新
- sleuth+zipkin 链路跟踪
Spring Cloud 对比 Dubbo
-
Dubbo
- Dubbo只是一个远程调用(RPC)框架
- 默认基于长连接,支持多种序列化格式
-
Spring Cloud
- 框架集
- 提供了一整套微服务解决方案(全家桶)
- 基于http调用, Rest API
配置eureka 服务
添加 依赖 eureka server
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<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>
application.yml
spring:
application:
name: eureka-server
server:
port: 2001
eureka:
server:
enable-self-preservation: false # 关闭保护模式 方便测试
instance:
hostname: eureka1 # 在注册中心的名字
client:
register-with-eureka: false
fetch-registry: false
- eureka 集群服务器之间,通过 hostname 来区分
- eureka.server.enable-self-preservation
eureka 的自我保护状态:心跳失败的比例,在15分钟内是否超过85%,如果出现了超过的情况,Eureka Server会将当前的实例注册信息保护起来,同时提示一个警告,一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务
- eureka.client.register-with-eureka=false
不向自身注册 - eureka.client.fetch-registry=false
不从自身拉取注册信息 - eureka.instance.lease-expiration-duration-in-seconds
最后一次心跳后,间隔多久认定微服务不可用,默认90
在主程序上添加 @EnableEurekaServer
package cn.tedu.sp05;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class Sp05EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(Sp05EurekaApplication.class, args);
}
}
eureka
注册中心 工作 机制
1. 服务提供者注册
一次一次的不提注册 直到注册成功
2. 心跳
eureka 提供者每30秒 向注册中心 发送一次心跳 如果eureka连续3次 接收不到 该服务的心跳 则会删除该服务
3. 消费者 拉取列表
每30秒 会从注册中心拉取一次列表 刷新列表
4.自我保护模式
是一种特殊情况,如果15分钟内,85%以上服务器都出现心跳异常(可能是网络不稳定),eureka会进入自我保护模式,所 有的注册信息都不删除。等待网络恢复后,可以自动退出保护模式
开发期间,为了影响测试,可以先关闭保护模式
eureka的高可用 搭建集群
因为需要向另一个eureka注册中心注册 所以也需要 添加 eureka 提供者 依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<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>
hosts
127.0.0.1 eureka1
127.0.0.1 eureka2
spring:
application:
name: eureka-server
server:
port: 2001
eureka:
server:
enable-self-preservation: false # 关闭自动保护模式
instance:
hostname: eureka1
client:
register-with-eureka: false # 关闭向注册中心注册自己
fetch-registry: false # 关闭向注册中心拉去信息
---
spring:
profiles: eureka1
server:
port: 2001
eureka:
server:
enable-self-preservation: true
instance:
hostname: eureka1
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka2:2002/eureka
---
spring:
profiles:
- eureka2
server:
port: 2002
eureka:
instance:
hostname: eureka2
client:
service-url:
defaultZone: http://eureka1:2001/eureka
register-with-eureka: true
fetch-registry: true
通过 idea 启动配置 在 启动页面 编辑参数
--spring.profiles.active=eureka1 --server.port=2001
--spring.profiles.active=eureka2 --server.port=2002
eureka 提供者
- 添加 eureka 提供者依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.编辑yml配置文件 向eureka 注册
spring:
application:
name: item-service # 将作为eureka查询地址的key
server:
port: 8001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
服务消费者
添加 依赖 eureka 消费者依赖 eureka-client 中已经包含 ribbon 依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<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>
添加配置
spring:
application:
name: ribbon
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
RestTemplate
RestTemplate 是SpringBoot提供的一个Rest远程调用工具 可直接进行远程调用 走的是http协议
它的常用方法有
getForObject() - 执行get请求
postForObject() - 执行post请求
ribbon
对 RestTemplate 进行封装、增强,添加了负载均衡和重试功能
package com.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
//@EnableDiscoveryClient 可以省略
@SpringBootApplication
public class Sp06RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(Sp06RibbonApplication.class, args);
}
//创建 RestTemplate的实例 交给spring管理
@Bean
public RestTemplate restTemplate(){
//设定重连
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
//设定超时 时间
f.setConnectTimeout(1000);
f.setReadTimeout(1000);
//将重连z注入给 RestTemplate
return new RestTemplate(f);
}
}
package com.tedu.sp06;
import com.tedu.sp01.pojo.Item;
import com.tedu.sp01.pojo.Order;
import com.tedu.sp01.pojo.User;
import com.tedu.sp01.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@Slf4j
@RestController
public class RestTemplateController {
/**
* RestTemplate 是springboot 提供的远程一种工具 走的是http协议 可以将返回的数据转换成java对象
* 常用的有两种方法
* restTemplate.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
* 第一个参数为url路径 其中{1} 是 template规定的占位符 表达式 1 2 3表示序号 参数顺序 需要在第三个参数中 具体的传参
* 例如 restTemplate.getForObject("http://localhost:8101/{1}/score?score={2}",
* JsonResult.class,
* userId, score);
* post提交:
* restTemplate.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
*
*httpclient 偏底层工具
* restTemplate 高度封装的工具
*rabbit 对restTemplate的进一步增强 实现负载均衡 和 重试功能
* rrabbit增强 负载均衡的实现
* 1. 在restTemplate 方法上加上 @LoadBalanced 注解 为服务添加负载均衡
* 2. 将地址信息 改成serviceName(提供者的名字) 直接从注册中心中获取服务者的地址信息
* 实现负载均衡 例如http://localhost:8001/{1} http://item-service/{1}*
*rabbit增强 重试的实现
*/
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/item-service/{orderId}")
@LoadBalanced // 添加负载均衡 rabbit 增强
public JsonResult<List<Item>> checkItem(@PathVariable String orderId){
//RestTemplate 定义的占位符格式http://localhost:8001/{1}
//"http://item-service/{1}" 直接从注册表 根据 名字 获取注册表 获取主机地址列表 实现负载均衡
JsonResult result= restTemplate.getForObject("http://item-service/{1}", JsonResult.class, orderId);
log.info("调用后台服务器成功");
return result;
}
@RequestMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items){
JsonResult result=restTemplate.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
log.info("远程后台服务器 调用成功");
return result;
}
@GetMapping("/user-service/{userId}")
public JsonResult<List<User>> checkUser(@PathVariable String userId){
JsonResult forObject = restTemplate.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
log.info("远程后台服务器 调用成功");
return forObject;
}
@GetMapping("user-service/{userId}/score")
public JsonResult<List<User>> checkUser1(@PathVariable String userId,Integer score){
JsonResult result = restTemplate.getForObject("http://localhost:8101/{1}/score?score={2}",
JsonResult.class,
userId, score);
return result;
}
@GetMapping("/order-service/{orderId}")
public JsonResult<List<Order>> checkOrder(@PathVariable String orderId){
JsonResult forObject = restTemplate.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
log.info("远程后台服务器 调用成功");
return forObject;
}
@GetMapping("/order-service")
public JsonResult<List<Order>> checkOrder1(){
JsonResult forObject = restTemplate.getForObject("http://localhost:8201", JsonResult.class, "");
log.info("远程后台服务器 调用成功");
return forObject;
}
}
feign
集成工具,集成了
- 远程调用
- ribbon
- hystrix
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在主启动类上添加注解@EnableFeignClients
package com.tedu.sp09;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableCircuitBreaker //启用断路器
@EnableFeignClients //启用feign 客户端
@SpringBootApplication
public class Sp09FeignApplication {
public static void main(String[] args) {
SpringApplication.run(Sp09FeignApplication.class, args);
}
}
远程调用
Feign 提供了一个声明式客户端接口,只需要定义一个接口,就可以通过接口远程调用服务,而不用写具体的代码
示例:
package com.tedu.sp09.feign;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import com.tedu.sp09.FeignImpl.ItemFeignClientFB;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name = "item-service",fallback = ItemFeignClientFB.class) //定义从注册表 拉取得是那个路径信息
public interface ItemFeignClient {
@GetMapping("/{orderId}") //定义访问的路径
JsonResult<List<Item>> getItems(@PathVariable String orderId);
@GetMapping("/{decreaseNumber}")
JsonResult devrease(@RequestBody List<Item> items);
}
@FeignClient() 注解 配合 @GetMapping 注解使用
其中 @FeignClient 注解中的name属性 指向的是 具体调用的服务名称 从而通过 eureka注册中心获取 该服务的地址,而@GetMapping注解主要作用就是通过该注解指定 具体访问该服务的那个路径
如 a服务远程调用b服务 则可以在a服务中声明Feign接口 通过@FeignClient注解定位到 b 服务 然后通过@GetMapping 中的路径决定具体的调用路径
// a服务 Controller
@Slf4j
@RestController
public class feignController {
@Autowired
private ItemFeignClient itemFeignClient;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId){
System.out.println("======");
return itemFeignClient.getItems(orderId);
}
负载均衡 与 重连 feign整合ribbon
无需任何配置 ,feign默认启用了 负载均衡和重连
重试 默认参数值如下, 如需修改 在yml配置文件中修改即可
- MaxAutoRetries: 0 # 最大重连次数 0 一台服务器 终会有初始连接(初始连接不计算在连接次数内)
- MaxAutoRetriesServer: 1 #重连更换的 服务器台数 为1台
- ReadTimeout:1000 # 秒 feign整合ribbon后 超时设定 不用再具体方法中设定 在配置文件中设定即可
配置文件 还可以配置到具体的服务:
# 所有服务都有效
ribbon:
MaxAutoRetries: 1
# 只对 item-service 有效
item-service:
ribbon:
MaxAutoRetries: 2
整合hystrix
feign 默认不启用 Hystrix ,不推荐启用 Hystrix
如需启用 则需要添加如下配置
- 添加 hystrix的完整依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在springBoot的主启动类上加上注解@EnableCircuitBreaker 表示启动断路器
package com.tedu.sp09;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableCircuitBreaker //启用断路器
@EnableFeignClients //启用feign 客户端
@SpringBootApplication
public class Sp09FeignApplication {
public static void main(String[] args) {
SpringApplication.run(Sp09FeignApplication.class, args);
}
}
- 在yml配置启用hystrix : feign.hystrix.enabled=true
feign:
hystrix:
enabled: true #启用hystrix
- 添加降级代码
在 @FeignClient注解中 添加属性 fallback=降级类.class
package com.tedu.sp09.feign;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import com.tedu.sp09.FeignImpl.ItemFeignClientFB;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name = "item-service",fallback = ItemFeignClientFB.class) //定义从注册表 拉取得是那个路径信息
public interface ItemFeignClient {
@GetMapping("/{orderId}") //定义访问的路径
JsonResult<List<Item>> getItems(@PathVariable String orderId);
@GetMapping("/{decreaseNumber}")
JsonResult devrease(@RequestBody List<Item> items);
}
- 创建 降级类 实现声明式客户端接口
package com.tedu.sp09.FeignImpl;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.web.util.JsonResult;
import com.tedu.sp09.feign.ItemFeignClient;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ItemFeignClientFB implements ItemFeignClient {
@Override
public JsonResult<List<Item>> getItems(String orderId) {
return JsonResult.err().msg("查询 item失败");
}
@Override
public JsonResult devrease(List<Item> items) {
return JsonResult.err().msg("删除item 失败");
}
}
监控 (Hystrix监控)
- 添加 监控依赖 actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 暴露监控端点
management:
endpoints:
web:
exposure:
include: hystrix.stream # * 代表暴露所有端点
- 重启项目 调用后台服务
- 查看 监控端点
http://localhost:3001/actuator/hystrix.stream // 根据个人配置访问 - 访问仪表盘
http://localhost:4001/hystrix // 根据个人配置访问
Hystrix Dashboard 仪表盘
仪表盘 是一款 对hystrix 降级和熔断的监控 ,用图标的方式直观的看到系统的错误情况
暴露监控数据
使用 actuator 工具,暴露监控数据
actuator 是由 spring boot 提供的工具,可以暴露项目中的多种监控数据,例如:
- spring容器中的所有对象
- spring MVC的所有映射路径
- 将康状态
- 环境变量
- …
Hystrix就是利用 actuator 来暴露自己的降级和熔断监控
actuator 的使用
-
添加actuator依赖
-
配置暴露的监控端点:
m.e.w.e.i="*" : 暴露所有的监控端点 m.e.w.e.i=health : 暴露健康状态端点 m.e.w.e.i=['health', 'mappings', 'beans'] :暴露多个监控端点
management:
endpoints:
web:
exposure:
include: hystrix.stream # * 代表暴露所有端点
management:
endpoints:
web:
exposure:
include: "*" # 要有双引号
- 启动服务
通过 访问服务的下一级路径 查看 监控的端点 如果 服务端口是3001
http://localhost:3001/actuato 来查看所有的监控
搭建仪表盘
仪表盘项目可以是一个完全独立的项目,可以不连接注册中心,不用注册,也不用发现其他服务
需要监控那个项目 只需要加上那个项目的路径 即可
- 新建 springboot项目 添加 hystrix dashboard 仪表盘依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- 添加主程序注解: @EnableHystrixDashboard
package com.tedu.sp08;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@EnableHystrixDashboard
@SpringBootApplication
public class Sp08HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(Sp08HystrixDashboardApplication.class, args);
}
}
- 启动项目 用浏览器 访问下属路径 hystrix 即可