官网地址:Spring Cloud
什么是集群?
集群就是多个提供相同功能的服务,他们每一个节点都是相互独立的
集群可以解决什么问题?
解决单点故障,当只有一个服务器时,这个服务器承受不了这个时间点的并发量就会宕机,这时候这个项目就会瘫痪,使用集群可以进行分流,通过负载均衡将人流分开,并且当一个宕机其他的服务器也是可以使用不会造成这个项目瘫痪不能使用,其实就是一个机器干活干不过来多加几个机器一起干,多几个机器还可以保证一个机器坏掉了不会影响项目运行
什么是负载均衡? 为什么需要负载均衡?
负载均衡主要有两个策略,一个是轮询一个是随机,轮询就是每个集群中节点被命中的几率时相同的,随机就是每个节点被命中的几率完全凭运气,负载均衡就是为了解决一个请求过来访问一个集群,这时候负载均衡可以根据他的负载均衡策略去找到集群中的某一个地址去访问
什么是分布式? 为什么需要分布式?
分布式是根据不同业务来区分不同的模块,比如service mapper controller层都是根据不同的任务来区分不同的层级,分布式个人理解就是这样的思想,一个模块代表一类的功能模块,每个功能模块相互联合起来就是一个整的项目,这样每个模块都可以专注于做一件事情,就跟电子厂流水线一样,每个站只专注做一个事情,他们站连起来就是一个完整的生产线
什么是反向代理? 为什么需要反向代理?
反向代理就是相当于负载均衡那样,当我们请求一个链接是,并不知道请求的是集群中的具体什么地址,这时候就有Ribbon替我们去找,也就是说我们并不知道我们真正需要访问的是什么链接但是能得到我们想要的数据
请描述你对单体架构的理解
相互高度耦合,当有一个功能需要关掉或者出错都会给这整个服务造成影响,但是开发简单
请描述你对微服务架构的理解,以及为什么需要使用微服务架构?
将每个功能都拆分,按照不同的功能进行拆分,这个才分的功能是独立的,他们联合起来就是一个整的项目,这些拆分出来的功能可以进行集群,这样可以高并发和单点故障
微服务架构的优缺点有哪些?
优点: 降低服务之间的耦合,每个功能相互不影响
因为是模块化的,所以重用度很高,就好比是Java,我们提取一个判断是否是偶数方法,那么当我们在另一个方法中想要这个判断就不用再从新写了,直接就可以调用这个判断方法,同理:微服务的功能就可以这么做,当某一个功能需要这个功能时可以直接调用这个功能模块就可以了
什么叫做微服务治理?
首先就是为了解决让我们的请求可以得到我们请求某一个服务的全部地址。适用于实现各个微服务之间的自动注册和发现,利用eureka可以实现注册微服务。服务端是eureka客户端是服务消费者以及服务提供者,这样客户端都注册到了eureka,然后eureka就会有一个心跳机制去检测每个注册进来的服务心跳,每个服务每隔30s就会给eureka发送一个请求告诉eureka还活着,eureka会隔一分钟来检测一次,如果有三次没有给他发心跳那么就会剔除
微服务拆分原则有哪些?
按照每个功能来拆分,每个服务相互的功能都不同,数据相互独立,暴漏一个接口让其他服务可以调用,个人理解就是把每个功能拎出来做一个服务器,这个服务器和其他服务器又互不相通,但是又因为要调用他的功能,这时候可以爆露出来一个接口访问得到
微服务常用的组件有哪些
注册服务中心:Eureka 分布式配置:Nacos 服务熔断:Sentinel 服务调用:OpenFeign 服务路由:SPring Cloud Geateway 分布式消息:SCS RabbitMQ 负载均衡:Ribbon 分布式事务:Seata
Eureka组件
问题1:order-service如何得知user-service实例地址?
获取地址信息的流程如下:
user-service服务实例启动后,将自己的信息注册到eureka-server(Eureka服务 端)。这个叫服务注册
eureka-server保存服务名称到服务实例地址列表的映射关系
order-service根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取
问题2:order-service如何从多个user-service实例中选择具体的实例?
order-service从实例列表中利用负载均衡算法选中一个实例地址
向该实例地址发起远程调用
问题3:order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
user-service会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳
当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除
order-service拉取服务时,就能将故障实例排除了
Eureka服务端本身就是一个雌雄同体的,意思就是eureka服务端本身就是一个既可以是服务端又可以是客户端, 因为eureka本身就是一个集群,这样eureka就充当客户端又是服务端
当请求过来需要访问注册中心,然后eureka服务端就会充当客户端和他的集群联系然后备份给其他集群,然后再充当服务端给这个请求的对应url的客户端的地址,这样做的好处就是当一个eureka节点挂了,那么因为是集群又是每次都使用了备份,这样就可以直接使用下一个eureka节点。 正是由于这个原因,在eureka服务端的配置文件中配置时就需要
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: false # 不注册自己
fetch-registry: false #不拉取服务本eureka服务中的服务信息
这个代码配置进去,让他自己连当他自己的客户端去链接
1 Eureka组件的作用
可以让每个服务都将自己的地址注册到eureka,然后当一个请求访问过来拉取服务器对应名称的地址,可以通过eureka的底层的Ribbon来进行负载均衡,最后通过负载均衡策略去确定具体访问从注册中心的那个那个服务
2 Eureka组件的应用场景
当某一个功能服务是一个集群,那么我们在调用的时候链接会写死,只能访问单一的一隔节点,这样集群特性就没用了,那么这时候可以使用eureka来将每个节点的地址注册到eureka,然后请求过来根据名称得到全部的地址再通过底层的Ribbon来进行负载均衡策略来确定本次请求具体访问那个地址
4 Eureka组件是如何进行服务状态监控的
客户都会隔30s就给服务端发送心跳,每隔一分钟服务端会检查客户端,如果超过三次没有发送心跳,那么服务端会认为这个服务已经死了,那么就会剔除,但是剔除最大剔除总服务器数量的的15%,如果已经死了的服务器又没有被剔除,然后又活了,那么依然可以使用
5 Eureka组件的使用步骤
第一步,引入服务端的依赖,服务端包含了springBoot的启动各类了所以不用再引用SpringBoot启动类:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
第二部写启动类:
给eureka-server服务编写一个启动类,一定要添加一个@EnableEurekaServer注解,开启eureka的注册中心功能:
例如:
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
第三步编写配置文件:
server:
port: 10086
spring:
application:
name: eureka-server
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
register-with-eureka: false # 不注册自己
fetch-registry: false #不拉取服务本eureka服务中的服务信息
第四步配置客户端:
引入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置文件:
修改application.yml文件,添加服务名称、eureka地址:
spring:
application:
name: userservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance: # 在Eureka中显示服务的ip地址
ip-address: 127.0.0.1 # 配置服务器ip地址
prefer-ip-address: true # 更倾向于使用ip,而不是host名
instance-id: ${eureka.instance.ip-address}:${server.port} # 自定义实例的id
6 Eureka组件服务剔除的时机
每过一分钟就会检查一次,如果超过三次没有发送心态就会剔除,但是剔除不会超过15%,当剔除了15%还有需要剔除的,eureka也不会剔除,当死掉的没有被剔除的活过来了那么就会继续发送心跳继续用
Ribbon组件
1 Ribbon组件的作用
是负载均衡,主要有两个策略,轮询和随机 当请求发送过来,Ribbon就会拦截链接,然后根据url获取服务器名称去eureka注册中心拉对应地址,然后跟据负载均衡策略来确定具体地址
2 Ribbon组件的使用步骤
添加@LoadBalanced到制作RestTemplate bean对象的方法上,默认是轮询,也可以自己设置策论:
第一种:
@Bean
public IRule randomRule(){
return new RandomRule();
}
第二种:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则饥饿加载:因为Ribbon默认是懒加载模式,也就是第一次访问才会去创建LoadBalanceClient,这样会造成第一次请求时间过长
在配置类中写下边的代码可以改为饥饿加载,也就是启动服务就会创建LoadBalanceClient
ribbon:
eager-load:
enabled: true
#写下边一行就代表只有这个userservice名称的是饥饿加载,不写的话就代表全部都是饥饿加载
clients: userservice
使用SpringCloud 需要注意的问题:
1:SpringCloud是微服务的一个方案,再这个方案下,使用的组件都是第三方提供的,由于第三方的组件都是基于SpringBoot写的,所以在使用SpringCloud的时候版本必须和SpringBoot的版本对应
2:在配置文件中写应用名称时候,不要使用 _ 下滑杠,因为写下滑杠会导致系统识别认为是驼峰命名
淘宝的架构演变:
100并发 -> 千万并发,阿里淘宝的 14 次架构演进之路!
单体架构和微服务架构图片对比:
微服务特征就是
拆分里粒度更小,每一个服务都有对应的唯一的业务能力,
自治:团队独立,因为每一个功能就是一个单独的服务所以可以做到技术独立,数据独立
面向服务:每一个服务提供统一的接口,想要这个服务直接调用接口
隔离性强:服务调用做好隔离,容错,降级,避免出现联调问题
Ribbon:
为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。
显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor
,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。
我们进行源码跟踪:
1)LoadBalancerInterceptor
可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了几件事:
-
request.getURI()
:获取请求uri,本例中就是 http://user-service/user/8 -
originalUri.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
,看类的介绍:
这不就是轮询的意思嘛。
到这里,整个负载均衡的流程我们就清楚了。
前边说了eureka可以实现将集群中的地址加载到注册中心中,那么在使用他的时候,怎么将请求链接去精准的知道他要访问那个请求呢?
就好比一个集群有80,81,82端口,那么我们在另外一个服务里面怎么访问这个集群呢?
如果直接写指定的端口号,那么显然是写死了,比如写了一个请求的是81端口的,那么每次访问的都是81的,这样的话集群就白白浪费了,无用武之地,这时候eureka底层就有一个Ribbon,可以通过Ribbon来实现的,直接给客户端的RestTemplate加上注解@LoadBalanced,然后因为集群只是端口号不一样但是他们在eureka注册应用名称都是一样的,所以在使用RestTemplate发起请求时url可以直接写这个名称,例如:集群中的节点应用名称为 test 原本是localhost:82/请求参数或者地址 现在就可以写成test/请求参数或者地址 就可以在访问的时候被Ribbon拦截然后根据url的test名称去eureka拉去是test的地址,然后根据算法去确定到底是用那种算法去确定访问的具体路径,比如常见的就是轮询和随机,默认是轮询,具体设置方法上边也已经写出了,直接copy即可
流程图和解析如下:
![](https://i-blog.csdnimg.cn/blog_migrate/dc6c0730b3eb3af78d7fc91996aa8099.png)
负载均衡策略图片: