此篇文章是在B站学习黑马SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式时所做的笔记。
- 认识微服务
- 1.**微服务技术对比**
- 2.SpringCloud
- 3.服务拆分及远程调用
- 3.1 服务拆分注意事项
- 3.2 微服务远程调用
- 3.2.1 远程调用方式分析
- 3.2.2 提供者与消费者
- 4.Eureka注册中心
- 4.1 动手实践
- 1) 搭建注册中心(搭建EurekaServer)
- 2) 服务注册
- 3) 服务发现
- 5.负载均衡
- 5.1 负载均衡原理
- 5.2 源码跟踪
- 5.3 负载均衡策略
- 5.4 饥饿加载
- 6.Nacos注册中心
- 6.1 认识nacos
- 6.2 注册服务到Nacos
- 6.3 Nacos服务分级存储模型
- 6.4 服务跨集群调用问题
- 6.5 服务集群属性设置
- 6.6 根据集群设置负载均衡优先级
- 6.7 根据权重负载均衡
- 6.8 环境隔离 - namespace
- 6.9 Nacos与Eureka的区别
单体架构:将业务的所有功能都几种在一个项目开发,打成一个包部署;优点:架构简单,部署成本低;缺点:耦合度高。
分布式架构:根据业务功能进行拆分,每个业务模块作为独立项目开发,称为一个服务;优点:降低耦合,有利于服务升级扩展;缺点:架构复杂,难度大。
分布式架构要考虑的问题:
- 服务拆分粒度如何?
- 服务集群地址如何维护?
- 服务直接如何实现远程调用?
- 服务健康状态如何感知?
微服务:是一种良好的分布式架构方案。
微服务架构特征:
-
单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
-
面向服务:微服务对外暴露业务接口
-
自治:团队独立、技术独立、数据独立、部署独立
-
隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
微服务结构:
1.微服务技术对比 2.SpringCloudSpringCloud是国内使用最广泛的微服务框架技术。官网:https://spring.io/projects/spring-cloud
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配。
SpringCloud的组件:
3.服务拆分及远程调用 3.1 服务拆分注意事项-
单一职责:不同微服务,不要重复开发相同业务
-
数据独立:不要访问其它微服务的数据库
-
面向服务:将自己的业务暴露为接口,供其它微服务调用
首先浏览器发送请求到用户模块,用户模块在到数据库查询信息;这时候订单模块在发送请求到用户模块,用户模块在到数据库查询信息。
步骤:
- 在配置类中注册RestTemplate。(在启动类中也可以注册,因为启动类中的注解包含了@Configuration)
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
- 服务远程调用RestTemplate
package cn.itcast.order.service;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.查询用户信息
String url = "https://localhost:8081/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class); //Xxxx.class作用是把Json数据转换为Xxx.class这个类型的对象。
// 3.把user赋值给order
order.setUser(user);
// 4.返回
return order;
}
}
总结:微服务调用方式
- 基于RestTemplate发起的http请求实现远程调用。
- http请求做远程调用是与语言物质的调用,只需要知道对方的ip、端口、接口路径、请求参数即可。
- 服务提供者:一次业务中被其它微服务调用的。(提供接口给其它微服务使用)
- 服务消费者:一次业务中调用其他微服务。(调用其他微服务的接口)
思考:服务A调用服务B,服务B调用服务C,那么服务B是什么绝色?
相对于A来说服务B是服务提供者;相对于服务C来说服务B是服务消费者。
总结:服务调用关系
-
服务提供者:暴露接口给其它微服务调用。
-
服务消费者:调用其它微服务提供的接口。
-
提供者与消费者角色其实是相对的。
-
一个服务可以同时是服务提供者和服务消费者。
EureKa的作用
消费者该如何获取服务提供者具体信息?
-
服务提供者启动时向eureka注册自己的信息。
-
eureka保存这些信息。
-
消费者根据服务名称向eureka拉取提供者信息。
如果有多个服务提供者,消费者该如何选择?
- 服务消费者利用负载均衡算法,从服务列表中挑选一个。
消费者如何感知服务提供者健康状态?
-
服务提供者会每隔30秒向EurekaServer发送心跳请求,报告健康状态。
-
eureka会更新记录服务列表信息,心跳不正常会被剔除。
-
消费者就可以拉取到最新的信息。
搭建EurekaServer服务步骤:
- 创建项目,引入注册中心依赖。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
- 编写启动类,添加
@EnaleEurekaServer
注解。
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
- 添加application.yml文件,编写下面的配置:
server:
port: 10086 # 服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
2) 服务注册
将xxxService服务注册到EurekaServer步骤如下:
- 在要注册到EurekaServer的项目下引入eureka客户端依赖。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
- 在application.yml文件,编写下面的配置:
Spring:
application:
name: userservice # user的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
3) 服务发现
服务拉取(服务发现)是基于服务名称获取服务列表,如何在对服务列表做负载均衡。
-
修改url路径,用服务名代替ip、端口。
-
在启动类的RestTemplate添加负债均衡注解:
@Bean
@LoadBalanced //负载均衡
public RestTemplate restTemplate(){
return new RestTemplate;
}
总结:
-
搭建注册中心
- 引入注册中心依赖。
- 在启动类上添加
@EnaleEurekaServer
注解。 - 编写application.yml配置文件。
-
服务注册
- 引入eureka-client依赖。
- 编写application.yml配置文件。
- 无论是消费者还是提供者,只要完成了上面两步都可以进行服务注册。
-
服务发现
- 给RestTemplate添加
@EnaleEurekaServer
注解。 - 用服务提供者的服务名称代替ip、端口号进行远程调用。
- 给RestTemplate添加
上面我们说到了注解@LoadBalanced
可以实现负载均衡下面,我们来探讨一下它的原理。
SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。
那么我们发出的请求明明是http://userservice/user/1,怎么变成了http://localhost:8081的呢?
5.2 源码跟踪为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。
显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor
,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。
我们进行源码跟踪:
1)LoadBalancerIntercepor
可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了几件事:
request.getURI()
:获取请求uri,本例中就是 http://user-service/user/8originalUri.getHost()
:获取uri路径的主机名,其实就是服务id,user-service
this.loadBalancer.execute()
:处理服务id,和用户请求。
这里的this.loadBalancer
是LoadBalancerClient
类型,我们继续跟入。
2)LoadBalancerClient
继续跟入execute方法:
代码是这样的:
- getLoadBalancer(serviceId):根据服务id获取ILoadBalancer,而ILoadBalancer会拿着服务id去eureka中获取服务列表并保存起来。
- getServer(loadBalancer):利用内置的负载均衡算法,从服务列表中选择一个。本例中,可以看到获取了8082端口的服务
放行后,再次访问并跟踪,发现获取的是8081:
果然实现了负载均衡。
3)负载均衡策略IRule
在刚才的代码中,可以看到获取服务使通过一个getServer
方法来做负载均衡:
我们继续跟入:
继续跟踪源码chooseServer方法,发现这么一段代码:
我们看看这个rule是谁:
这里的rule默认值是一个RoundRobinRule
,看类的介绍:
这就是轮询的意思。
到这里,整个负载均衡的流程我们就清楚了。
4)总结
SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:
基本流程如下:
- 拦截我们的RestTemplate请求http://userservice/user/1
- RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
- DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
- eureka返回列表,localhost:8081、localhost:8082
- IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
- RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求。
Ribbon的负载均衡规则是IRule接口来定义的,每一个实现类都是一种规则。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWVT3RhE-1650854419191)(http://www.kaotop.com/file/tupian/20220427/favicon.ico)]
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
修改负载均衡规则的两种方式:
-
代码方式:在服务消费者的启动类上注入一个新的IRule。
@Bean public IRule randomRule(){ return new RandomRule(); }
-
配置文件的方式:在服务消费者的application.yml文件上配置:
# userservice这是服务的名称,自己设置的 userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
5.4 饥饿加载注意:一般情况下都是用默认的负载均衡规则。
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: userservice # 指定对userservice这个服务饥饿加载
6.Nacos注册中心
6.1 认识nacos
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。功能比Eureka多,在国内受欢迎程度高。
6.2 注册服务到Nacos在bin目录下运行指令:startup.cmd -m standalone 启动Nacos
-
在父工程中添加Nacos依赖管理:
<dependency> <groupId>com.alibaba.cloudgroupId> <artifactId>spring-cloud-alibaba-dependenciesartifactId> <version>2.2.6.RELEASEversion> <type>pomtype> <scope>importscope> dependency>
-
需要注释掉原有的eureka依赖。
-
添加Nacos的客户端依赖:
<dependency> <groupId>com.alibaba.cloudgroupId> <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId> dependency>
-
修改服务消费者和服务提供者中的application.yml文件,注释掉eureka地址,添加Nacos地址。
spring:
application:
name: orderservice # order的服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务地址
- 启动并测试
启动后在浏览器输出: http://10.50.58.199:8848/nacos/index.html
6.3 Nacos服务分级存储模型- 一级是服务
- 二级是集群
- 三级是实例
- 服务调用时尽可能选择本地集群的服务,因为跨集群调用延迟较高。
- 本地集群不可访问时,再去访问其它集群。
- 在application.yml文件下设置集群名称:
spring:
application:
name: orderservice # order的服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务地址
discovery:
cluster-name: GZ # 配置集群名称,也就是机房位置。
6.6 根据集群设置负载均衡优先级
-
修改服务消费者中的application.yml配置:
spring: cloud: nacos: server-addr: localhost:8848 # nacos 服务地址 discovery: cluster-name: HZ # 配置集群名称,也就是机房位置。
-
然后在服务消费者中设置负载均衡的IRule为NacosRule。
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
6.7 根据权重负载均衡NacosRule负载均衡策略:这个规则会优先寻找与自己同集群的服务;找不到才会去其他的集群找,而且日志会发出警告;确定了集群后,在采用随机的方式选择实例。
在实际部署当中会出现这样的情况:
- 企业里服务器设备会更新迭代,有些服务器性能比较好,有些服务器性能比较差,而我们希望性能好的机器承担更多的用户请求。
Nacos提供了权重配置来控制访问频率,权重越大则访问率越高;我们就可以把性能好的机器权重设高一些,性能差的机器权重设小写。
可以在Nacos控制台设置权重值,权重值在0~1之间,权重值越大被访问的频率就越高。
6.8 环境隔离 - namespace权重值设置为0将完全不能被访问。
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离。
- 可以在控制台中创建namespace,用来隔离不同环境
- 填写命名空间和描述
- 然后会在Nacos控制台看到命名空间名称和命名空间ID
- 修改服务消费者的application.yml,添加namespace:
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: abc123
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: orderservice # order的服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务地址
discovery:
cluster-name: HZ # 配置集群名称,也就是机房位置。
namespace: 011dadf0-ae68-48f9-a2b7-e9d8ca809376 # 命名空间,填ID
- 重启order-server后
- 此时访问order-server后,因为namespace不同,会导致找不到user-server然后报错。
6.9 Nacos与Eureka的区别每个namespace都有唯一;服务在application.yml文件所在是写的是id不是空间名称;不同namespace下的服务相互不可见。
Nacos的服务实例分为两种类型,临时实例和非临时实例
临时实例宕机时,会从nacos的服务列表中剔除,而非临时实例则不会。
-
Nacos与eureka的共同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
-
Nacos与Eureka的区别
- Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
- Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式
SpringCloud复习02
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)