天天看点

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

目录

⼀、单体应⽤存在的问题

1.1 商城项⽬架构

1.2 单体项⽬存在的问题

⼆、微服务架构

2.1 微服务架构概念

2.2 微服务架构优点

2.3 微服务架构缺点

三、微服务架构开发需要解决的问题

3.1 服务之间如何相互发现?

3.2 服务之间如何进⾏通信?

3.2.1 同步调⽤

3.2.2 异步调⽤

3.3 服务挂了该如何解决?

3.3.1 服务故障雪崩

3.3.2 如何解决服务故障雪崩

3.4 客户端如何统⼀访问多个接⼝服务?

四、微服务架构框架

4.1 主流的微服务架构框架

4.2 SpringCloud简介

4.3 Spring Cloud核⼼组件

4.4 SpringCloud版本介绍

五、搭建服务注册与发现中⼼

5.1 创建SpringBoot应⽤,添加依赖

5.2 配置服务注册与发现中⼼

5.3 在启动类添加@EnableEurekaServer注解

5.4 运⾏及访问

 六、服务注册

6.1 创建SpringBoot应⽤

6.2 注册服务

6.2.1 添加依赖

6.2.2 配置application.yml

6.2.3 在当前服务应⽤的启动类添加 @EnableEurekaClient 注解

七、服务发现-Ribbon

7.1 基础配置

7.1.1 创建SpringBoot应⽤,添加依赖

7.1.2 配置application.yml

7.1.3 在启动类添加 @EnableDiscoveryClient 注解

7.2 服务调⽤

7.2.1 配置RestTemplate

7.2.2 在Service中注⼊RestTemplate对象调⽤服务

7.3 案例流程图

7.4 Ribbon服务调⽤说明

⼋、基于Ribbon进⾏服务调⽤的参数传递

8.1 RestTemplate发送调⽤请求的⽅法

8.2 put/post请求传参

8.3 get请求传参

九、服务发现-Feign

9.1 基础配置

9.1.1 创建SpringBoot应⽤,添加依赖

9.1.2 配置application.yml

9.1.3 在启动类添加注解

9.2 服务调⽤

9.2.1 创建Feign客户端

9.2.2 使⽤Feign客户端调⽤服务

9.3 Feign传参

9.3.1 POST请求

9.3.2 Get请求

⼗、服务注册与发现中⼼的可靠性和安全性

10.1 可靠性

10.2 安全性

10.2.1 添加SpringSecurity的依赖

10.2.3 配置Spring Security

10.2.4 服务提供者和服务消费者连接到注册中⼼都要帐号和密码

⼗⼀、熔断器-Hystrix

11.1 熔断器介绍

11.2 熔断器的原理

11.3 基于Ribbon服务调⽤的熔断器使⽤

11.3.1 服务消费者的 服务降级

11.3.2 服务提供者的 服务降级

11.3.3 服务熔断配置

11.4 基于Feign服务调⽤的熔断器使⽤

11.4.1 Feign中的熔断器使⽤

11.4.2 Ribbon 参数配置

11.5 熔断器仪表盘监控

11.5.1 搭建熔断器仪表盘

11.5.2 配置使⽤了熔断器的服务可被监控

⼗⼆、微服务拆分

⼗三、服务链路追踪

13.1 服务追踪说明

13.2 Zipkin

13.3 搭建zipkin服务器

13.4 服务中Sleuth配置

13.5 zipkin服务数据存储

⼀、单体应⽤存在的问题

1.1 商城项⽬架构

为了解决⾼并发问题,我们的 Tomcat 采⽤了集群部署,但是每个 Tomcat 节点上不是的 依然是单体项⽬(虽然是前后端分离,但是后端采⽤的是单体开发 —— 所有的接⼝都在 同⼀个项⽬中)

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

1.2 单体项⽬存在的问题

⼀个成功的应⽤必然有⼀个趋势:⽤户量会不断增加、项⽬的业务也会不断的扩展 ⽤户量的增加会带来⾼并发的问题,⾼并发问题解决⽅案:

  • 应⽤服务器 --> 单体优化 --> 集群(负载均衡、分布式并发)
  • 数据库服务器 --> 数据库优化 --> 缓存redis --> 分布式数据库

项⽬业务的扩展,也会带来⼀些问题:

  • 项⽬结构越来越臃肿(项⽬结构和代码复杂、项⽬体积逐渐变得庞⼤)
  • 项⽬结构和代码复杂导致项⽬不易维护和⼆次开发、扩展和更新就会变得困难
  • 项⽬体积逐渐变得庞⼤导致启动时间越来越⻓、⽣产⼒⼤受限制
  • 单体应⽤中任何⼀个模块的任何⼀个bug都会导致整个系统不可⽤(单点故障)
  • 复杂的单体项⽬也会带来持续部署的障碍
  • 单体项⽬使得采⽤新的技术和框架会变得困难

⼆、微服务架构

2.1 微服务架构概念

微服务架构是⼀种架构概念,就是将⼀个单体应⽤中的每个功能分解到各个离散的服务 中以实现对单体应⽤的解耦,并提供更加灵活的服务⽀持 从单体到微服务

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

2.2 微服务架构优点

  • 解决了单体项⽬的复杂性问题
  • 每个服务都可以由单独的团队进⾏开发
  • 每个服务都可以使⽤单独的技术栈进⾏开发
  • 每个服务都是独⽴的进⾏部署和维护
  • 每个服务都可以独⽴进⾏扩展

2.3 微服务架构缺点

  • 微服务架构本身就是⼀个缺点:如何把握“微”的粒度;
  • 微服务架构是⼀个分布式系统,虽然单个服务变得简单了,但是服务之间存在相互的调 ⽤,整个服务架构的系统变得复杂了;
  • 微服务架构需要依赖分布式数据库架构;
  • 微服务的单元测试及调⽤变得⽐单体更为复杂;
  • 部署基于微服务架构的应⽤程序变得⾮常复杂;
  • 进⾏微服务架构的应⽤程序开发的技术成本变得更⾼。

三、微服务架构开发需要解决的问题

在微服务架构开发的系统中必然存在很多个服务,服务之间需要相互感知对⽅的存在, 需要进⾏服务间的调⽤,该如何实现呢? —— 进⾏微服务架构开发需要解决的问题: 1. 如此多的服务,服务间如何相互发现? 2. 服务与服务之间该如何通信? 3. 如果某个服务挂了,该如何处理? 4. 前端访问多个不同的服务时该如何统⼀访问路径呢?

3.1 服务之间如何相互发现?

微服务架构 —— 每个服务只处理⼀件事情 / ⼀个步骤,在⼀个复杂的业务中必然会存在服 务间的相互调⽤,服务想要相互调⽤就需要先发现对⽅。 通过服务注册与发现中⼼实现服务间的相互发现

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

服务注册与发现中⼼也是⼀台独⽴服务器 1. 服务提供者在服务注册与发现中⼼进⾏注册 2. 服务注册与发现中⼼进⾏服务记录,并与服务提供者保持⼼跳 3. 服务消费者通过服务注册与发现中⼼进⾏服务查询(服务发现) 4. 服务注册与发现中⼼返回可⽤的服务的服务器地址列表 5. 服务消费者通过负载均衡访问服务提供者

3.2 服务之间如何进⾏通信?

服务消费者在调⽤服务提供者时,⾸先需要通过 服务注册与发现中⼼ 进⾏服务服务查询, 返回服务列表给服务消费者, 服务消费者 通过 LoadBalance 调⽤ 服务提供者 , 那么他们之 间是如何通信呢? —— 数据传输规则 服务与服务间的通信⽅式有 2 种:同步调⽤ 和 异步调⽤

3.2.1 同步调⽤

  • REST(SpringCloud Netflix,SpringCloud Alibaba)
    • 基于HTTP协议的请求和响应
    • 更容易实现、技术更灵活
    • ⽀持多语⾔、同时可以实现跨客户端
    • 适⽤⾯很⼴
  • RPC(Dubbo)
    • 基于⽹络层协议通信
    • 传输效率⾼
    • 安全性更⾼
    • 如果有统⼀的开发规划或者框架,开发效率是⽐较⾼的

3.2.2 异步调⽤

服务间的异步通信通常是通过消息队列实现的

3.3 服务挂了该如何解决?

3.3.1 服务故障雪崩

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

3.3.2 如何解决服务故障雪崩

  • 服务集群——尽量保证每个服务可⽤
  • 服务降级与熔断——避免请求阻塞造成正常的服务出现故障

3.4 客户端如何统⼀访问多个接⼝服务?

通过路由⽹关实现接⼝的统⼀访问

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

四、微服务架构框架

4.1 主流的微服务架构框架

  • Dubbo(阿⾥、开源apache):2012年推出、2014年停更、2015年⼜继续更新
  • Dubbox(当当⽹基于Dubbo的更新)
  • jd-hydra(京东基于Dubbo的更新)
  • SpringCloud Netflix (2016年)/ SpringCloud Alibaba
  • ServiceComb(CSE)华为 2017年

4.2 SpringCloud简介

Spring Cloud 是⼀个基于 SpringBoot 实现的微服务架构应⽤开发框架,它为我们进⾏微 服务架构应⽤开发提供了服务注册与发现、熔断器、⽹关路由、配置管理、负载均衡、 消息总线、数据监控等⼀系列⼯具。 Spring Cloud ⽐较成熟的两个体系:

  • Spring Cloud Netflix
  • Spring Cloud Alibaba

4.3 Spring Cloud核⼼组件

  • Spring Cloud Netflix
    • Eureka 服务注册与发现中⼼,⽤于服务治理
    • Ribbon 服务访问组件、进⾏服务调⽤,实现了负载均衡
    • Hystrix 熔断器,服务容错管理
    • Feign 服务访问组件(对Ribbon和HyStrix的封装)
    • zuul ⽹关组件
  • Spring Cloud Config 配置管理的组件—分布式配置中⼼
  • Spring Cloud Bus 消息总线
  • Spring Cloud Consul 服务注册与发现中⼼(功能类似eureka)

4.4 SpringCloud版本介绍

  • SpringCloud版本 : A-H,2020.0.2
  • SpringCloud的版本对SpringBoot版本时有依赖的
    • A ---- 1.2
    • B ---- 1.3
    • C ---- 1.4
    • D-E ---- 1.5
    • F-G-H ---- 2.x

五、搭建服务注册与发现中⼼

使⽤ Spring Cloud Netflix 中的 Eureka 搭建服务注册与发现中⼼

5.1 创建SpringBoot应⽤,添加依赖

  • spring web
  • eureka server

5.2 配置服务注册与发现中⼼

## 设置服务注册与发现中⼼的端⼝ server : port : 8761 ## 在微服务架构中,服务注册中⼼是通过服务应⽤的名称来区分每个服务的 ## 我们在创建每个服务之后,指定当前服务的 应⽤名 / 项⽬名 spring : application : name : service-eureka eureka : client : ## ip 就是服务注册中⼼服务器的 ip ## port 就是服务注册与发现中⼼设置的 port service-url : defaultZone : http : //192.168.54.59 : 8761/eureka ## 设置服务注册与发现中⼼是否为为集群搭建(如果为集群模式,多个 eureka 节点之间 需要相互注册) register-with-eureka : false ## 设置服务注册与发现中是否作为服务进⾏注册 fetch-registry : false

5.3 在启动类添加@EnableEurekaServer注解

5.4 运⾏及访问

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

 六、服务注册

创建保存订单的服务( order-add )注册到服务注册与发现中⼼

6.1 创建SpringBoot应⽤

创建 spring boot 应⽤,完成功能开发

6.2 注册服务

将能够完成特定业务的 SpringBoot 应⽤作为服务提供者,注册到服务注册与发现中⼼

6.2.1 添加依赖

eureka-server [ 注意版本! ]

<dependency> <groupId> org.springframework.cloud </groupId> <artifactId> spring-cloud-starter-netflix-eureka-server </artifactId> </dependency>

6.2.2 配置application.yml

## 当前服务的 port server : port : 9001 ## 当前应⽤名会作为服务唯⼀标识注册到 eureka spring : application : name : order-add datasource : driver-class-name : com.mysql.jdbc.Driver url : jdbc : mysql : //localhost : 3306/db_2010_sc?characterEncoding=utf-8 username : root password : admin123 mybatis : mapper-locations : classpath : mappers/* type-aliases-package : com.qfedu.order.beans ## 配置 Eureka 服务注册与发现中⼼的地址 eureka : client : service-url : defaultZone : http : //localhost : 8761/eureka

6.2.3 在当前服务应⽤的启动类添加 @EnableEurekaClient 注解

@SpringBootApplication
@MapperScan("com.qfedu.order.dao")
@EnableEurekaClient
public class OrderAddApplication {
 public static void main(String[] args) {
 SpringApplication.run(OrderAddApplication.class, args);
 }
}
           

七、服务发现-Ribbon

服务消费者( api-order-add )通过 eureka 查找服务提供者( order-add ) , 通过服务调⽤ 组件调⽤提供者

  • eureka server
  • ribbon

7.1 基础配置

Ribbon 客户端已经停更进维啦

7.1.1 创建SpringBoot应⽤,添加依赖

  • eureka server
  • ribbon

7.1.2 配置application.yml

server : port : 8001 spring : application : name : api-order-add eureka : client : service-url : defaultZone : http : //localhost : 8761/eureka

7.1.3 在启动类添加 @EnableDiscoveryClient 注解

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

7.2 服务调⽤

7.2.1 配置RestTemplate

@Configuration
public class AppConfig {
 @LoadBalanced //启⽤Ribbon(负载均衡)
 @Bean
 public RestTemplate getRestTemplate(){
 return new RestTemplate();
 }
}
           

7.2.2 在Service中注⼊RestTemplate对象调⽤服务

@Service
public class OrderAddServiceImpl implements OrderAddService {
 @Autowired
 private RestTemplate restTemplate;
 @Override
 public ResultVO saveOrder(Order order) {
 //1. 调⽤ order-add服务进⾏保存
 ResultVO vo = restTemplate.postForObject("http://orderadd/order/add", order, ResultVO.class);
 //2. 调⽤ orderitem-add 保存订单快照
 //3. 调⽤ stock-update 修改商品库存
 //4. 调⽤ shopcart-del 删除购物⻋记录
 return null;
 }
}
           

7.3 案例流程图

服务注册与发现-案例流程图

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

7.4 Ribbon服务调⽤说明

@LoadBalanced 注解是 Ribbon 的⼊⼝,在 RestTemplate 对象上添加此注解之后,再使 ⽤ RestTemplate 发送 REST 请求的时候,就可以通过 Ribbon 根据服务名称从 Eureka 中查 找服务对应的访问地址列表,再根据负载均衡策略(默认轮询)选择其中的⼀个,然后 完成服务的调⽤

  • 获取服务列表
  • 根据负载均衡策略选择服务
  • 完成服务调⽤

⼋、基于Ribbon进⾏服务调⽤的参数传递

8.1 RestTemplate发送调⽤请求的⽅法

SpringCloud 的服务调⽤是基于 REST 的,因此当服务提供者规定了请求的⽅式,服务消 费者必须发送对应⽅式的请求才能完成服务的调⽤, RestTemplate 提供了多个⽅法⽤于 发送不同形式的请求

//post⽅式请求
restTemplate.postForObject();
//get⽅式请求
restTemplate.getForObject();
//delete⽅式请求
restTemplate.delete();
//put⽅式请求
restTemplate.put();
           

8.2 put/post请求传参

  • 服务消费者请求传参
//参数1:访问服务的url
//参数2:传递的对象参数
//参数3:指定服务提供者返回的数据类型
ResultVO vo = restTemplate.postForObject("http://order-add/order/add",
order, ResultVO.class);
           
  • 服务提供者接收参数
@PostMapping("/add")
public ResultVO addOrder(@RequestBody Order order){
 return orderService.saveOrder(order);
}
           

8.3 get请求传参

  • 服务消费者请求传参
String userId = order.getUserId();
ResultVO vo = restTemplate.getForObject("http://order-add/order/add?
userId="+userId, ResultVO.class);
           
  • 服务提供者接收参数
@GetMapping("/add")
public ResultVO addOrder(Order order){
 return orderService.saveOrder(order);
}
@GetMapping("/add")
public ResultVO addOrder(String userId){
 //return orderService.saveOrder(order);
}
           

九、服务发现-Feign

9.1 基础配置

9.1.1 创建SpringBoot应⽤,添加依赖

  • spring web
  • eureka server
  • OpenFeign

9.1.2 配置application.yml

server : port : 8002 spring : application : name : api-order-add-feign eureka : client : service-url : defaultZone : http : //localhost : 8761/eureka

9.1.3 在启动类添加注解

@SpringBootApplication
@EnableDiscoveryClient //声明为服务消费者
@EnableFeignClients //声明启⽤feign客户端
public class ApiOrderAddFeignApplication {
 public static void main(String[] args) {
 SpringApplication.run(ApiOrderAddFeignApplication.class, args);
 }
}
           

9.2 服务调⽤

使⽤ Feign 进⾏服务调⽤的时候,需要⼿动创建⼀个服务访问客户端(接⼝)

9.2.1 创建Feign客户端

@FeignClient("order-add")
public interface OrderAddClient {
 @PostMapping("order/add")
 public ResultVO addOrder(Order order);
}
           

9.2.2 使⽤Feign客户端调⽤服务

@Service
public class OrderAddServiceImpl implements OrderAddService {
 @Autowired
 private OrderAddClient orderAddClient;
 @Override
 public ResultVO saveOrder(Order order) {
 //1. 调⽤ order-add服务进⾏保存
 ResultVO vo = orderAddClient.addOrder(order);
 //2. 调⽤ orderitem-add 保存订单快照
 //3. 调⽤ stock-update 修改商品库存
 //4. 调⽤ shopcart-del 删除购物⻋记录
 return vo;
 }
}
           

9.3 Feign传参

9.3.1 POST请求

  • 通过请求体传递对象
    • 服务提供者
@PostMapping("/add")
public ResultVO addOrder(@RequestBody Order order){
 System.out.println("-------------------order-add");
 System.out.println(order);
 return orderService.saveOrder(order);
}
           
    • 服务消费者(Feign客户端)
@FeignClient("order-add")
public interface OrderAddClient {
 @PostMapping("order/add")
 public ResultVO addOrder(Order order);
} 1234567
           
  • 通过请求⾏传参
    • 服务提供者
@PostMapping("/add")
public ResultVO addOrder(@RequestBody Order order,String str){
 System.out.println("-------------------order-add");
 System.out.println(order);
 System.out.println(str);
 return orderService.saveOrder(order);
}
           
    • 服务消费者(Feign客户端)
//1.对⽤POST请求调⽤服务,Feign客户端的⽅法参数默认为body传值(body只能有⼀个
值)
//2.如果有多个参数,则需要通过@RequestParam声明参数为请求⾏传值
@PostMapping("order/add")
public ResultVO addOrder(Order order,@RequestParam("str") String
str);
           

9.3.2 Get请求

Get 请求调⽤服务,只能通过 url 传参 在 Feign 客户端的⽅法中,如果不指定参数的传值⽅式,则默认为 body 传参, Get 请求也 不例外;因此对于 get 请求传递参数,必须通过 @RequestParam 注解声明

  • 服务提供者
@GetMapping("/get")
public Order addOrder(String orderId){
 return new Order();
}
           
  • 服务消费者(Feign客户端)
@GetMapping("order/get")
public Order getOrder(@RequestParam("orderId") String orderId);
           

⼗、服务注册与发现中⼼的可靠性和安全性

10.1 可靠性

在微服务架构系统中,服务消费者是通过服务注册与发现中⼼发现服务、调⽤服务的, 服务注册与发现中⼼服务器⼀旦挂掉,将会导致整个微服务架构系统的崩溃,如何保证 Eureka 的可靠性呢?

  • 使⽤eureka集群

Eureka 集群搭建 相互注册、相互发现

## 设置服务注册与发现中⼼的端⼝ server : port : 8761 ## 在微服务架构中,服务注册中⼼是通过服务应⽤的名称来区分每个服务的 ## 我们在创建每个服务之后,指定当前服务的 应⽤名 / 项⽬名 spring : application : name : service-eureka eureka : client : ## 设置服务注册与发现中⼼是否为集群搭建 register-with-eureka : true ## 设置服务注册与发现中是否作为服务进⾏注册 fetch-registry : true ## ip 就是服务注册中⼼服务器的 ip ## port 就是服务注册与发现中⼼设置的 port service-url : defaultZone : http : //192.168.54.10 : 8761/eureka

10.2 安全性

当完成 Eureka 的搭建之后,只要知道 ip 和 port 就可以随意的注册服务、调⽤服务,这是 不安全的,我们可以通过设置帐号和密码来限制服务的注册及发现。 在 eureka 中整合 Spring Security 安全框架实现帐号和密码验证

10.2.1 添加SpringSecurity的依赖

<dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-security </artifactId> </dependency>

10.2.2 设置访问 eureka 的帐号和密码

spring : security : user : name : zhangsan password : 123456

10.2.3 配置Spring Security

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 http.csrf().disable();
 //设置当前服务器的所有请求都要使⽤spring security的认证
 
http.authorizeRequests().anyRequest().authenticated().and().httpBasic(
);
 }
} 
           

10.2.4 服务提供者和服务消费者连接到注册中⼼都要帐号和密码

eureka : client : service-url : defaultZone : http : //zhangsan : [email protected] : 8761/eureka

⼗⼀、熔断器-Hystrix

服务故障的雪崩效应:当 A 服务调⽤ B 服务时,由于 B 服务的故障导致 A 服务处于阻塞状 态,当量的请求可能会导致 A 服务因资源耗尽⽽出现故障。 为了解决服务故障的雪崩效应,出现了熔断器模型。

11.1 熔断器介绍

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

熔断器作⽤ :

  • 服务降级 :⽤户请求A服务,A服务调⽤B服务,当B服务出现故障或者在特定的时间段内 不能给A服务响应,为了避免A服务因等待B服务⽽产⽣阻塞,A服务就不等B服务的结果 了,直接给⽤户⼀个降级响应
  • 服务熔断 :⽤户请求A服务,A服务调⽤B服务,当B服务出现故障的频率过⾼达到特定阈 值(5s 20次)时,当⽤户再请求A服务时,A服务将不再调⽤B服务,直接给⽤户⼀个降级响应

11.2 熔断器的原理

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

11.3 基于Ribbon服务调⽤的熔断器使⽤

11.3.1 服务消费者的 服务降级

  • 添加熔断器依赖 hystrix
<dependency> <groupId> org.springframework.cloud </groupId> <artifactId> spring-cloud-starter-netflix-hystrix </artifactId> </dependency>
  • 在启动类添加 @EnableHystrix 注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class ApiOrderAddApplication {
 public static void main(String[] args) {
 SpringApplication.run(ApiOrderAddApplication.class, args);
 }
}
           
  • 在调⽤服务提供者的业务处理⽅法中,进⾏降级配置
@Service
public class OrderAddServiceImpl implements OrderAddService {
 @Autowired
 private RestTemplate restTemplate;
 @HystrixCommand(fallbackMethod =
"fallbackSaveOrder",commandProperties = {
 
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds
",value="3000")
 })
 public ResultVO saveOrder(Order order) {
 //1. 调⽤ order-add服务进⾏保存
 //参数1:访问服务的url
 //参数2:传递的对象参数
 //参数3:指定服务提供者返回的数据类型
 ResultVO vo =
 restTemplate.postForObject("http://order-add/order/add",
order, ResultVO.class);
 System.out.println(vo);
 //2. 调⽤ orderitem-add 保存订单快照
 //3. 调⽤ stock-update 修改商品库存
 //4. 调⽤ shopcart-del 删除购物⻋记录
 return vo;
 }
 /**
 * 降级⽅法:与业务⽅法拥有相同的参数和返回值
 * @return
 */
 public ResultVO fallbackSaveOrder(Order order){
 return ResultVO.fail("⽹络异常,请重试!",null);
 }
}
           

11.3.2 服务提供者的 服务降级

  • 配置步骤⼀致
  • 服务提供者接⼝降级
@RestController
@RequestMapping("/order")
public class OrderController {
 @Autowired
 private OrderService orderService;
 @HystrixCommand(fallbackMethod =
"fallbackAddOrder",commandProperties = {
 
@HystrixProperty(name="execution.isolation.thread.timeoutInMillisecond
s",value="3000")
 })
 @PostMapping("/add")
 public ResultVO addOrder(@RequestBody Order order){
 System.out.println("-------------------order-add");
 System.out.println(order);
 try {
 Thread.sleep(5000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 return orderService.saveOrder(order);
 }
 public ResultVO fallbackAddOrder(@RequestBody Order order){
 System.out.println("-------------------order-add--fallback");
 return ResultVO.fail("订单保存失败!",null);
 }
}
           

11.3.3 服务熔断配置

  • 熔断器状态:闭合、打开、半开
  • 服务熔断配置
@HystrixCommand(fallbackMethod =
"fallbackSaveOrder",commandProperties = {
 
@HystrixProperty(name="execution.isolation.thread.timeoutInMillise
conds",value="3000"),
 @HystrixProperty(name="circuitBreaker.enabled",value="true"),
//启⽤服务熔断
 
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",v
alue="10000"),//时间
 
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",valu
e="10"),//请求次数
 
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",va
lue="50"),//服务错误率
})
public ResultVO saveOrder(Order order) {
 //1. 调⽤ order-add服务进⾏保存
 ResultVO vo = restTemplate.postForObject("http://orderadd/order/add", order, ResultVO.class);
 System.out.println(vo);
 //2. 调⽤ orderitem-add 保存订单快照
 //3. 调⽤ stock-update 修改商品库存
 //4. 调⽤ shopcart-del 删除购物⻋记录
 return vo; }
/**
* 降级⽅法:与业务⽅法拥有相同的参数和返回值
* @return
*/
public ResultVO fallbackSaveOrder(Order order){
 return ResultVO.fail("⽹络异常,请重试!",null);
}
           

服务熔断:当⽤户请求服务 A ,服务 A 调⽤服务 B 时,如果服务 B 的故障率达到特定的 阈值时,熔断器就会被打开⼀个时间周期(默认 5s ,可⾃定义),在这个时间周期 内如果⽤户请求服务 A ,服务 A 将不再调⽤服务 B ,⽽是直接响应降级服务。

11.4 基于Feign服务调⽤的熔断器使⽤

Feign 是基于 Ribbon 和 Hystrix 的封装

11.4.1 Feign中的熔断器使⽤

  • 添加依赖

        SpringBoot 2.3.11 Spring Cloud H

<parent> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-parent </artifactId> <version> 2.3.11.RELEASE </version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version> 1.8 </java.version> <spring-cloud.version> Hoxton.SR11 </spring-cloud.version> </properties>
<dependency> <groupId> org.springframework.cloud </groupId> <artifactId> spring-cloud-starter-netflix-hystrix </artifactId> </dependency>
  • 在application.yml启⽤熔断器机制
feign : hystrix : enabled : true
  • 在启动类添加 @EnableHystrix
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class ApiOrderAddFeignApplication {
 public static void main(String[] args) {
 SpringApplication.run(ApiOrderAddFeignApplication.class,
args);
 }
}
           
  • 创建服务降级处理类

        FeignClient的服务降级类: 1. 必须实现 Feign 客户端接⼝, 2. 必须交给 Spring 容器管理

@Component
public class OrderAddClientFallback implements OrderAddClient {
 public ResultVO addOrder(Order order, String str) {
 System.out.println("-------addOrder的降级服务");
 return ResultVO.fail("fail",null);
 }
 public Order getOrder(String orderId) {
 System.out.println("-------getOrder的降级服务");
 return new Order();
 }
}
           
  • 在Feign客户端指定降级处理类
@FeignClient(value = "order-add", fallback =
OrderAddClientFallback.class)
public interface OrderAddClient {
 //1.对⽤POST请求调⽤服务,Feign客户端的⽅法参数默认为body传值(body只能
有⼀个值)
 //2.如果有多个参数,则需要通过@RequestParam声明参数为请求⾏传值
 @PostMapping("order/add")
 public ResultVO addOrder(Order order,@RequestParam("str")
String str);
 @GetMapping("order/get")
 public Order getOrder(@RequestParam("orderId") String orderId);
}
           
  • Service类通过Feign客户端调⽤服务
@Service
public class OrderAddServiceImpl implements OrderAddService {
 @Autowired
 private OrderAddClient orderAddClient;
 //当我们创建Feign客户端的降级类并交给Spring管理后 在Spring容器中就会出现
两个OrderAddClient对象
 @Override
 public ResultVO saveOrder(Order order) {
 //1. 调⽤ order-add服务进⾏保存
 ResultVO vo = orderAddClient.addOrder(order,"测试字符串");
 Order order1 = orderAddClient.getOrder("订单编号");
 System.out.println(order1);
 //2. 调⽤ orderitem-add 保存订单快照
 //3. 调⽤ stock-update 修改商品库存
 //4. 调⽤ shopcart-del 删除购物⻋记录
 return vo;
 }
}
           

11.4.2 Ribbon 参数配置

SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪
ribbon : ## Ribbon 建⽴连接最⼤等待时间 ConnectTimeout : 1000 ## 在当前服务提供者尝试连接次数 MaxAutoRetries : 2 ## 与服务提供者通信时间 ReadTimeout : 5000 ## 设置熔断器服务降级时间 (默认 1000 ) hystrix : command : default : execution : isolation : thread : timeoutInMilliseconds : 8000

11.5 熔断器仪表盘监控

如果服务器的并发压⼒过⼤、服务器⽆法正常响应,则熔断器状态变为 open 属于正常的 情况;但是如果⼀个熔断器⼀直处于 open 状态、或者说服务器提供者没有访问压⼒的情 况下熔断器依然为 open 状态,说明熔断器的状态就不属于正常情况了。如何了解熔断器 的⼯作状态呢 ?

  • 熔断器仪表盘

11.5.1 搭建熔断器仪表盘

  • 创建SpringBoot项⽬,添加依赖
<dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-actuator </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-hystrix dashboard </artifactId> </dependency>
  • 配置仪表盘的port和appName
server : port : 9999 spring : application : name : hystrix-dashboard hystrix : dashboard : proxy-stream-allow-list : "localhost"
  • 启动类添加 @EanbleHystrixDashboard 注解
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
 public static void main(String[] args) {
 SpringApplication.run(HystrixDashboardApplication.class,
args);
 }
}
           
  • 访问 http://localhost:9999/hystrix
SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

11.5.2 配置使⽤了熔断器的服务可被监控

  • 添加依赖
<dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-actuator </artifactId> </dependency>
  • 配置
@Configuration
public class DashBoardConfig {
 @Bean
 public ServletRegistrationBean getServletRegistrationBean(){
 HystrixMetricsStreamServlet streamServlet = new
HystrixMetricsStreamServlet();
 ServletRegistrationBean registrationBean =
 new ServletRegistrationBean(streamServlet);
 registrationBean.setName("HystrixMetricsStreamServlet");
 registrationBean.setLoadOnStartup(1);
 registrationBean.addUrlMappings("/hystrix.stream");
 return registrationBean;
 }
}
           
  • 查看指定服务的熔断器⼯作参数
SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪
SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪

⼗⼆、微服务拆分

⼗三、服务链路追踪

13.1 服务追踪说明

  • 微服务架构是通过业务来划分服务的,使⽤REST调⽤。对外暴露的⼀个接⼝,可能需要 很多个服务协同才能完成这个接⼝功能,如果链路上任何⼀个服务出现问题或者⽹络超 时,都会形成导致接⼝调⽤失败。
SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪
  • 随着业务的不断扩张,服务之间互相调⽤会越来越复杂,它们之间的调⽤关系也许如下:
SpringCloud 基本使用⼀、单体应⽤存在的问题 ⼆、微服务架构 三、微服务架构开发需要解决的问题 四、微服务架构框架 五、搭建服务注册与发现中⼼  六、服务注册 七、服务发现-Ribbon ⼋、基于Ribbon进⾏服务调⽤的参数传递 九、服务发现-Feign ⼗、服务注册与发现中⼼的可靠性和安全性 ⼗⼀、熔断器-Hystrix ⼗⼆、微服务拆分 ⼗三、服务链路追踪
  • 随着服务的越来越多,对调⽤链的分析会越来越复杂。

13.2 Zipkin

  • ZipKin是⼀个开放源代码的分布式跟踪系统,由Twitter公司开源,它致⼒于收集服务的 定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。它 的理论模型来⾃于 Google Dapper 论⽂。
  • 每个服务向 ZipKin 报告计时数据,ZipKin 会根据调⽤关系通过 ZipKin UI ⽣成依赖关系 图,显示了多少跟踪请求通过每个服务,该系统让开发者可通过⼀个 Web 前端轻松的收 集和分析数据,例如⽤户每次请求服务的处理时间等,可⽅便的监测系统中存在的瓶 颈。

13.3 搭建zipkin服务器

  • 创建SpringBoot项⽬(版本2.1.x)
  • 添加依赖
<dependency> <groupId> io.zipkin.java </groupId> <artifactId> zipkin-server </artifactId> <version> 2.11.10 </version> </dependency> <!--zipkin 界⾯ --> <dependency> <groupId> io.zipkin.java </groupId> <artifactId> zipkin-autoconfigure-ui </artifactId> <version> 2.11.10 </version> </dependency>
  • 在启动类添加 @EnableZipkinServer 注解
@SpringBootApplication
@EnableZipkinServer
public class ZipkinApplication {
 public static void main(String[] args) {
 SpringApplication.run(ZipkinApplication.class, args);
 }
}
           
  • 配置yml
spring : application : name : zipkin server : port : 9411 management : endpoints.web.exposure.include : '*' metrics.web.server.auto-time-requests : false

13.4 服务中Sleuth配置

  • 在服务应⽤中添加Sleuth依赖
<!-- spring-cloud-sleuth-zipkin --> <dependency> <groupId> org.springframework.cloud </groupId> <artifactId> spring-cloud-sleuth-zipkin </artifactId> <version> 2.0.2.RELEASE </version> </dependency>
  • 在服务应⽤中配置yml
spring : application : name : goods-provider zipkin : enabled : true base-url : http : //localhost : 9411 sleuth : sampler : probability : 0.1

13.5 zipkin服务数据存储

  • 创建数据库数据表
CREATE TABLE IF NOT EXISTS zipkin_spans (
 `trace_id` BIGINT NOT NULL,
 `id` BIGINT NOT NULL,
 `name` VARCHAR(255) NOT NULL,
 `parent_id` BIGINT,
 `debug` BIT(1),
 `start_ts` BIGINT COMMENT "Span.timestamp(): epoch micros used
for endTs query and to implement TTL",
 `duration` BIGINT COMMENT "Span.duration(): micros used for
minDuration and maxDuration query"
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id`, `id`) COMMENT
"ignore insert on duplicate";
ALTER TABLE zipkin_spans ADD INDEX(`trace_id`, `id`) COMMENT "for
joining with zipkin_annotations";
ALTER TABLE zipkin_spans ADD INDEX(`trace_id`) COMMENT "for
getTracesByIds";

ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT "for getTraces
and getSpanNames";
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT "for
getTraces ordering and range";
CREATE TABLE IF NOT EXISTS zipkin_annotations (
 `trace_id` BIGINT NOT NULL COMMENT "coincides with
zipkin_spans.trace_id",
 `span_id` BIGINT NOT NULL COMMENT "coincides with
zipkin_spans.id",
 `a_key` VARCHAR(255) NOT NULL COMMENT "BinaryAnnotation.key or
Annotation.value if type == -1",
 `a_value` BLOB COMMENT "BinaryAnnotation.value(), which must be
smaller than 64KB",
 `a_type` INT NOT NULL COMMENT "BinaryAnnotation.type() or -1 if
Annotation",
 `a_timestamp` BIGINT COMMENT "Used to implement TTL;
Annotation.timestamp or zipkin_spans.timestamp",
 `endpoint_ipv4` INT COMMENT "Null when
Binary/Annotation.endpoint is null",
 `endpoint_ipv6` BINARY(16) COMMENT "Null when
Binary/Annotation.endpoint is null, or no IPv6 address",
 `endpoint_port` SMALLINT COMMENT "Null when
Binary/Annotation.endpoint is null",
 `endpoint_service_name` VARCHAR(255) COMMENT "Null when
Binary/Annotation.endpoint is null"
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id`,
`span_id`, `a_key`, `a_timestamp`) COMMENT "Ignore insert on
duplicate";
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`)
COMMENT "for joining with zipkin_spans";
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`) COMMENT "for
getTraces/ByIds";
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`)
COMMENT "for getTraces and getServiceNames";
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT "for
getTraces";
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT "for
getTraces";

CREATE TABLE IF NOT EXISTS zipkin_dependencies (
 `day` DATE NOT NULL,
 `parent` VARCHAR(255) NOT NULL,
 `child` VARCHAR(255) NOT NULL,
 `call_count` BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`,
`child`);
           
  • pom依赖
<!-- zipkin-storage-mysql-v1 --> <dependency> <groupId> io.zipkin.zipkin2 </groupId> <artifactId> zipkin-storage-mysql-v1 </artifactId> <version> 2.11.12 </version> </dependency> <!--mysql 驱动 --> <dependency> <groupId> mysql </groupId> <artifactId> mysql-connector-java </artifactId> <version> 5.1.47 </version> </dependency>
  • 配置yml
spring : application : name : zipkin datasource : username : root password : admin123 driver-class-name : com.mysql.jdbc.Driver url : jdbc : mysql : //localhost : 3306/zipkin zipkin : storage : type : mysql