springcloud进阶
一、Consul服务注册和发现
为什么引入注册中心
微服务所在的IP地址和端口号硬编码到订单微服务中,会存在非常多的问题
(1)如果订单微服务和支付微服务的IP地址或者端口号发生了变化,则支付微服务将变得不可用,需要同步修改订单微服务中调用支付微服务的IP地址和端口号。
(2)如果系统中提供了多个订单微服务和支付微服务,则无法实现微服务的负载均衡功能。
(3)如果系统需要支持更高的并发,需要部署更多的订单微服务和支付微服务,硬编码订单微服务则后续的维护会变得异常复杂。
所以,在微服务开发的过程中,需要引入服务治理功能,实现微服务之间的动态注册与发现,从此刻开始我们正式进入SpringCloud实战
consul是什么?
怎么使用?
安装后出现exe文件,运行后,打开cmd窗口,执行consul agent -dev
再打开localhost:8500会出现下面页面
服务提供者8001集成
修改pom
<!--SpringCloud consul discovery --> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions> </dependency>
修改yml
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
启动类添加注解
@EnableDiscoveryClient
此时运行项目会发现localhost:8500中多了一个项目
服务消费者80集成
和服务提供者8001类似,这里要注意
controller层的
public static final String PaymentSrv_URL="http://cloud-payment-service";
URL写成8001的项目名,就是上面图片中的名字
但是这里直接运行会报错
2025-04-07T15:57:50.880+08:00 ERROR 22004 --- [p-nio-80-exec-5] c.a.cloud.exp.GlobalExceptionHandler : 全局异常信息:I/O error on GET request for "http://cloud-payment-service/pay/get/1": cloud-payment-service
原因
服务地址解析
在微服务架构中,通常会用服务名(像cloud-payment-service
)来标识服务,而非具体的 IP 地址和端口号。没有@LoadBalanced
注解时,RestTemplate
会把cloud-payment-service
当作普通的主机名处理,无法将其解析为具体服务实例的 IP 地址和端口号,从而导致请求失败。
当添加@LoadBalanced
注解后,RestTemplate
会与服务注册中心(如 Eureka、Consul)协作,从注册中心获取cloud-payment-service
对应的所有服务实例列表,然后从列表里挑选一个合适的实例来发送请求。
负载均衡
在实际生产环境中,一个服务往往会有多个实例来处理高并发请求。@LoadBalanced
注解会为RestTemplate
集成负载均衡器(如 Ribbon),负载均衡器会按照特定的算法(例如轮询、随机等)从服务实例列表里选择一个实例处理请求。
要是没有@LoadBalanced
注解,请求就无法被正确分发到可用的服务实例上,可能会因为请求的实例不可用而失败。添加该注解后,负载均衡器会保证请求被分发到健康的实例上,从而提升请求成功的概率。
解决方法,在消费者的config中添加@LoadBalanced注解
@Configurationpublic class RestTemplateConfig
{@Bean @LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}
}
此时的8500端口
注册中心的异同
AP
CP
服务配置
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。比如某些配置文件中的内容大部分都是相同的,只有个别的配置项不同。就拿数据库配置来说吧,如果每个微服务使用的技术栈都是相同的,则每个微服务中关于数据库的配置几乎都是相同的,有时候主机迁移了,我希望一次修改,处处生效。
当下我们每一个微服务自己带着一个application.yml,上百个配置文件的管理....../(ㄒoㄒ)/~~
官网说明
步骤服务提供者8001
修改pom
<!--SpringCloud consul config--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-config</artifactId> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId> </dependency>
新建bootstrap.yml
把application.yml中一些东西提到bootstrap.yml
spring:application:name: cloud-payment-service####Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}config:profile-separator: '-' # default value is ",",we update '-'format: YAML# config/cloud-payment-service/data # /cloud-payment-service-dev/data # /cloud-payment-service-prod/data
application.yml
server:port: 8001# ==========applicationName + druid-mysql8 driver=================== spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueusername: rootpassword: 123456profiles:active: dev # 多环境配置加载内容dev/prod,不写就是默认default配置# ========================mybatis=================== mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.atguigu.cloud.entitiesconfiguration:map-underscore-to-camel-case: true
8500新建config文件夹
config下新建3个文件夹
这3个文件夹分别create data
8001的controller中增加代码
@Value("${server.port}") private String port; @GetMapping(value = "/pay/get/info") public String getInfoByConsul(@Value("${atguigu.info}") String atguigu){return "atguigu:"+atguigu+"\t"+"port"+port; }
打开http://localhost:8001/pay/get/info
可以访问到
在idea中可以切换,会访问到consul中不同的内容
动态刷新
我们修改了consul的data内容,可能不会即时刷新,这时候需要进行配置
方法一:
主启动类添加注解
@RefreshScope
这个方法可能不生效,放在controller上也不行,不知道为什么,
方法二:官网有默认的时间是55秒,自己也可以修改
bootstrap.yml中增加
watch:
wait-time: 1
consul如果关闭,所有配置都会失效,需要重新配置
二、Spring Cloud LoadBalancer
在springcloudCommons这个包下面
LB负载均衡(Load Balance)是什么
简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用),常见的负载均衡有软件Nginx,LVS,硬件F5等
spring-cloud-starter-loadbalancer组件是什么
Spring Cloud LoadBalancer是由SpringCloud官方提供的一个]源的、简单易用的客户端负载均衡器,,它包含在SpringCloud-commons中用它来换了以前的Ribbon组件。相比较于Ribbon,SpringCloud LoacBalancer不仅能够支持RestTemplate,还支持WebClient(WeClient是SpringWeb Flux中提供的功能,可以实现响应式异步请求)
loadbalancer本地负载均衡客户端 VS Nginx服务端负载均衡区别
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求,即负载均衡是由服务端实现的。
loadbalancer本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
80通过轮询负载访问8001/8002/8003
LoadBalancer 在工作时分成两步:
第一步,先选择ConsulServer从服务端查询并拉取服务列表,知道了它有多个服务(上图3个服务),这3个实现是完全一样的,
默认轮询调用谁都可以正常执行。类似生活中求医挂号,某个科室今日出诊的全部医生,客户端你自己选一个。
第二步,按照指定的负载均衡策略从server取到的服务注册列表中由客户端自己选择一个地址,所以LoadBalancer是一个客户端的负载均衡器。
idea中新建8002,把8001的复制过去,会发现访问localhost:8001/pay/get/info报错,这是因为consul在关闭后,之前设置的资源会消失,需要进行持久化
按上面图片的步骤在consul的安装目录新建。。。。。。。。
然后再8500中create config/。。。。。
就可以运行了 访问8001和8002都可以成功
订单80修改
1. POM
<!--loadbalancer--> < dependency > < groupId >org.springframework.cloud </ groupId > < artifactId >spring-cloud-starter-loadbalancer </ artifactId > </ dependency |
此时访问80就会轮询访问8001和8002
原因
订单80的controller加入以下代码
@Resource private DiscoveryClient discoveryClient; @GetMapping("/consumer/discovery") public String discovery() {List<String> services = discoveryClient.getServices();for (String element : services) {System.out.println(element);}System.out.println("===================================");List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");for (ServiceInstance element : instances) {System.out.println(element.getServiceId()+"\t"+element.getHost()+"\t"+element.getPort()+"\t"+element.getUri());}//这里的get是写死的,只能获取第一个return instances.get(0).getServiceId()+":"+instances.get(0).getPort(); }
浏览器访问,idea会打印,所有注册的服务和需要的服务
算法解释
负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标 ,每次服务重启动后rest接口计数从1开始。 List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service"); 如: List [0] instances = 127.0.0.1:8002 List [1] instances = 127.0.0.1:8001 8001+ 8002 组合成为集群,它们共计2台机器,集群总数为2, 按照轮询算法原理: 当总请求数为1时: 1 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001 当总请求数位2时: 2 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002 当总请求数位3时: 3 % 2 =1 对应下标位置为1 ,则获得服务地址为127.0.0.1:8001 当总请求数位4时: 4 % 2 =0 对应下标位置为0 ,则获得服务地址为127.0.0.1:8002 如此类推...... |
切换负载均衡算法,
@LoadBalancerClient(value = "cloud-payment-service", configuration = RestTemplateConfig.class) :该注解的作用是为名为cloud-payment-service 的服务客户端定制负载均衡配置。configuration 属性指定了配置类,也就是当前的RestTemplateConfig 类 |
|
@Configuration // 服务名称 @LoadBalancerClient(value = "cloud-payment-service",configuration = RestTemplateConfig.class) public class RestTemplateConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}@BeanReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); //RandomLoadBalancer 随机,不是轮询return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);} }