SpringCloud
微服务架构
随着互联网的不断发展,软件系统的架构也是在不断的更新。由原先的单体架构逐渐演变成分布式系统架构,再到目前非常主流的微服务系统架构。
分布式系统架构是指将一个软件系统分割成多个独立的服务,并且这些服务可以在不同的计算机或服务器上运行,并通过网络进行通信。
微服务系统架构:本质上也属于分布式系统架构,在微服务系统架构中,更加重视的是服务拆分粒度。
微服务架构的特点:
1、单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
2、自治:团队独立、技术独立、数据独立,独立部署和交付
3、面向服务:服务提供统一标准的接口,与语言和技术无关
微服务系统架构的优点:
1、可扩展性好:由于系统中的不同组件可以独立地进行扩展和升级,从而提高了整个系统的扩展性和可靠性。
2、容错性高:由于系统中的组件可以在不同的计算机或服务器上运行,因此即使某些节点出现故障也不会影响整个系统的运行。
3、高效性强:分布式系统可以将负载和任务分配到不同的节点上,从而提高系统的并发能力和处理速度。
4、灵活性强:分布式系统可以支持多种编程语言和应用程序框架,并且可以利用各种云计算技术,如Docker、Kubernetes等。
微服务系统架构的存在的问题:
1、微服务的管理:这些微服务如果没有进行统一的管理,那么维护性就会极差。
2、服务间的通讯:微服务之间肯定是需要进行通讯,比如购物车微服务需要访问商品微服务。
3、前端访问问题:由于每一个微服务都是部署在独立的一台服务器的,每一个微服务都存在一个对应的端口号,前端在访问指定微服务的时候肯定需要指定微服务的ip地址和端口号,难道需要在前端维护每一个微服务的ip地址和端口号?
4、配置文件管理:当构建服务集群的时候,如果每一个微服务的配置文件还是和微服务进行绑定,那么维护性就极差。
微服务技术对比
Dubbo | SpringCloud | SpringCloudAlibaba | |
---|---|---|---|
注册中心 | zookeeper、Redis | Eureka、Consul | Nacos、Eureka |
服务远程调用 | Dubbo协议 | Feign(http协议) | Dubbo、Feign |
配置中心 | 无 | SpringCloudConfig | SpringCloudConfig、Nacos |
服务网关 | 无 | SpringCloudGateway、Zuul | SpringCloudGateway、Zuul |
服务监控和保护 | dubbo-admin,功能弱 | Hystrix | Sentinel |
企业需求
Spring Cloud Alibaba概述
针对微服务系统架构所存在的问题,肯定是需要有具体的技术来解决,而所使用到的技术就是Spring Clouad Alibaba。那么想要了解Spring Clouad Alibaba,那么就需要先了解一下Spring Cloud。
1、Spring Cloud 是一系列框架的有序集合。在Spring Cloud这个项目中包含了很多的组件【子框架】,每一个组件都是用来解决问题系统架构中所遇到的问题,因此Spring Cloud可以看做是一套微服务的解决方案。
2、Spring Cloud中常见的组件:Eureka(服务注册中心)、Openfeign(服务远程调用)、Gateway(服务网关)、Spring Cloud Config(统一配置中心)等。
3、Spring Cloud项目官方网址:https://spring.io/projects/spring-cloud
4、Spring Cloud依赖于Spring Boot,并且有版本的兼容关系,如下所示:
Spring Cloud Alibaba简介
Spring Cloud Alibaba是阿里针对微服务系统架构所存在的问题给出了一套解决方案,该项目包含了微服务系统架构必须的一些组件。
常见的组件可以参看官网地址:https://spring-cloud-alibaba-group.github.io/github-pages/2021/en-us/index.html
注意:
1、Spring Cloud Alibaba中所提供的组件是遵循Spring Cloud规范的,两套技术所提供的组件是可以搭配使用的。
2、在现在企业开发中往往是两套技术组件搭配进行使用:Nacos(服务注册中心和配置中心)、Openfeign(远程调用)、Ribbon(客户端负载均衡器)、Gateway(服务网关)、Sentinel(服务保护组件)等。
服务远程调用(RestTemplate)
RestTemplate
是 Spring Framework 提供的一个同步 HTTP 客户端,用于在 Spring 应用程序中方便地进行 RESTful 服务的调用。它封装了底层的 HTTP 客户端,实现了与 RESTful API 进行交互的常见模式,简化了 HTTP 请求和响应的处理。
主要特点
- 简化的 API:
RestTemplate
提供了一系列方法来执行 HTTP 请求,例如getForObject
,getForEntity
,postForObject
,postForEntity
,put
,delete
等,使得调用 REST 接口非常简单。 - 支持多种 HTTP 方法:可以使用
RestTemplate
发起 GET、POST、PUT、DELETE 等多种类型的请求。 - 对象转换:
RestTemplate
使用HttpMessageConverter
将 HTTP 请求和响应体中的 JSON/XML 数据与 Java 对象之间进行自动转换,减少了手动解析的工作。 - 错误处理:可以通过实现
ResponseErrorHandler
接口来自定义错误处理机制。 - 支持 URI 模板:可以使用 URI 模板(例如
{id}
)来构建动态的 URL。 - 集成:可以与 Spring 的其他部分(如 Spring Boot)无缝集成,提供易用的配置方式。
配置 RestTemplate
在 Spring Boot 应用中,通常可以通过配置类来定义 RestTemplate
的 Bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
使用示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
@Autowired
private RestTemplate restTemplate;
public User getUserById(Long id) {
String url = "https://api.example.com/users/{id}";
return restTemplate.getForObject(url, User.class, id);
}
public void createUser(User user) {
String url = "https://api.example.com/users";
restTemplate.postForObject(url, user, User.class);
}
}
注册中心产品
本小结主要给大家来介绍一下常见的注册中心的产品。
Eureka
Eureka是Netflix开源的一个基于REST的服务治理框架,主要用于实现服务注册、发现和负载均衡。通过Eureka,我们可以将微服务的各个实例注册到服务中心,并根据需要进行负载均衡和调用,从而实现整个微服务架构的高可用和弹性。
Eureka包含两个组件:Eureka Server和Eureka Client。
服务提供者在启动时会通过Eureka Client向Eureka Server注册自己的信息(包括IP地址、端口号和服务名等),并且每隔一段时间会发送心跳来告诉Eureka Server它仍然存活。服务消费者可以通过Eureka Client从Eureka Server获取服务提供者的列表,并对这些服务进行负载均衡和调用。
Eureka的优点包括:
1、简单易用:Eureka框架非常简单易用,便于快速上手和部署。
2、高可用性:Eureka支持多节点部署,并会自动将失效的节点剔除,确保整个系统的高可用性和弹性。
3、动态扩展性:Eureka可以根据实际需求进行扩展,通过添加新的服务提供者可以很容易地增加应用程序的处理能力。
4、易于集成:Eureka可以与Spring Cloud等流行的微服务框架进行无缝集成,从而提供更完善的微服务体系支持。
Eureka的不足之处:
1、Eureka Server 为单点故障问题,虽然可以通过多节点部署来优化和缓解,但是在高并发场景下仍可能成为限制系统扩展的瓶颈。
2、Eureka的服务注册中心本身也需要高可用环境,一旦出现问题,可能影响到整个微服务的正常运行。
官网地址:https://docs.spring.io/spring-cloud-netflix/docs/current/reference/html/
Nacos
Nacos官网地址:https://nacos.io/zh-cn/
Nacos是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos Server:服务注册中心,它是服务,其实例及元数据的数据库。服务实例在启动时注册到服务注册表,并在关闭时注销。服务注册中心可能会调用服务实例的健康检查 API 来验证它是否能够处理请求。Nacos Server需要独立的部署。
Nacos Client: Nacos Client负责和Nacos Server进行通讯完成服务的注册和服务的发现。
Nacos Console:是Nacos的控制模块,Nacos提供了可视化的后台管理系统,可以很容易的实现服务管理操作。
Nacos的优点包括:
1、高可用性:Nacos支持多节点部署,通过选举算法实现了高可用和故障转移能力,在节点宕机或网络异常情况下仍能保证整个系统的稳定运行。
2、动态扩展性:Nacos可以根据实际需求进行快速扩展和缩容,支持集群、多数据中心、地域感知等特性。
3、完备的功能支持:Nacos支持服务注册与发现、配置管理、流量管理、DNS解析、存储KV对等功能,并且提供了Web界面和RESTful API等多种方式来使用这些功能。
4、易于集成:Nacos提供了多种语言和框架的集成方案,并且支持Spring Cloud等流行的微服务框架。
总的来说,Nacos是一个功能齐全、易于使用和高可用的分布式服务治理平台,可以为分布式系统提供高效、稳定的运行环境。
Nacos注册中心
通过注册中心可以对服务提供方和服务消费方进行解耦。
工作流程说明:
1、服务提供方在启动的时候,会向注册中心注册自己服务的详情信息(ip、端口号等)。在注册中心中会维护一张服务清单,保存这些注册信息,注册中心需要以心跳的方式去监测清单中的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
2、服务消费方向服务注册中心咨询服务,并获取所有服务的实例清单,然后按照指定的负载均衡算法从服务清单中选择一个服务实例进行访问。
Nacos入门
Windows环境安装Nacos
-
解压Nacos安装文件到没有中文和空格目录
-
进入bin目录,使用cmd打开,通过命令启动Nacos服务
startup.cmd -m standalone
- 打开浏览器访问nacos的所提供的后端管理界面:http://localhost:8848/nacos
用户名和密码:nacos/nacos,登录成功以后会进入到nacos的主页面
微服务注册到nacos中
1、在工程中引入如下依赖
<!-- nacos作为注册中心的依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2、在application.yml文件中添加如下配置
spring:
# 配置nacos注册中心的地址
cloud:
nacos:
discovery:
server-addr: localhost:8848 #nacos注册中心的IP加端口号
application:
name: yourServiceName # 每一个服务注册到nacos注册中心都需要提供一个服务名称,order微服务注册的时候需要更改微服务名称
3、启动微服务:就可以在nacos的后台管理系统中看到
另外,我们可以将service多次启动, 模拟多实例部署,但为了避免端口冲突,需要修改端口设置:
远程调用Nacos
当我们把微服务都注册到注册中心以后,那么此时就可以根据服务的名称从注册中心获取服务的ip地址和端口号了,就可以使用远程调用。
1、RestTemplate
只使用restTemplate对远程接口进行调用
2、DiscoveryClient
使用到Spring Cloud中所提供的一个服务发现的客户端对象:DiscoveryClient
在 Spring Cloud 中,DiscoveryClient
是实现微服务之间通信的核心组件之一。具体实现通常通过 EurekaDiscoveryClient
或 ConsulDiscoveryClient
来完成。
分布式系统:在任何需要微服务协作的分布式系统中,服务发现机制都是必不可少的,它让微服务能够动态地找到和调用彼此。
@Service
public class OrderServiceImpl implements OrderService {
@Autowired // 注入RestTemplate远程调用工具
private RestTemplate restTemplate ;
@Autowired
private DiscoveryClient discoveryClient ;
@Autowired
private OrderMapper orderMapper ;
@Override
public Order findOrderByOrderId(Long orderId) {
// 根据id查询订单数据
Order order = orderMapper.findOrderByOrderId(orderId);
// 根据服务名称从注册中心中获取服务实例列表
ServiceInstance serviceInstance = chooseServiceInstance("chs-cloud-user");
// 发起远程调用
User user = restTemplate.getForObject("http://" + serviceInstance.getHost() +":" + serviceInstance.getPort() +"/api/user/findUserByUserId/" + order.getUserId(), User.class);
order.setUser(user);
// 返回订单数据
return order;
}
// 根据服务的名称从注册中心中获取服务地址信息
public ServiceInstance chooseServiceInstance(String applicationName) {
// 获取服务实例列表
List<ServiceInstance> instances = discoveryClient.getInstances(applicationName);
// 编写一个简易的随机负载均衡算法
int size = instances.size();
Random random = new Random() ;
int instanceIndex = random.nextInt(size);
ServiceInstance serviceInstance = instances.get(instanceIndex);
// 返回服务实例
return serviceInstance ;
}
}
在 Spring Cloud 的使用场景中,开发者可以在应用程序类中添加
@EnableDiscoveryClient
注解,使应用能够作为一个服务注册到服务注册中心:
java@SpringBootApplication
@EnableDiscoveryClient
public class MyMicroserviceApplication {
public static void main(String[] args) {
SpringApplication.run(MyMicroserviceApplication.class, args);
}
}
3、spring-cloud-loadbalancer
spring-cloud-loadbalancer
是 Spring Cloud 的一部分,提供了一个轻量级的负载均衡解决方案,用于在微服务架构中处理服务间的请求分发。它允许你在使用 Spring Cloud 的环境中轻松实现客户端负载均衡。
主要特点
客户端负载均衡:spring-cloud-loadbalancer
实现了客户端负载均衡的概念,由客户端自己选择后端服务实例,而不是通过外部负载均衡器。这种方式可以减少延迟,提高响应速度。
集成 Spring Cloud:与其他 Spring Cloud 项目(如 Eureka、Consul 和 Spring Cloud Gateway)深度集成,支持从这些服务发现工具中获取服务实例信息。
可插拔的负载均衡策略:用户可以自定义负载均衡策略,Spring Cloud LoadBalancer 支持多种内置的负载均衡算法,如轮询、随机、接口哈希等。
透明性:通过使用 Spring Cloud LoadBalancer,开发者可以将负载均衡的配置与业务逻辑分开,使代码更加简洁和易于管理。
与 WebFlux 集成:spring-cloud-loadbalancer
完全支持反应式编程模型(WebFlux),允许在响应式应用程序中使用负载均衡。
使用步骤:
1、在order微服务中添加依赖
<!-- spring cloud 所提供的负载均衡器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
2、在声明RestTemplate的方法上添加@LoadBalanced注解
@Configuration
public class AppConfig {
@Bean
@LoadBalanced // 让RestTemplate具有负载均衡的能力
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
3、更改远程调用代码
// 服务提供方的服务ip地址和端口号可以使用服务提供方的服务名称进行替换
restTemplate.getForObject("http://chs-cloud-user/api/user/findUserByUserId/" + order.getUserId(), User.class);
注意:默认使用的负载均衡算法就是轮询【依次调用对应服务】
4、OpenFeign+loadbalancer(重点)
使用OpenFeign.+loadbalancer组件进行远程服务调用(最推荐的一种方式)
feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign其作用就是帮助我们优雅的实现http请求的发送。
OpenFeign 是一个声明式的 HTTP 客户端,简化了在 Java 应用中调用 RESTful 服务的开发。它适用于微服务架构,使得服务间的调用更加简洁和易于维护。当 OpenFeign 与负载均衡器结合使用时,可以有效地在多个服务实例之间分配请求,从而提高可用性和容错能力。
OpenFeign 与负载均衡的结合
1、添加依赖:
在 pom.xml
中加入 OpenFeign 和负载均衡器的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2、启用 OpenFeign:
在主应用程序类中添加
@EnableFeignClients
注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
//开启openFeign远程调用,默认在当前启动类所在的包及子包下去扫描openFeign接口
@EnableFeignClients(vlaue="指定扫描的包名")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
3、定义 Feign 客户端:
创建接口并使用 Feign 注解定义请求
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "my-service") //nacos中服务器的名字
public interface MyServiceClient {
//无参
@GetMapping("/api/data") //请求数据的uri
String getData();
//有参
@GetMapping("/api/user/findUserByUserId/{userId}") //请求数据的uri
public abstract User queryById(@PathVariable("userId") Long userId) ; // 根据userId查询用户信息的接口方法
}
注意==》如果参数过多可以创建一个类,实现该接口进行操作
4、调用 Feign 客户端:
通过自动注入的 Feign 客户端调用服务,负载均衡器会根据配置处理请求。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private MyServiceClient MyServiceClient ;
@GetMapping("/fetch-data")
public String fetchData() {
//调用自己创建的接口
return myServiceClient.getData();
}
@GetMapping(value = "/findUserByUserId/{userId}")
public User findUserByUserId(@PathVariable("userId") Long userId) {
User user = myServiceClient.queryById(id);
return user ;
}
}
请求分发: 负载均衡器能够智能地将请求分发到不同的服务实例,避免某个实例过载。
故障转移: 如果一个服务实例不可用,负载均衡器可以自动将请求切换到其他健康的实例,提高系统可靠性。
简化开发: OpenFeign 通过注解的方式简化了 HTTP 请求的构建,可以轻松与负载均衡器集成。
Nacos高级特性
服务集群
在实际生产环境中,为了保证每一个服务的高可用,那么此时就需要去构建服务集群,但是并不是说把所有的服务都部署在一个机房里。而是将多个服务分散的部署到不同的机房中,每一个机房的服务可以看做成是一个集群。
微服务互相访问时,应该尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群。例如:上海机房内的order微服务应该优先访问同机房的user微服务。
集群配置
- 修改chs-cloud-user的application.yml文件,添加集群配置:
spring:
cloud:
nacos:
discovery:
cluster-name: SH # 配置服务所属集群
启动三个服务user微服务实例,实例所属集群分配情况:实例1属于SH,实例2和实例3属于BJ
- 通过添加添加JVM参数更改服务实例所属集群,启动实例2和实例3
实例2:10101
-Dserver.port=10101 -Dspring.cloud.nacos.discovery.cluster-name=BJ
实例3:10103
-Dserver.port=10103 -Dspring.cloud.nacos.discovery.cluster-name=BJ
结果:
集群访问
NacosLoadBalancer
优先同集群内进行随机的负载均衡。如果统计群内的目标服务实例均不可用,此时跨集群随机负载均衡。
需求:当order服务优先访问SH集群中的user微服务实例,当SH集群中的user微服务实例出现问题以后,在访问BJ集群中的实例。
步骤:
1、给order微服务的application.yml文件,添加集群配置:
spring:
cloud:
nacos:
discovery:
cluster-name: SH # 配置服务所属集群
2、order微服务在loadbalancer组件中集成nacos
spring:
# 配置nacos注册中心的地址
cloud:
loadbalancer:
nacos: # 集成nacos的负载均衡算法
enabled: true
权重配置
实际部署中会出现这样的场景:服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
在Nacos控制台,找到chs-cloud-user的实例列表,点击编辑,即可修改权重:
权重取值范围:0~100
- 在配置文件中进行权重配置:
spring:
cloud:
nacos:
discovery:
weight: 0.1
注意:如果权重修改为0,则该实例永远不会被访问
环境隔离
在实际的开发过程中,可能会存在很多个软件环境:开发环境、测试环境、生产环境。
nacos也是支持多环境隔离配置的,在nacos是通过namespace来实现多环境的隔离。
namespace + group 才可以确定具体的微服务实例。默认情况下,所有service、group都在同一个namespace,名为public
创建名称空间
我们也可以创建新的名称空间,来将不同的服务隔离到不同的环境下面。
微服务配置名称空间
给微服务添加名称空间的配置,来指定该微服务所属环境。
例如,修改chs-cloud-order的application.yml文件
spring:
# 配置nacos注册中心的地址
cloud:
nacos:
discovery:
namespace: 4a88035e-acf3-45a9-924f-2421acbff67a # 配置服务实例所属名称空间
此时order微服务所对应的服务实例就属于新的名称空间,user微服务所对应的服务实例属于public的名称空间,那么此时在进行远程调用的时候,就会出现如下的错误:
微服务配置分组
cloud:
nacos:
discovery:
server-addr: 192.168.126.1:8848
group: groupName #分组的名称
同一个命名空间下,还可以创建多个组,默认的组DEFAULT GROUP,命名空间和组,用于实现环境的隔离,项目的隔离…一种隔离机制。
注意:微服务的发布和调用,针对【同一个命名空间和组】进行的。
实例类型
Nacos中的服务实例存在两种类型:
1、临时实例:如果实例宕机超过一定时间,会从服务列表剔除,并且实例会定时上报自身的健康状态给Nacos注册中心,默认的类型。
2、非临时实例:如果实例宕机,不会从服务列表剔除,Nacos注册中心会主动询问实例的健康状态,也可以叫永久实例。
配置一个服务实例为永久实例:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 配置该实例为非临时实例
LoadBalancer
Spring Cloud LoadBalancer是Spring Cloud中负责客户端负载均衡的模块,其主要原理是通过选择合适的服务实例来实现负载均衡。
客户端负载均衡:就是负载均衡算法由客户端提供
LoadBalancer原理
Spring Cloud LoadBalancer的底层采用了一个拦截器【LoadBalancerInterceptor】,拦截了RestTemplate发出的请求,对地址做了修改
执行流程说明:
1、通过LoadBalancerInterceptor请求拦截器拦截我们的RestTemplate请求:http://chs-cloud-user/api/user/findUserByUserId/1
2、获取请求的url,然后从请求的url中获取服务提供方的主机名称
3、然后调用LoadBalancerClient中的execute方法,将服务提供方的名称传递过去
4、在LoadBalancerClient的choose方法中通过ReactiveLoadBalancer.Factory从Nacos注册中心中获取服务列表以及负载均衡算法实例对象
5、通过ReactiveLoadBalancer从服务列表中选择一个服务实例地址,然后发起远程调用
源码跟踪
LoadBalancerInterceptor
核心源码如下所示:
可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了几件事:
1、request.getURI()
:获取请求uri,本例中就是 http://chs-cloud-user/api/user/findUserByUserId/1
2、originalUri.getHost()
:获取uri路径的主机名,其实就是服务id,chs-cloud-user
3、this.loadBalancer.execute()
:处理服务id,和用户请求。
这里的this.loadBalancer
是BlockingLoadBalancerClient
类型,我们继续跟入。
BlockingLoadBalancerClient
核心源码如下所示:
ReactiveLoadBalancer.Factory的getInstance方法做了两件事情:
1、获取了一个具体的负载均衡算法对象
2、根据服务的id从Nacos注册中心中获取服务地址列表
紧跟着调用了RoundRobinLoadBalancer#choose方法,从服务列表中选择一个服务实例对象。
默认的负载均衡算法:RoundRobinLoadBalancer
更改负载均衡算法
LoadBalancer默认的负载均衡算法是RoundRobinLoadBalancer,如果想更改默认的负载均衡算法,那么此时需要向Spring容器中注册一个Bean,并且配置负载均衡的使用者。
随机负载均衡策略
1、在Spring容器中注册一个Bean
public class CustomLoadBalancerConfiguration {
/**
* @param environment: 用于获取环境属性配置,其中LoadBalancerClientFactory.PROPERTY_NAME表示该负载均衡器要应用的服务名称。
* @param loadBalancerClientFactory: 是Spring Cloud中用于创建负载均衡器的工厂类,通过getLazyProvider方法获取ServiceInstanceListSupplier对象,以提供可用的服务列表。
* ServiceInstanceListSupplier:用于提供ServiceInstance列表的接口,可以从DiscoveryClient或者其他注册中心中获取可用的服务实例列表。
*
*/
@Bean
ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory)
{
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);
}
}
注意:Bean的返回值类型为==》ReactorLoadBalancer
2、配置负载均衡算法的使用者
@Configuration
@LoadBalancerClients(value = {
@LoadBalancerClient(name = "chs-cloud-user" , configuration = CustomLoadBalancerConfiguration.class) // 将负载均衡算法应用到指定的服务提供方中
})
public class RestTemplateConfiguration {
@Bean
@LoadBalanced // 让RestTemplate具有负载均衡的能力
public RestTemplate restTemplate() {
return new RestTemplate() ;
}
}
OpenFeign组件
使用OpenFeign.+loadbalancer组件进行远程服务调用(最推荐的一种方式)
feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign其作用就是帮助我们优雅的实现http请求的发送。
OpenFeign 是一个声明式的 HTTP 客户端,简化了在 Java 应用中调用 RESTful 服务的开发。它适用于微服务架构,使得服务间的调用更加简洁和易于维护。当 OpenFeign 与负载均衡器结合使用时,可以有效地在多个服务实例之间分配请求,从而提高可用性和容错能力。
OpenFeign 与负载均衡的结合
1、添加依赖:
在 pom.xml
中加入 OpenFeign 和负载均衡器的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2、启用 OpenFeign:
在主应用程序类中添加
@EnableFeignClients
注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
//开启openFeign远程调用,默认在当前启动类所在的包及子包下去扫描openFeign接口
@EnableFeignClients(vlaue="指定扫描的包名")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
3、定义 Feign 客户端:
创建接口并使用 Feign 注解定义请求
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "my-service") //nacos中服务器的名字
public interface MyServiceClient {
//无参
@GetMapping("/api/data") //请求数据的uri
String getData();
//有参
@GetMapping("/api/user/findUserByUserId/{userId}") //请求数据的uri
public abstract User queryById(@PathVariable("userId") Long userId) ; // 根据userId查询用户信息的接口方法
}
注意==》如果参数过多可以创建一个类,可以接收一个对象。
4、调用 Feign 客户端:
通过自动注入的 Feign 客户端调用服务,负载均衡器会根据配置处理请求。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private MyServiceClient MyServiceClient ;
@GetMapping("/fetch-data")
public String fetchData() {
//调用自己创建的接口
return myServiceClient.getData();
}
@GetMapping(value = "/findUserByUserId/{userId}")
public User findUserByUserId(@PathVariable("userId") Long userId) {
User user = myServiceClient.queryById(id);
return user ;
}
}
请求分发: 负载均衡器能够智能地将请求分发到不同的服务实例,避免某个实例过载。
故障转移: 如果一个服务实例不可用,负载均衡器可以自动将请求切换到其他健康的实例,提高系统可靠性。
简化开发: OpenFeign 通过注解的方式简化了 HTTP 请求的构建,可以轻松与负载均衡器集成。
OpenFeign自定义配置
日志配置
OpenFeign可以支持很多的自定义配置,如下表所示:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign.Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
日志两种方式的配置
1、基于配置文件的方式
基于配置文件修改feign的日志级别可以针对单个服务:
# 1、将feign包下产生的日志的级别设置为debug
logging:
level:
com.chs.chs.cloud.order.feign: debug
# 2、openfeign日志级别配置
spring:
cloud:
openfeign:
client:
config:
chs-cloud-user:
loggerLevel: full
也可以针对所有服务:
# 1、将feign包下产生的日志的级别设置为debug
logging:
level:
com.chs.chs.cloud.order.feign: debug
# 2、openfeign日志级别配置
spring:
cloud:
openfeign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: full
而日志的级别分为四种:
① NONE:不记录任何日志信息,这是默认值。
② BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
③ HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
④ FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
2、Java代码的方式
基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC; // 日志级别为BASIC
}
}
如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
如果是局部生效,则把它放到对应的@FeignClient这个注解中:
@FeignClient(value = "chs-cloud-user", configuration = DefaultFeignConfiguration .class)
超时配置
超时机制概述:Feign 的超时机制是指在使用 Feign 进行服务间的 HTTP 调用时,设置请求的超时时间。当请求超过设定的超时时间后,Feign 将会中断该请求并抛出相应的异常。
超时机制的意义:
1、防止长时间等待:通过设置适当的超时时间,可以避免客户端在请求服务时长时间等待响应而导致的性能问题。如果没有超时机制,客户端可能会一直等待,从而影响整个系统的吞吐量和响应时间。
2、避免资源浪费:超时机制可以帮助及时释放占用的资源,例如连接、线程等。如果请求一直处于等待状态而不超时,将导致资源的浪费和系统的负载增加。
3、优化用户体验:超时机制可以防止用户长时间等待无响应的情况发生,提供更好的用户体验。当请求超时时,可以及时给出错误提示或进行相应的处理,以提醒用户或采取其他措施。
feign默认的超时配置为:建立连接的超时时间为:10s,读取数据的超时时间为:60s
超时时间越长,资源浪费的时间就越长,系统的稳定性就越差,因此需要设置为一个较为合理的超时时间,设置防止如下所示:
spring:
cloud:
openfeign:
client:
config:
default:
loggerLevel: full
read-timeout: 2000 # 读取数据的超时时间设置为2s
connect-timeout: 2000 # 建立连接的超时时间设置为2s
重试配置
feign一旦请求超时了,那么此时就会直接抛出SocketTimeoutException: Read timed out的异常。请求超时的原因有很多种,如网络抖动、服务不可用等。如果由于网络暂时不可用导致触发了超时机制,那么此时直接返回异常信息就并不是特别的合理,尤其针对查询请求,肯定希望得到一个结果。合理的做法:触发超时以后,让feign进行重试。
具体步骤:
1、自定义重试器
public class FeignClientRetryer implements Retryer {
// 定义两个成员变量来决定重试次数
private int start = 1 ;
private int end = 3 ;
@Override
public void continueOrPropagate(RetryableException e) { // 是否需要进行重试取决于该方法是否抛出异常,如果抛出异常重试结束
if(start >= end) {
throw new RuntimeException(e) ;
}
start++ ;
}
@Override
public Retryer clone() { // 框架底层调用该方法得到一个重试器
return new FeignClientRetryer();
}
}
2、指定配置的重试器类
spring:
cloud:
openfeign:
client:
config:
default:
loggerLevel: full
read-timeout: 2000
connect-timeout: 2000
retryer: com.chs.chs.cloud.order.feign.FeignClientRetryer # 配置自定义重试器,重试器的全类名
Nacos配置中心
Nacos除了可以做注册中心,同样可以做配置管理来使用。
统一配置管理
当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就显得十分的不方便,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理所有实例的配置。
nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。
Nacos中添加配置
在Nacos服务端创建一个配置
然后在弹出的表单中,填写配置信息
微服务集成配置中心
微服务需要进行改造,从Nacos配置中心中获取配置信息进行使用。
1、引入依赖
<!-- nacos作为配置中心时所对应的依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2、 配置文件中配置 Nacos Config 地址并引入服务配置
spring:
cloud:
nacos:
config:
server-addr: localhost:8848 # nacos服务器的IP:端口号
config:
import:
- nacos:userservice-dev.yaml # 引入服务器的配置文件
注意: 引入的配置文件的名称跟nacos中创建的名称一致
读取自定义配置
@Value
通过@Value注解读取自定义配置
@RestController
@RequestMapping(value = "/api/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService ;
@Value("${pattern.dateformat}") //获取nacos配置文件中的值
private String pattern;
@GetMapping(value = "/findUserByUserId/{userId}")
public User findUserByUserId(@PathVariable(value = "userId") Long userId) {
System.out.println("pattern = " + pattern);
return userService.findUserByUserId(userId) ;
}
}
@ConfigurationProperties
通过实体类,配合@ConfigurationProperties注解读取自定义配置。
当配置属性很多时,可以用到该方法读取配置信息
1、定义一个实体类,类中的属性名跟配置文件中的属性名一致,必须提供get和set方法
@Data
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
private String dateformat ;
}
2、在启动类上添加@EnableConfigurationProperties注解
@SpringBootApplication
@EnableConfigurationProperties(value = { PatternProperties.class })
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class , args) ;
}
}
3、使用该实体类
@RestController
@RequestMapping(value = "/api/user")
@Slf4j
public class UserController {
@Autowired
private UserService userService ;
@Value("${pattern.dateformat}") //获取nacos配置文件中的值
private String pattern ;
@Autowired // 注入实体类
private PatternProperties patternProperties ;
@GetMapping(value = "/findUserByUserId/{userId}")
public User findUserByUserId(@PathVariable(value = "userId") Long userId) {
System.out.println("pattern = " + pattern);
System.out.println("patternProperties = " + patternProperties);
return userService.findUserByUserId(userId) ;
}
}
配置热更新
我们最终的目的,是修改Nacos中的配置后,微服务中无需重启即可让配置生效,也就是配置热更新。
热更新的两种方式
方式一:在@Value注入的变量所在类上添加注解@RefreshScope
方式二:通过实体类,配合@ConfigurationProperties注解读取配置信息,自动支持热更新
配置优先级
思考问题:如果在application.yml文件中和Nacos配置中心中都定义了相同的配置内容,那么哪一个配置的优先级较高呢?
优先级顺序:Nacos配置中心的配置(后导入的配置 > 先导入的配置) > application.yml
标签:负载,服务,springcloud,Nacos,实例,public,cloud From: https://www.cnblogs.com/21CHS/p/18528823