Spring cloud 是一个基于 Spring Boot 实现的服务治理工具包,用于微服务架构中管理和协调服务的。Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。有了 Spring Cloud 之后,让微服务架构的落地变得更简单。
Spring Cloud Netflix是一个开源的分布式系统开发框架,它基于Spring Boot构建,并集成了Netflix的多个组件,为开发者提供了一套完整的解决方案,用于构建可伸缩、高可用、可靠的分布式系统。
Spring Cloud Netflix的体系由多个组件组成,包括Eureka、Ribbon、Hystrix、Feign和Zuul等。这些组件分别用于服务注册与发现、负载均衡、熔断器、声明式HTTP客户端和API网关等功能。通过将这些组件集成在一起,Spring Cloud Netflix能够提供一种简单而强大的方式来构建分布式系统。
每个服务足够内聚,足够小,聚焦到一个指定的业务功能或业务需求
开发简单,开发效率提高,一个服务可能就是专一干某件事
微服务能够被小团队单独开发
微服务是松耦合的,无论在开发阶段还是部署阶段都是独立的
微服务只是业务逻辑的代码,不会和HTML、CSS或其他界面混合
每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库
Spring Cloud Netflix提供了Eureka组件,用于服务注册与发现。在分布式系统中,服务的数量可能会动态变化,而Eureka能够帮助我们自动发现和注册服务。通过Eureka,我们可以轻松地实现服务之间的通信和协作,而无需手动配置每个服务的位置和地址。
Eureka是一个服务发现框架,它允许微服务应用程序在注册中心中注册自己,并从中心中发现其他服务。它提供了一个REST API,用于管理和查询服务实例。Eureka还提供了负载均衡和故障转移功能,以确保服务的可靠性和可用性。
Eureka保证的是AP
Eureka在设计优先保证可用性。Eureka各个节点都是平等的。几个节点挂了不会影响正常节点的工作,剩余节点依然可用提供注册和查询功能
自我保护机制:
默认情况下,EurekaServer在一定事件内页面收到微服务的心跳,则会注销该实例(默认90s)(长链接),当网络分区故障发生时,微服务与Eureka之间无法正常通信,Eureka通过自我保护机制解决,这种模式下,Eureka会保护服务注册表的信息,不再删除注册表中的数据,网络恢复后,自动退出自我保护机制。
基本架构
Eureka的优点在于它的简单性和易用性。它提供了一个轻量级的服务发现框架,可以快速地构建和部署微服务应用程序。此外,Eureka还可以与其他SpringCloudNetflix组件无缝集成,例如Zuul和Ribbon,以提供更强大的功能。
其次,Spring Cloud Netflix的Ribbon组件用于负载均衡。在大规模的分布式系统中,负载均衡是非常重要的,它能够将请求分发到多个服务实例上,从而提高系统的性能和可靠性。
主要功能是提供客户端的软件负载均衡算法。Ribbon能够根据一定的策略选择合适的服务实例,并将请求转发到这些实例上。
就是在配置文件中列出loadBalancer(简称LB:负载均衡)后面所有的机器,ribbon会自动的帮助你基于某种规则(如简单轮询,随机)去连接这些机器。
Ribbon内置的七大负载均衡算法
Feign是一个声明式的、基于注解的HTTP客户端,它是Netflix开源的一个轻量级的HTTP通信框架。Feign旨在简化微服务架构中的服务间通信,使得开发者可以像编写本地方法调用一样来调用远程服务。
Feign的核心思想是通过定义接口的方式来描述服务的调用,然后在运行时动态生成实现类。开发者只需要在接口上添加注解来配置请求的URL、HTTP方法、请求参数、请求头等信息,Feign会自动根据注解生成对应的HTTP请求,并将响应转换为Java对象返回。
通过Feign,我们可以使用简单的注解来定义服务接口,并自动生成具体的实现代码。这样,我们就可以像调用本地方法一样调用远程服务,从而提高开发效率和代码的可读性。
另外,Spring Cloud Netflix的Hystrix组件是一种熔断器,用于处理服务之间的故障和延迟。在分布式系统中,一个服务的故障可能会导致整个系统的崩溃,而Hystrix能够帮助我们处理这些故障。它可以监控服务的状态和性能,并在出现故障时提供备选方案,从而保证系统的可用性和可靠性。
在分布式环境中,不可避免地会遇到所依赖的服务挂掉的情况,Hystrix 可以通过增加 延迟容忍度 与 错误容忍度,来控制这些分布式系统的交互。Hystrix 在服务与服务之间建立了一个中间层,防止服务之间出现故障,并提供了失败时的备选响应( fallback) 策略,来增加系统的整体可靠性和弹性,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会长时间占用。
熔断与降级的区别:
服务熔断:代码写在提供方,相当于处理、返回异常
服务降级:代码写在消费方,提供方关闭服务后,提示、返回异常信息
最后,Spring Cloud Netflix的Zuul组件是一种API网关,用于管理和保护后端服务。在分布式系统中,我们经常需要对外提供统一的API接口,而Zuul能够帮助我们实现这一目标。它可以对请求进行路由和过滤,从而实现请求的转发、安全验证和限流等功能。
Zuul的核心功能包括:
综上所述,Spring Cloud Netflix体系是一种强大而灵活的分布式系统开发框架。通过集成Netflix的多个组件,它能够提供一套完整的解决方案,用于构建可伸缩、高可用、可靠的分布式系统。无论是服务注册与发现、负载均衡、熔断器、声明式HTTP客户端还是API网关,Spring Cloud Netflix都能够提供简单而强大的功能,帮助开发者构建优秀的分布式系统。因此,Spring Cloud Netflix体系在现代分布式系统开发中具有重要性和优势。
Nacos 是什么?【Nacos = Eureka + Config】:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos 就是注册中心 + 配置中心的组合。
动态配置中心可以实现路由规则的动态配置、限流规则的动态配置、动态数据源、开关、动态UI等场景.
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。
Nacos 支持 AP 和 CP 模式的切换
1、C 是所有节点在同一时间看到的数据是一致的;而 A 的定义是所有的请求都会收到响应。
2、何时选择使用 AP 或 CP 模式?
(1)一般来说,如果不需要存储服务级别的信息且服务实例是通过 nacos-client 注册,并能够保持心跳上报,那么选择 AP 模式。当前主流的服务如:SpringCloud 和 Dubbo 服务,都适合用于 AP 模式,AP 模式为了服务的可能性而减弱一致性,因此 AP 模式下只支持注册临时实例。
(2)如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S 服务和 DNS 服务都是用于 CP 模式。CP 模式则支持注册持久换实例,此时是以 Raft 协议为集群运行模式。该模式下注册实例之前必须先注册服务,如果服务不存在,则返回错误。
(3)模式切换:curl -X PUT ‘$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP’
nacos服务客户端(要注册到nacos的服务)启动时会每隔一段时间(默认5秒)向nacos发送心跳包,nacos注册中心15秒内没有检测到心跳包会默认认为nacos处于一种不健康的状态,30秒还没收到则认为这个服务已不可用。
Nacos的保护阈值
Nacos中可以针对具体的实例设置一个保护阈值=当前服务健康实例数/当前服务总实例数。
⼀般情况下,服务消费者要从Nacos获取可用实例有健康/不健康状态之分。Nacos在返回实例时,只会返回健康实例。
保护阈值存在的意义在于当服务A健康实例数/总实例数 < 保护阈值时,说明健康的实例不多了,保护阈值会被触发(状态true)。
Nacos会把该服务所有的实例信息(健康的+不健康的)全部提供给消费者,消费者可能访问到不健康的实例,请求失败,但这样也⽐造成雪崩要好。牺牲了⼀些请求,保证了整个系统的可⽤。
这里我们看到了不健康实例的另外一个作用:防止产生雪崩。
那么,如果所有的实例都是临时实例,当雪崩场景发生时,Nacos的阈值保护机制是不是就没有足够的(包含不健康实例)实例返回了?如果有一部分实例是持久化实例,即便它们已经挂掉,状态为不健康的,但当触发阈值保护时,还是可以起到分流的作用。
Sentinel: 分布式系统的流量防卫兵。随着微服务的流行,服务与服务之间的稳定性变得越来越重要,Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
Sentinel的主要功能包括:
总的来说,Sentinel是一款用于流量控制和熔断降级的工具。它提供了流量控制、熔断降级、系统保护、统计监控等功能,帮助开发者构建可靠的微服务架构。Sentinel还提供了一些其他的功能,如热点参数限流、系统负载保护、授权规则等,可以根据实际需求进行定制。
常见的限流算法:计数器、令牌桶、漏斗算法、滑动窗口
(1)计算器算法:计数器算法是一种比较简单的限流实现算法,在指定周期内累加访问次数,当访问次数达到设定的阙值时,触发限流策略,当进入下一个时间周期时进行访问次数的清零。
(2)令牌桶算法:是网络流量整形(traffic shaping)和速率限制(Rate Limiting)中最常用的一种算法。对于每个请求,都需要从令牌桶中获得一个令牌,如果没有获得令牌,则需要触发限流策略。
系统会以一个恒定速度(r tokens/sec)往固定容量的令牌桶中放入令牌,如果此时有客户端请求过来,需要先从令牌桶中拿到令牌以获得访问资格。
(3)漏斗算法:令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务。令牌桶的另外一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率. 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量。
(4)滑动窗口:主要作用是控制数据注入网络的速度,平滑网络上的突发流量。
Feign 原理
● 启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。
● RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
● RequestTemplate声场Request,然后将Request交给client处理,这个client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
● 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。
Feign应用过程分析:
1)通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
2) Feign Starter 会在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
3) Feign接口被调用时,底层代理对象会将接口中的请求信息通过编码器创建 Request对象,基于此对象进行远程过程调用。
4) Feign客户端请求对象会经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
5) Feign客户端会携带 Request 调用远端服务并返回一个响应。
6) Feign客户端对象对Response信息进行解析然后返回客户端。
网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务,所以,使用网关的好处在于:
(1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;
(2)降低函数间的耦合度。 一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性
(3)解放开发人员把精力专注于业务逻辑的实现。由网关统一实现服务路由(灰度与ABTest)、负载均衡、访问控制、流控熔断降级等非业务相关功能,而不需要每个服务 API 实现时都去考虑
性能强劲:是第一代网关 Zuul 的 1.6 倍
功能强大:内置了很多实用的功能,例如转发、监控、限流等
设计优雅,容易扩展
其实现依赖 Netty 与 WebFlux,不是传统的 Servlet 编程模型,学习成本高
不能将其部署在 Tomcat、Jetty 等 Servlet 容器里,只能打成 jar 包执行
流量网关和服务网关在系统整体架构中所处的位置如上图所示,流量网关(如Nignx)是指提供全局性的、与后端业务应用无关的策略,例如 HTTPS证书卸载、Web防火墙、全局流量监控等。而微服务网关(如Spring Cloud Gateway)是指与业务紧耦合的、提供单个业务域级别的策略,如服务治理、身份认证等。也就是说,流量网关负责南北向流量调度及安全防护,微服务网关负责东西向流量调度及服务治理。
主要定义了下面的几个信息:
架构图
项目结构:使用聚合工程划分每个功能模块:系统,通用,内容…,再将每个功能模块用聚合工程划分为服务提供端(service)和服务消费者(api,名字自取)。(zx-gateway为后面添加)
流程:添加微服务体系网关–>添加pom文件和修改配置文件–>需要url即客户端请求最终被被转发到的微服务名称(服务消费者)–>服务消费者编写feign接口和controller控制层(需要服务提供者)–>将服务添加到Nacos服务端上
在zx-parent中添加全局配置
注意点:
①Spring Cloud体系需要用dependcyManagement来管理项目的版本
②在添加nacos作为服务端时,如果使用loadbalancer要排除掉ribbon
③gateway和springboot web包冲突,二者需要分开添加,或者在配置文件中强制修改
<packaging>pompackaging>
<properties>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-boot.version>2.7.15spring-boot.version>
<alibaba-cloud.version>2.2.6.RELEASEalibaba-cloud.version>
<springcloud.version>2021.0.5springcloud.version>
properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-loadbalancerartifactId>
dependency>
<dependency>
<groupId>com.alibaba.bootgroupId>
<artifactId>nacos-config-spring-boot-starterartifactId>
<version>0.2.12version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${springcloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${alibaba-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>
dependencies>
dependencyManagement>
①在zx-model-system中添加配置文件
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
②修改zx-system-service的application.yml文件
server:
port: 6061
spring:
application:
name: zx-system-service #nacos注册的服务名
cloud:
nacos:
discovery:
server-addr: 192.168.136.150:8848 #nacos的连接地址
#datasource: 添加自己的数据库配置
#mybatis-plus: 如果使用mybatisplus也添加自身的配置
nacos:
config: #nacos配置中心设定
server-addr: 192.168.136.150:8848
namespace: 50534434-733f-4cbc-89e0-38203ce1b2f6 #创建的dev命名空间
data-id: provider-hello.yml #指定data-id,文件名随便定义
auto-refresh: true #必须设置启动自动刷新,才可实现动态刷新
bootstrap:
enable: true #必须启动bootstrap的支持
log-enable: true
type: yaml #必须设置文件类型,默认properties属性文件
③启动类上添加@EnableDiscoveryClient注解
@SpringBootApplication
@EnableDiscoveryClient //告知启动Nacos,注册到服务端上
public class SystemRunApp {
public static void main(String[] args) {
SpringApplication.run(SystemRunApp.class, args);
}
}
①修改zx-system-api的application.yml文件
server:
port: 6060
spring:
application:
name: zx-system-api #nacos注册名称
cloud:
nacos:
discovery:
server-addr: 192.168.136.150:8848
#datasource:如果提供者用到,需要在消费者添加
②编写feign接口(以登录功能为例)
服务提供者的控制层
feign接口:
注意点:
①需要添加@FeignClient注解,内容为服务提供者配置文件中声明的
②编码规则:Openfeign开发过程中,对请求参数的要求
@FeignClient("zx-system-service") //找到服务提供者,按名称来
public interface LoginFeign {
// URI要唯一,服务消费者和服务提供者的URI一样,启动报错
@PostMapping("/admin/login")
public SysResult login(@RequestBody User user);
@GetMapping("/admin/get/{token}")
public SysResult getUserInfoByToken(@PathVariable("token") String token);
}
③编写controller
controller名字可以与服务提供者的controller一致,否则会报错,
如果不一致,需要修改URI的路径名称
@RestController
@CrossOrigin
public class LoginController {
@Autowired
private LoginFeign loginFeign;
@PostMapping("/admin/login")
public SysResult login(@RequestBody User user){
return loginFeign.login(user);
}
@GetMapping("/admin/get/{token}")
public SysResult getUserInfoByToken(@PathVariable("token") String token){
return loginFeign.getUserInfoByToken(token);
}
}
③在启动类上添加注解
@SpringBootApplication
@EnableDiscoveryClient //nacos客户端
@EnableFeignClients //feign客户端
public class SystemApiRunApp {
public static void main(String[] args) {
SpringApplication.run(SystemApiRunApp.class, args);
}
}
①在zx-gateway中修改pom.xml和application.yml文在这里插入代码片
件
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
dependencies>
server:
port: 5050
spring:
main:
web-application-type: reactive #网关底层使用了响应式异步框架,排除web的servlet框架
application:
name: zx-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.136.150:8848 #注册中心nacos地址
gateway:
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOriginPatterns: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
allowedHeaders: "*" #允许请求中携带的头信息
allowCredentials: true #是否允许携带cookie
maxAge: 36000 #跨域检测的有效期,单位s
routes:
- id: system #名称唯一,访问的URL都加这个目录 http://localhost:5050/system/admin/login
uri: lb://zx-system-api #lb负载均衡,bank-admin-api服务名称
predicates:
- Path=/system/** #断言,/hi开头的就转发上面的uri服务
filters:
- StripPrefix=1 #把上面URL增加的id/去掉
logging:
level:
com.alibaba.nacos.client.*: WARN
②编写SpringBoot启动类
@SpringBootApplication
public class GatewayRunApp {
public static void main(String[] args) {
SpringApplication.run(GatewayRunApp.class, args);
}
}
这里使用Vite构建Vue项目
在vite.config.js中修改
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: "http://localhost:5050",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') //正则表达式
}
}
}
})
在登录js中修改地址为’/api/system/admin/login’