微服务调用方式:
- 基于RestTemplate发起的http请求实现远程调用
- http请求做远程调用是与语言无关的调用,只要知道对方的ip、端口、接口路径、请求参数即可。
微服务架构相对于单体架构,能够带来许多显著的改变和优势,特别是当应用程序的规模和复杂性增加时。
1. 独立的部署和开发
- 独立部署:每个微服务可以独立部署,不需要重新部署整个应用。这提高了部署的灵活性和速度。
- 独立开发:不同的开发团队可以并行地开发不同的微服务,从而提高了开发效率。
2. 技术栈多样性
- 多样化技术栈:不同的微服务可以使用不同的技术栈,根据需求选择最佳工具和技术。例如,一个微服务可以使用 Java,而另一个微服务可以使用 Python 或 Node.js。
3. 扩展性
- 独立扩展:每个微服务可以根据需求独立扩展,而不需要扩展整个应用。这使得系统资源的利用更加高效。
4. 故障隔离
- 故障隔离:如果某个微服务出现故障,不会影响到其他微服务,从而提高了系统的整体稳定性和可靠性。
5. 更好的组织和模块化
- 更好的模块化:微服务架构鼓励将系统划分为独立的、松耦合的模块,每个模块专注于特定的业务功能。这有助于代码的重用和维护。
6. 持续交付和 DevOps
- 持续交付:微服务架构使得持续集成和持续交付(CI/CD)更加容易实现。每个微服务可以独立进行构建、测试和部署。
- DevOps 实践:微服务架构与 DevOps 实践高度契合,可以更好地实现自动化和快速迭代。
Eureka
在Eureka架构中,微服务角色有两类:
1.EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
2.EurekaClient:客户端
Provider:服务提供者,例如案例中的user-service
- 注册自己的信息到EurekaServer
- 每隔30秒向EurekaServer发送心跳
consumer:服务消费者,例如案例中的order-service
- 根据服务名称从EurekaServer拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发起远程调用
注册流程
Eureka Server是服务注册和发现的中心节点。所有微服务(Eureka Clients)将向Eureka Server注册自身的服务信息,并可以从Eureka Server中获取其他服务的信息。
Eureka Client是向Eureka Server注册和发现服务的客户端。
- 启动Eureka Server项目,访问
http://localhost:10086
,你应该能够看到Eureka Server的管理页面。 - 启动Eureka Client项目,Eureka Client会自动向Eureka Server注册自己。刷新Eureka Server管理页面,你应该能够看到注册的客户端服务。
- 模拟多个实例的部署, VM options : -Dserve.port:XX
服务发现是微服务架构中的一个关键部分,允许服务自动注册和发现其他服务,从而实现服务之间的通信和协作。当一个服务(Eureka Client)启动时,它会自动向服务注册中心(Eureka Server)注册自己。服务会发送一个包含自身元数据(如服务名称、IP地址、端口号、运行状态等)的注册请求到Eureka Server。Eureka Server接收到注册请求后,会将该服务的信息存储在一个注册表中。
为了保持服务注册信息的最新状态,Eureka Client会定期(默认每30秒)向Eureka Server发送续约(heartbeat)请求。Eureka Client会发送一个包含服务ID的续约请求到Eureka Server。Eureka Server接收到续约请求后,会更新该服务的最后心跳时间,表示该服务仍然是可用的。
当一个服务需要调用另一个服务时,它会向Eureka Server发送服务查询请求,以获取目标服务的位置信息。Eureka Server根据请求返回目标服务的一个或多个实例信息,包括IP地址和端口号等。
当一个服务需要下线时,它会发送下线请求到Eureka Server。Eureka Server接收到下线请求后,会将该服务从注册表中移除。如果Eureka Server在一定时间内(默认90秒)没有收到某个服务的续约请求,它会将该服务标记为下线,并从注册表中移除。
服务注册时,服务启动,Eureka Client向Eureka Server发送注册请求,Eureka Server存储服务信息。服务续约时,定期(每30秒)发送续约请求,Eureka Server更新服务状态。服务发现时,服务查询其他服务的信息,Eureka Server返回服务实例信息。服务下线时,服务主动发送下线请求或被动超时下线,Eureka Server移除服务信息。
通过RestTemplate或Feign Client来调用其他服务。
@LoadBalanced
注解通常用于在使用Ribbon进行客户端负载均衡时,标记RestTemplate
或WebClient
bean。Ribbon是Spring Cloud中的一个客户端负载均衡器,可以根据负载均衡策略自动分发请求到多个服务实例。
使用@LoadBalanced
注解后,Spring Cloud会自动为标记的RestTemplate
或WebClient
bean集成负载均衡功能。这样,你就可以通过服务名称而不是具体的IP地址和端口来访问服务。Ribbon会根据服务名称解析可用的服务实例,并选择一个进行请求。
Ribbon
Ribbon被集成到Spring Cloud中,为微服务架构提供了客户端负载均衡的能力。Ribbon主要用于在多个服务实例中选择一个进行请求,以实现负载均衡和故障转移。
Ribbon负载均衡规则
规则接口是IRule,默认实现是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
负载均衡自定义方式
- 代码方式:配置灵活,但修改时需要重新打包
- 配置方式:直观,方便,无需重新打包发布,但是无法做全局配置
饥饿加载
开启饥饿加载,指定饥饿加载的微服务名称
Nacos
Nacos支持服务注册、发现、配置管理以及服务健康状态监测。Nacos在微服务架构中被广泛用于服务治理,包括服务注册和发现、配置中心等。
注册流程
安装Nacos Server并启动,可以选择单机模式或集群模式。
单机模式:startup.cmd -m standalone
服务注册
注册中心
Nacos服务分级存储模型
一级是服务,例如userservice
二级是集群,例如杭州或上海
三级是实例,例如杭州机房的某台部署了userservice的服务器
集群属性
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高,本地集群不可访问时,再去访问其它集群。
修改application.yml文件,添加spring.cloud.nacos.discovery.cluster-name属性即可
负载均衡和权重控制
NacosRule负载均衡策略
- 优先选择同集群服务实例列表
- 本地集群找不到提供者,才去其它集群寻找,并且会报警告
- 确定了可用实例列表后,再采用随机负载均衡挑选实例
实例的权重控制
- Nacos控制台可以设置实例的权重值,0~1之间
- 同集群内的多个实例,权重越高被访问的频率越高
- 权重设置为0则完全不会被访问
环境隔离
Nacos的namespace用来做环境隔离,每个namespace都有唯一id,不同namespace下的服务不可见。
Nacos和Eureka区别
Nacos与eureka的共同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别
- Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
- Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式(A:高可用 C:一致性 P:分区容错性)
Feign
Feign 是一个基于 Java 的声明式、模板化的 HTTP 客户端,它简化了编写基于 HTTP 的客户端代码的过程。Feign 的作用主要在于简化了远程服务的调用,尤其是针对 RESTful API,提高了开发效率,降低了代码复杂度,使得开发者可以更加专注于业务逻辑的实现而不必过多关注远程调用的细节。
使用Feign的过程
Feign的自定义配置
1.方式一是配置文件yml,feign.client.config.xxx.loggerLevel
- ①如果xxx是default则代表全局
- ②如果xxx是服务名称,例如userservice则代表某服务
2.方式二是java代码配置Logger.Level这个Bean
- ①如果在@EnableFeignClients注解声明则代表全局
- ②如果在@FeignClient注解中声明则代表某服务
Feign的优化
优化措施:
- 日志级别尽量用basic或none
- 使用HttpClient或OKHttp代替URLConnection
引入feign-httpClient依赖
配置文件开启httpClient功能,设置连接池参数
Feign的实践
- 让controller和FeignClient继承同一接口
- 将FeignClient、POJO、Feign的默认配置都定义到一个项目中,供所有消费者使用。
开启Feign的sentinel功能
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
Gateway
Spring Cloud Gateway 是 Spring Cloud 生态系统中的一个组件,用于构建基于 Spring Boot 的 API 网关服务。API 网关是一个入口点,它允许你在一个统一的位置管理、监控和控制你的微服务架构中的所有 API 请求。Spring Cloud Gateway 提供了灵活的路由方式、过滤器链和可插拔的功能,使得它成为构建微服务架构中的前置代理非常方便的工具。
通过 Spring Cloud Gateway,可以实现身份认证、权限校验,服务路由、负载均衡,请求限流等功能,从而提高了整个微服务架构的可维护性、可观察性和安全性。
搭建网关服务步骤
1.创建 Spring Boot 项目: 使用 Spring Initializr 或者手动创建一个基于 Spring Boot 的项目。确保项目中包含必要的依赖,如 spring-boot-starter-web
、spring-cloud-starter-gateway
等。
2.配置 Spring Cloud Gateway: 在项目的配置文件(例如 application.properties
或 application.yml
)中配置 Gateway 的路由、过滤器等信息。你可以配置路由规则、请求转发目标、过滤器链等。
路由配置包括:
- 1. 路由id:路由的唯一标示
- 2.路由目标(uri):路由的目标地址,http代表固定地址,b代表根据服务名负载均衡,支持lb和http两种
- 3.路由断言(predicates):判断路由的规则,判断请求是否符合要求,符合则转发到路由目的地
- 4.路由过滤器(filters):对请求或响应做处理
路由断言工厂Route Predicate Factory
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件,例如Path=/user/**是按照路径匹配,这个规则是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的
过滤器
Spring Cloud Gateway 中的过滤器允许开发者在请求被路由之前或者响应被返回给客户端之前对请求和响应进行一系列的处理。过滤器提供了一种灵活的机制,可以用于实现各种功能,例如鉴权、日志记录、请求重试、流量控制等。Gateway 中的过滤器可以通过配置文件或者 Java 代码进行定义和配置。
Spring Cloud Gateway 提供了两种类型的过滤器:
-
全局过滤器: 全局过滤器对所有的路由都生效,它们可以用于实现一些通用的功能,如权限验证、日志记录等。全局过滤器可以通过实现
GlobalFilter
接口来定义。 -
路由过滤器: 路由过滤器只对指定的路由生效,它们可以用于实现一些特定的功能,如请求转发、请求修改等。路由过滤器可以通过实现
GatewayFilter
接口来定义。
全局过滤器
作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFiter的逻辑需要自己写代码实现。定义方式是实现GlobalFilter接口。
/**
* 处理请求并将其传递给下一个过滤器
* @param exchange 当前请求的上下文,其中包含request、response等各种数据
* @param chain 过滤器链,基于它向下传递请求
* @return 根据返回值标记当前请求是否被完成或拦截,chain.filter(exchange)就放行了。
*/
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
实现步骤:1.实现GlobalFilter接口2.添加@Order注解或实现Ordered接口3.编写处理逻辑
自定义过滤器
路由过滤器分为全局过滤器defaultFilters和局部过滤器
过滤器执行顺序
跨域资源共享(CORS)
在使用 Spring Cloud Gateway 进行 API 网关服务时,可能会遇到跨域资源共享(CORS)的问题,特别是当网关代理前端应用程序时。跨域问题通常是由于浏览器的安全策略限制导致的,为了解决跨域问题,可以在 Spring Cloud Gateway 中配置 CORS 支持。
-
添加 CORS 过滤器: 在 Spring Cloud Gateway 中配置一个 CORS 过滤器,用于在响应中添加 CORS 相关的头部信息。可以通过编写一个全局过滤器或者路由过滤器来实现。
-
配置允许跨域的域名: 在 CORS 过滤器中配置允许跨域请求的域名、方法、头部等信息。这样可以确保只有指定的域名可以跨域访问网关服务。
Sleuth
Spring Cloud Sleuth 是 Spring Cloud 提供的分布式追踪解决方案。它自动为你的微服务应用添加唯一的标识符(TraceId 和 SpanId),并将这些标识符传播到调用链的所有部分,从而使你能够追踪一个请求的整个生命周期。
主要功能
- TraceId: 表示一整个请求调用链的唯一标识符。
- SpanId: 表示一个基本工作单元的唯一标识符,一个 Trace 可以包含多个 Span。
- 自动注入: Sleuth 会自动注入这些标识符到日志、HTTP Headers 和消息中(如 Kafka、RabbitMQ)。
- 集成: Sleuth 可以与各种日志系统和分布式追踪系统集成,如 Zipkin。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
在 application.yml
中进行基本配置:
spring:
sleuth:
sampler:
probability: 1.0 # 采样概率,1.0 表示采样所有请求
运行应用时,Sleuth 会自动将 TraceId 和 SpanId 添加到日志条目中:
Zipkin
Zipkin 是一个分布式追踪系统,用于收集和分析微服务间的延迟数据。它可以帮助你理解请求在系统中流动的路径,并识别性能瓶颈。
主要功能
- 数据收集: Zipkin 收集来自各种服务的追踪数据。
- 数据存储: Zipkin 可以将数据存储在各种存储后端,如内存、MySQL、Elasticsearch 等。
- 数据查询: Zipkin 提供一个 Web UI,用于查询和可视化追踪数据。
- 分析: Zipkin 可以显示请求路径中的每个 Span 的时间和依赖关系,帮助你找到性能瓶颈。
通过 Docker 运行 Zipkin 服务器:
docker run -d -p 9411:9411 openzipkin/zipkin
配置 Sleuth 发送数据到 Zipkin
spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0 # 采样概率,1.0 表示采样所有请求
打开浏览器访问 http://localhost:9411
,你可以看到 Zipkin 的 Web UI,在那里你可以查询和可视化追踪数据。
结合 Sleuth 和 Zipkin,你可以轻松实现分布式追踪。
比如在gateway中引入zipkin的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.8.RELEASE</version>
在Kafka和其他模块中引入sleuth的依赖,通过日志中的traceID,确定不同模块下哪些是一次请求,可以将trace ID通过Kafka写入日志中:
@KafkaListener(topics = "inventory-log", groupId = "inventory-log-group")
public void listen(String logMessage) {
long traceId = tracer.currentSpan().context().traceId();
String traceIdString = Long.toUnsignedString(traceId);
LocalDateTime time = LocalDateTime.now();
inventoryLogMapper.insertLog(logMessage, time, traceIdString);
log.info("日志记录:{}", logMessage);
}
级联调用是指一个服务调用另一个服务,后者又调用其他服务,形成一个调用链。这种调用链可以带来一些挑战,比如延迟累积、故障传播等。
保证服务运行的健壮性,避免级联失败导致的雪崩问题,就属于微服务保护。
请求限流,降低了并发上限;线程隔离,降低了可用资源数量;服务熔断,降低了服务的完整度,部分服务变的不可用或弱可用。
Sentinel
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentinel 控制台地址
port: 8719 # Sentinel 客户端端口
eager: true # 是否开启启动预热
http-method-specify: true # 开启请求方式前缀
在簇点链路后面点击流控按钮,即可对其做限流配置:对QPS进行限制
线程隔离:设置并发线程数
触发限流或熔断后的请求不一定要直接报错,也可以返回一些默认数据或者友好提示,用户体验会更好。
给FeignClient编写失败后的降级逻辑有两种方式:
-
方式一:FallbackClass,无法对远程调用的异常做处理
-
方式二:FallbackFactory,可以对远程调用的异常做处理,我们一般选择这种方式。
Sentinel中的断路器不仅可以统计某个接口的慢请求比例,还可以统计异常请求比例。当这些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正常时,再放行对于该接口的请求。
Seata
RabbitMQ
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
host: # 你的虚拟机IP
port: 5672 # 端口
virtual-host: /test # 虚拟主机
username: test01 # 用户名
password: 01 # 密码