学习资料整理自B站狂神说
搭建SpringCloud项目
什么是微服务
微服务架构是一种架构模式、一种架构风格,它提倡将单一的应用程序划分成一组细小的服务,每个服务运行在其独自的进程中,服务之间相互协调,相互配置,为用户提供最终价值。服务之间采用轻量级的通信机制相互沟通,每个服务都围绕着具体的业务进行构建,并且能够被单独的部署到生产环境中,另外,应尽量避免统一的集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文选择合适的语言、工具对其进行构建,可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
微服务化的核心就是将传统的一站式应用,根据业务拆分成一个个的服务,彻底地解耦,每个微服务提供单一的业务功能服务,从技术角度看就是一种小而独立的处理过程,类似进程的概念,能够自行单独启动或销毁,甚至拥有自己的数据库。
微服务优点
微服务缺点
微服务技术栈
服务开发
SpringBoot/Spring/SpringMVC
服务配置与管理
Netflix公司的Archaius/阿里的Diamond等
服务注册与发现
Eureka/Consul/Zookeeper等
服务调用
Rest/RPC/gRpc
熔断器
Hystrix/Envoy等
负载均衡
Ribbon/Nginx等
服务接口调用(客户端调用服务的简化工具)
Feign等
消息队列
Kafka/RabbitMQ/ActiveMQ等
配置中心
SpringCloudConfig/Chef等
服务路由(API网关)
Zuul等
服务监控
Zabbix/Nagios/Metrics/Specatator等
全链路追踪
Zipkin/Brave/Dapper等
服务部署
Docker/OpenStack/Kubernetes等
数据流操作开发包
SpringCloud Stream(封装与Redis、Rabbit、Kafka等发送接收消息)
事件消息总线
SpringCloud Bus
等…
微服务解决方案
Spring Cloud Netflix 一站式解决方案
Apache Dubbo Zookeeper 半自动,需要整合第三方组件
Spring Cloud Alibaba 2020年出版
SpringCloud的优势
SpringCloud和SpringBoot什么关系?
- SpringBoot专注于快速方便的开发单个服务;
- SpringCloud关注是整个微服务协调治理的框架;
- SpringCloud依赖于SpringBoot。
相关资料网站
英文官网:https://spring.io/projects/spring-cloud/
中文API:https://springcloud.cc/spring-cloud-dalston.html
SpringCloud中国社区:http://springcloud.cn
SpringCloud中文网:https://springcloud.cc
SpringCloud的版本用的是伦敦的地铁站来命名,并通过首字母排序
SpringBoot | SpringCloud |
---|---|
1.2.x | Angel(天使) |
1.3.x | Brixton |
1.4.x | Camden |
1.5.x | Dalston |
1.5.x | Edgware |
2.0.x | Finchley |
2.1.x | Greenwich |
2.2.x, 2.3.x (Starting with SR5) | Hoxton |
<packaging>pompackaging>
<properties>
<spring.cloud-version>Greenwich.SR1spring.cloud-version>
<spring.boot-version>2.1.4.RELEASEspring.boot-version>
<mysql-version>8.0.13mysql-version>
<druid-version>1.1.10druid-version>
<mybatis-version>1.3.0mybatis-version>
<logback-version>1.2.3logback-version>
<junit-version>4.12junit-version>
<log4j-version>1.2.17log4j-version>
<lombok-version>1.16.18lombok-version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.complier.source>1.8maven.complier.source>
<maven.complier.target>1.8maven.complier.target>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud-version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring.boot-version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql-version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>${druid-version}version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis-version}version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>${logback-version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit-version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j-version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok-version}version>
dependency>
dependencies>
dependencyManagement>
create table dept
(
dept_id bigint auto_increment
primary key,
dept_name varchar(50) null,
db_source varchar(50) null
);
INSERT INTO ssx.dept (dept_id, dept_name, db_source) VALUES (1, '开发部', 'ssx');
INSERT INTO ssx.dept (dept_id, dept_name, db_source) VALUES (2, '人事部', 'ssx');
INSERT INTO ssx.dept (dept_id, dept_name, db_source) VALUES (3, '市场部', 'ssx');
INSERT INTO ssx.dept (dept_id, dept_name, db_source) VALUES (4, '财务部', 'ssx');
INSERT INTO ssx.dept (dept_id, dept_name, db_source) VALUES (5, '运维部', 'ssx');
package com.ssx.springcloud.entity;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @author: stone
* @create: 2020-09-21 23:58
*/
@Data
@NoArgsConstructor
@Accessors(chain = true) //链式写法
public class Dept implements Serializable {
private Long deptId;
private String deptName;
//数据所在数据库实例名
private String dbSource;
public Dept(String deptName){
this.deptName = deptName;
}
}
写好service、dao、mapper、mybatis-config、application.yml
package com.ssx.springcloud.controller;
import com.ssx.springcloud.entity.Dept;
import com.ssx.springcloud.service.IDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author: stone
* @create: 2020-09-22 14:45
*/
@RestController()
@RequestMapping("/dept")
public class DeptController {
@Autowired
IDeptService deptService;
@PostMapping("/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/get/{id}")
public Dept getDeptById(@PathVariable("id") Long id){
return deptService.getDeptById(id);
}
@GetMapping("/list")
public List<Dept> getDeptAll(){
return deptService.getDeptAll();
}
}
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: com.ssx.springcloud.entity
spring:
application:
name: springcloud-privoder-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 数据源
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://ip:port/ssx?serverTimezone=Hongkong
username: root
password: ***
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
configuration>
对外提供服务
编写配置类将RestTemplate注入,然后通过restTemplate访问服务提供者
package com.ssx.springcloud.controlller;
import com.ssx.springcloud.entity.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author: stone
* @create: 2020-09-22 15:59
*/
@RestController
@RequestMapping("/dept/")
public class DeptConsumerController {
/**
* 首先 consumer服务 没有 service层
* 通过RestTemplate访问另一个服务
* 提供多种便捷访问远程http服务的脚手架,简单的RESTFul服务模版。
*/
@Autowired
RestTemplate restTemplate;
private static final String REST_URL_PREFIX = "http://localhost:8001/dept/";
@RequestMapping("add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"add",dept,Boolean.class);
}
@RequestMapping("get/{id}")
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"get/"+id, Dept.class);
}
@RequestMapping("list")
public List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"list",List.class);
}
}
测试结果
RESTFul项目就搭完了。
Eureka是Netflix的一个子模块,也是核心模块之一。
Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了,功能类似于Dubbo的注册中心,比如Zookeeper。
Eureka的基本架构
三大角色
填好配置文件
server:
port: 7001
eureka:
instance:
hostname: sc-eureka # 服务名
client:
register-with-eureka: false #是否将eureka注册到eureka
fetch-registry: false #false表示 当前服务为注册中心
service-url: # 监控页面
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类启动
@SpringBootApplication
@EnableEurekaServer // 服务端启动类,用于接收其它微服务的注册
public class EurekaServer_7001 {
public static void main(String args[]){
SpringApplication.run(EurekaServer_7001.class,args);
}
}
访问7001端口,Eureka管理页面
springcloud-provider-dept-8001 pom.xml
加入两个依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
启动器加上eureka客户端注解
@SpringBootApplication
@EnableEurekaClient // 启动注册到Eureka Server
public class DeptProvider_8001 {
public static void main(String args[]){
SpringApplication.run(DeptProvider_8001.class,args);
}
}
修改服务提供者的application.yml
eureka:
instance:
instance-id: sc-provider-dept # 对应eureka注册列表上的Status字段
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka/
# info 用于actuator展示信息
info:
app.name: ssx-springcloud
company.name: blog.ssx.com
启动 8001,访问 7001
点击status中的链接跳转actuator服务信息页面
eureka自我保护机制
在正常情况下,EurekaServer在一定时间内(默认90s)没有接收到心跳包则会将该服务注册掉。
但是极端条件下,如突然某个服务的网络中断了,这时此微服务无法与Eureka进行通信,此时EurekaServer通过自我保护机制,临时的将此服务的信息状态保存在服务注册列表中。那么如果在短时间内,微服务的集体或大部分丢失(故障),那么Eureka会进入自我保护模式,进入此模式后,EurekaServer将会把服务注册表保护起来,不再删除服务注册表的数据(也就是不会注销任务微服务)。
等到网络故障恢复后,收到的心跳数恢复到正常阈值以上时,该EurekaServer节点会自动退出自我保护模式。
**自我保护模式是一种应对网络异常的安全保护措施。这种设计的思想是:宁可同时全部微服务,也不盲目注销任务可能健康的微服务。**这种设计可以让eureka服务集群更健壮稳定。
关闭自我保护的配置:(不推荐关闭)
eureka.server.enable-self-preservation = fase
在服务上获取注册中心信息
DeptController.java
@GetMapping("/discoveryClient")
public Object discoveryClient(){
// 团队协同开发时,用到的获取服务注册列表的信息
discoveryClient.getServices().forEach(System.out::println);
List<ServiceInstance> instances = discoveryClient.getInstances("springcloud-privoder-dept");
System.out.println("============================");
instances.forEach(i->{
System.out.println("getHost:"+i.getHost());
System.out.println("getInstanceId:"+i.getInstanceId());
System.out.println("getScheme:"+i.getScheme());
System.out.println("getServiceId:"+i.getServiceId());
System.out.println("getMetadata:"+i.getMetadata());
System.out.println("getPort:"+i.getPort());
System.out.println("getUri:"+i.getUri());
});
return discoveryClient.getServices();
}
启动器加上注解
@EnableDiscoveryClient // 获取注册中心的信息
console
springcloud-privoder-dept
============================
getHost:192.168.0.100
getInstanceId:sc-provider-dept
getScheme:null
getServiceId:SPRINGCLOUD-PRIVODER-DEPT
getMetadata:vlsi.utils.CompactHashMap@3837686d
getPort:8001
getUri:http://192.168.0.100:8001
画了个鬼画符,,,
含义很简单,就是将eureka分别注册到另外两台上,
各种微服务要注册到三台eureka上
7001的配置文件,另外两台按此规律修改
server:
port: 7001
eureka:
instance:
hostname: eureka-7001 # 服务名
client:
register-with-eureka: false #是否将eureka注册到eureka
fetch-registry: false #false表示 当前服务为注册中心
service-url: # 监控页面
# Eureka 单机配置
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# Eureka 集群配置,配置三台:将此eureka注册到另外两台eureka
defaultZone: http://eureka-7002.com:7002/eureka/,http://eureka-7003.com:7003/eureka/
含义很简单,就是将eureka分别注册到另外两台上
# 集群结构
defaultZone: http://eureka-7001.com:7001/eureka/,http://eureka-7002.com:7002/eureka/,http://eureka-7003.com:7003/eureka/
将此服务注册到三台eureka上
由于在单机上做测试,修改hosts映射
127.0.0.1 eureka-7001.com
127.0.0.1 eureka-7002.com
127.0.0.1 eureka-7003.com
运行~
如果当一台eureka挂了,另外两台还可用
然后eureka启动回来会重新注册到注册中心。
至此Eureka 集群搭建完毕。
著名的CAP理论指出:一个分布式系统不可能同时拥有完美的一致性、可用性、容错性。
所以选择集群结构时,也在选择怎样进行更合适的搭配。
由于分区容错性在分布式系统中是必须保证的,因此Eureka和Zookeeper的抉择是:
Zookeeper —— Consistency 一致性、 Partition Tolerance 容错性
zk中会出现这样一种情况:当master节点因为网络故障与其它节点失去联系时,剩余节点会重新选举leader。但是选举leader的时间为 30s~120s,且选举期间整个zk集群都是不可用的,这样会导致选举期间注册服务处于瘫痪状态。在云部署环境下,因为网络问题是的zk集群失去master节点是大概率会发生的事件,虽然网络最终能够恢复,但是漫长的选举时间导致的注册服务瘫痪不可用是不能容忍的。
Eureka —— Availability 可用性、Partition Tolerance 容错性
eureka看懂了这点,因此在设计之初就优先保证了可用性。eureka集群的各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余节点依然可以提供注册和查询服务。而eureka的客户端在向某个eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台eureka健康则注册服务依旧可用,只是查询的结果可能不是最新的。除此之外eureka还有自我保护机制,如果在15分钟内超过85%的节点都没有正常心跳,那么eureka就会认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper整个注册服务瘫痪.
Spring Cloud Ribbon 是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置项如:连接超时、重试等等。
在配置文件中列出Load Balancer(简称LB:负载均衡)后面所有的机器,Ribbon会自动的帮助你基于某种规则(简单轮询、随机连接等)去连接这些机器。
负载均衡(Load Balancer)在微服务或分布式集群中提供负载,达到服务高可用(常用的负载均衡软件还有:Nginx、Lvs等);
负载均衡分类:
集中式
在服务的消费方和提供方之间使用独立的Load Balancer软件如Nginx,由该软件负责把访问请求通过某种策略转发到服务的提供方;
进程式
在消费方实现Load Balancer,负载服务得到服务提供方的地址集,然后自行使用一些算法将请求分发出去。Ribbon属于进程式Load Balancer,是一个类库并继承与消费方。
三个服务提供者,三个数据库,一个注册中心,一个服务消费者
三个数据库,都拥有Dept表**
提供者服务
拷贝出三份服务提供者
修改端口:8001 8002 8003
修改链接的数据库url
修改eureka注册id
eureka:
instance:
instance-id: sc-provider-dept8001 # 对应eureka注册列表上的Status字段
三个服务提供者的微服务名儿一致,在微服务中体现为集群
消费者服务
RestTemplate注入器要加上@LoadBalancer
@Bean
@LoadBalanced // 负载均衡模式下,需要使用此注解注入RestTemplate
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
调用服务提供者的链接改用微服务名儿,通过微服务名儿调用
// Ribbon负载均衡时,通过微服务名访问,无需知道ip端口
private static final String REST_URL_PREFIX = "http://springcloud-privoder-dept/dept/";
启动器需要声明为eureka客户端,因为需要获取eureka中心的信息
@SpringBootApplication
@EnableEurekaClient // 使用Ribbon负载时,需要在消费方引入eureka客户端
public class DeptConsumer_80 {
public static void main(String args[]){
SpringApplication.run(DeptConsumer_80.class,args);
}
}
消费者服务注册配置eureka地址
server:
port: 80
eureka:
client:
register-with-eureka: false # 不需要注册
service-url:
defaultZone: http://eureka-7001.com:7001/eureka/,http://eureka-7002.com:7002/eureka/,http://eureka-7003.com:7003/eureka/
首先启动注册中心、然后是三个服务提供者,最后是服务消费者
访问
可以看出来自三个数据库
至此服务集群完成!
Ribbon提供了多种负载均衡的算法模式供大家使用,默认是轮询算法;
通过源码可知,大概有以下几种,常用的为轮询算法、随机算法、权重算法等。
实现 IRule.interface;
自定义算法类不能放在启动类上下文的@ComponentScan中。
官方原话:FooConfiguration必须是@Configuration,但请注意,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan)。
自定义负载算法
/**
* 自定义ribbon 微服务 负载均衡算法
* 先拷贝了轮询模式的代码再进行修改
* 算法思想:简单实现 每个服务使用3次再提供下一个服务
* @author: stone
* @create: 2020-09-25 00:08
*/
public class MyRule extends AbstractLoadBalancerRule {
//内容挺长,,,就不贴了,看看源码很容易就能写出来
}
配置类应用算法类
/**
* 自定义负载算法配置类,指定使用那个自定义负载算法
* 此类要单独注入,不然全部ribbon集群都会共用一个负载算法
* @author: stone
* @create: 2020-09-25 00:30
*/
@Configuration
public class UseMyRule {
@Bean
public IRule myRule(){
return new MyRule();
}
}
启动器要加一个注解@RibbonClient指定负载配置类
/**
* @author: stone
* @create: 2020-09-22 16:21
*/
@SpringBootApplication
@EnableEurekaClient // 使用Ribbon负载时,需要在消费方引入eureka客户端
@RibbonClient(name = "springcloud-privoder-dept",configuration = UseMyRule.class) //指定使用自定义负载均衡算法
public class DeptConsumer_80 {
public static void main(String args[]){
SpringApplication.run(DeptConsumer_80.class,args);
}
}
完成~
feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
Feign的目的是让负载更简单~
主要是java程序员都习惯面向接口编程,也是很多开发人员的规范,所以诞生了Feign。
从此访问微服务有了两种方法:
Feign的作用
Feign集成了Ribbon实现负载均衡
利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并通过轮询实现了客户端的负载均衡,与Ribbon不同的是,Feign只需要定义服务绑定接口且以声明式的方法,优雅、简单的实现了服务调用。
核心代码,业务层放在 api 项目
/**
* 使用Feign访问微服务
* @author: stone
* @create: 2020-09-25 11:34
*/
@FeignClient("springcloud-privoder-dept") //指定微服务的服务名,将请求转发到这个服务上
@Component
public interface DeptService {
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/get/{id}")
public Dept getDeptById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> getDeptAll();
}
新建Feign项目
用于接收客户的请求,通过调用 service 实现转发
@RestController
@RequestMapping("/dept")
public class DeptConsumerController {
@Autowired
DeptService deptService;
@PostMapping("/add")
public boolean add(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/get/{id}")
public Dept get(@PathVariable("id") Long id){
return deptService.getDeptById(id);
}
@GetMapping("/list")
public List<Dept> list(){
return deptService.getDeptAll();
}
}
/**
* @author: stone
* @create: 2020-09-22 16:21
*/
@SpringBootApplication
@EnableEurekaClient // 使用Feign负载时,需要在消费方引入eureka客户端
@EnableFeignClients(basePackages = "com.ssx.springcloud") //扫描使用Feign的类
public class DeptConsumer_80_Feign {
public static void main(String args[]){
SpringApplication.run(DeptConsumer_80_Feign.class,args);
}
}
启动测试
启动 feign项目,绑定了80端口
https://github.com/Netflix/Hystrix/wiki
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败(超时、异常等),Hystrix能够保证在一个依赖出问题的时候,不会导致整个服务失败,避免级联故障,以提高分布式系统的稳定性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似保险丝熔断器),向调用方返回一个服务预期的、可处理的备选响应(Fallback),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会长时间阻塞占用,从而避免了故障在分布式系统的蔓延乃至雪崩。
分布式系统雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上的某个微服务调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,这就是所谓的“雪崩效应”。
对于高流量应用来说,单一的后端依赖可能会导致所有服务器上的所有资源在几秒内饱和,并且更糟糕的是这些应用程序还可能导致服务之间的延迟增加、备份队列,线程和其它系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障系统和延迟进行隔离与管理,以便单个依赖关系的失败导致整个应用系统崩溃。
引用Hystrix官方的三个图说明:
- 当服务都健康的时候,用户发起请求
- 当某一个微服务故障时
- 导致大量请求在此微服务阻塞,占用资源
在服务提供者对异常或超时等突发问题进行处理。
熔断机制是对应雪崩效应的一中微服务链路保护机制。
当扇出链路的某一个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现,Hystrix可以监控微服务间调用的情况,当失败的调用到达一定的阈值,缺省是5秒内20次调用失败就会启动熔断机制。
@HystrixCommand
代码实现
添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
<version>1.4.6.RELEASEversion>
dependency>
编写熔断方法,使用@HystrixCommand注解
/**
* 使用熔断器
* @author: stone
* @create: 2020-09-22 14:45
*/
@RestController
@RequestMapping("/dept")
public class DeptController {
@Autowired
IDeptService deptService;
/**
* 支持熔断的方法
* @param id
* @return
*/
@GetMapping("/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGetDept") //添加失败时返回的方法
public Dept getDeptById(@PathVariable("id") Long id){
Dept dept = deptService.getDeptById(id);
if(dept==null){
throw new RuntimeException("getDeptById_hystrix() dept is null!!!");
}
return dept;
}
/**
* 根据id查询失败时的回调、备用方法
* @param id
* @return
*/
public Dept hystrixGetDept(@PathVariable("id") Long id){
return new Dept().
setDeptId(id).
setDeptName("find no dept for this id.").
setDbSource("null");
}
}
启动熔断服务
@SpringBootApplication
@EnableEurekaClient // 启动注册到Eureka Server
@EnableDiscoveryClient // 获取注册中心的信息
@EnableCircuitBreaker // 启动hystrix熔断器
public class DeptProvider_8004_hystrix {
public static void main(String args[]){
SpringApplication.run(DeptProvider_8004_hystrix.class,args);
}
}
启动测试
在微服务调用方,对不能正常返回的服务进行失败回调的预判,返回预定的信息。
场景:当遇到某些服务的高峰洪流时,关闭一些访问比较少的服务,将服务器资源让给热点服务。
代码实现
编写当微服务请求不可达时的fallbackl,在服务调用端
/**
* 服务降级
* 若调用的服务不能正常请求时执行
* @author: stone
* @create: 2020-09-26 14:12
*/
@Component
public class DetpServiceFallbackFactory implements FallbackFactory {
@Override
public DeptService create(Throwable throwable) {
//访问服务端失败时,返回预先准备的信息
return new DeptService() {
@Override
public boolean addDept(Dept dept) {
return false;
}
@Override
public Dept getDeptById(Long id) {
return new Dept().
setDeptId(id).
setDeptName("当前服务不可用,请稍候再试!").
setDbSource("");
}
@Override
public List<Dept> getDeptAll() {
return null;
}
};
}
}
feign调用微服务的声明入口需要指定fallback
//指定微服务的服务名,将请求转发到这个服务上
//若服务不可达,则使用fallback回调
@FeignClient(value = "springcloud-privoder-dept",fallbackFactory = DetpServiceFallbackFactory.class)
public interface DeptService {}
在消费者服务开启feign-hystrix功能
server:
port: 80
eureka:
client:
register-with-eureka: false # 不需要注册
service-url:
defaultZone: http://eureka-7001.com:7001/eureka/,http://eureka-7002.com:7002/eureka/,http://eureka-7003.com:7003/eureka/
# 开启 feign-hystrix fallback
feign:
hystrix:
enabled: true
启动测试:当服务提供者不存活时,feign-hystrix fallback
服务熔断
在服务提供者(provider)实现,当处理的任务发生异常、或者超时等问题是,直接返回缺省值给服务消费者(consumer),避免服务堆积引起雪崩,保证系统整体的稳定性;
服务降级
在服务消费者(consumer)实现,由服务消费者发送请求到服务提供者时,由于服务提供者宕机或者其它原因导致请求不可达时,服务消费者使用缺省值返回给用户。应对一些特殊场景如秒杀等导致用户集中使用一个服务时,场面一边倒,关闭一些没人用或者访问量较少的服务,让出系统资源达到服务降级的效果,保证热点服务的可用性。
实则只监控这个注解修饰的接口
@HystrixCommand
代码实现
新建module:springcloud-consumer-dashboard-9001
添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
<version>1.4.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
<version>1.4.6.RELEASEversion>
dependency>
开启dashboard监控器
@SpringBootApplication
@EnableHystrixDashboard // 开启dashboard监控器
public class Dashborard_9001 {
public static void main(String args[]){
SpringApplication.run(Dashborard_9001.class,args);
}
}
绑定端口9001
server:
port: 9001
在所需监控的微服务中注册提供监控的servlet bean。
我这边加到了springcloud-provider-dept-8004-hystrix服务上
@SpringBootApplication
@EnableEurekaClient // 启动注册到Eureka Server
@EnableDiscoveryClient // 获取注册中心的信息
@EnableCircuitBreaker // 启动hystrix熔断器
public class DeptProvider_8004_hystrix {
public static void main(String args[]){
SpringApplication.run(DeptProvider_8004_hystrix.class,args);
}
/**
* 注册一个bean
* 将本服务的actuator监控公布出去
* @return
*/
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
启动访问 http://localhost:8004/actuator/hystrix.stream
获得请求时,显示心跳信息
地址栏中输入8004的监控地址:http://localhost:8004/actuator/hystrix.stream
进入图形监控页面:
监控图解释
实心圆
曲线
最近2分钟的流量变化曲线图,观察该实例的流量变化趋势。
整图说明
Zuul包含了对请求的路由和过滤这两个主要的功能。
路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;
过滤器功能则负责对请求的处理进行干预,是实现请求校验,服务聚合等功能的基础。
Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获取其它微服务的消息,即以后的访问微服务都是通过Zuul跳转访问。
提供:代理+路由+过滤 三大功能
引入zuul依赖
<dependency>
<groupId>com.ssxgroupId>
<artifactId>springcloud-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
编写启动类,启动zuul
@SpringBootApplication
@EnableZuulProxy //开启zuul
public class Zuul9527 {
public static void main(String args[]){
SpringApplication.run(Zuul9527.class,args);
}
}
配置zuul路由
zuul:
routes:
dept1.serviceId: springcloud-privoder-dept
dept1.path: /d/**
# ignored-services: springcloud-privoder-dept #忽略直接访问这个服务的请求
# ignored-services: "*" # 忽略全部未代理的路由地址
相关配置还有很多很多。
启动测试
zuul能配置的东西还很多
官方开发指引:https://www.springcloud.cc/spring-cloud-dalston.html#_router_and_filter_zuul
Spring Cloud Config 为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为各个微服务应用的所有环节提供一个配置中心。
SpringCloud config分布式配置中心
配置中心可以是本地仓库,也可以是云端仓库。
SpringCloud Config分为服务端和客户端两部分:
分布式配置中心的功能:
SpringCloud Config分布式配置中心与github整合
默认使用Git存储配置文件(也可以用svn/本地),以http或https访问;
配置中心分服务端和客户端,服务端负责与git连接。
依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
<version>2.1.1.RELEASEversion>
dependency>
配置
server:
port: 3344
spring:
application:
name: springcloud-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/eliott/study-cloud.git
config下有几个相关配置:
application-dev.yml
对应application-dev.yml
对应config-client.yml这个放在git上
server:
port: 8010
spring:
profiles: dev
application:
name: springcloud-provider-dept
eureka:
client:
server-uri:
defaultZone: http://eureka-7001.com:7001/eureka/,http://eureka-7002.com:7002/eureka/,http://eureka-7003.com:7003/eureka/
---
server:
port: 8010
spring:
profiles: test
application:
name: springcloud-provider-dept
eureka:
client:
server-uri:
defaultZone: http://eureka-7001.com:7001/eureka/,http://eureka-7002.com:7002/eureka/,http://eureka-7003.com:7003/eureka/
启动测试
@SpringBootApplication
@EnableConfigServer // 开启config 服务
public class ConfigServer3344 {
public static void main(String args[]){
SpringApplication.run(ConfigServer3344.class,args);
}
}
客户端为具体业务服务
依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
<version>1.4.6.RELEASEversion>
dependency>
配置
使用bootsrap.yml避免引用配置中心的配置文件时起冲突
# application.yml 应用级配置
spring:
application:
name: config-client-3355
# bootstrap.yml 系统级配置
spring:
cloud:
config:
uri: http://localhost:3344 # config server
name: config-client # git上面的资源名称
label: master # 分支
profile: dev # 环境
启动测试
@SpringBootApplication
public class ConfigClient3355 {
public static void main(String args[]){
SpringApplication.run(ConfigClient3355.class,args);
}
}
写一个controller获取配置信息
直接使用@Value获取
@Value("${spring.application.name}")
private String applicationName;
完结撒花~