springboot&springcloud

熟练掌握SpringCloud(Eureka、Fegin、Hystrix、Gateway、Config)等分布式服务框架,了解阿里系组件(Nacos、Sentinel、Seata)。

eureka 注册中心

nacos 注册中心

nacos = eureka + config + bus

nacos的注册表

nacos怎么实现动态配置刷新的

  • 配置动态刷新主要依靠以下:
    • @Scope
    • @RefreshScope
    • RefreshScope
    • GenericScope
    • Scope
    • ContextRefresher
  • @Scope
    • @RefreshScope可以实现动态刷新完全依赖于@Scope注解
    • @Scope代表了bean的作用域,其中主要属性有value(属性介绍如下),proxyMode。proxyMode就是实现@RefreshScope的本质。在@RefreshScope中,proxyMode的默认是设置为ScopedProxyMode.TARGET_CLASS,使用@RefreshScope注解时会给当前创建的bean生成一个代理对象,会通过代理对象来访问,每次访问都会创建一个新的对象。
      • singleton 表示该bean是单例的。(默认)
      • prototype 表示该bean是多例的,即每次使用该bean时都会新建一个对象。
      • request 在一次http请求中,一个bean对应一个实例。
      • session 在一个httpSession中,一个bean对应一个实例
    • 在scope接口中有一个Object get(String name,ObjectFactory objectFactory)方法,这个方法可以创建一个新的bean,也是就说@RefreshScope在调用刷新的时候会使用此方法来创建一个新的对象,这样就可以通过string的装配机制将属性重新注入了,也就实现了动态刷新。
    • 如何处理老对象,怎么创建新对象?
      • 主要依赖几个重要的类,其中 RefreshScope extends GenericScope,GenericScope impiements Scope.
      • GenericScope实现了Scope中的Object get方法,在GenericScope里面包装了一个内部类BeanLifecycleWrapperCache来对使用@RefreshScope注解创建的对象进行缓存,使其在不刷新的时候获取的是同一个对象。(可以把BeanLifecycleWrapperCache想象为一个Map)
      • 知道了对象是缓存,所以进行动态刷新时,只需要清除缓存,重新创建就好了。
  • 实现流程
    • 需要动态刷新的类使用注解@RefreshScope
    • @RefreshScope标注了@Scope注解,并默认了ScopeProxyMode.TARGET_CLASS属性,此属性的功能就是会给当前bean创建一个代理对象,在每次调用时都用他来调用GenericScope类中的get方法来获取对象。
    • 如果属性发生变更会调用ContextRefresher refresh() -> RefreshScope refreshAll()进行缓存清理方法调用,并发送刷新事件通知 -> GenericScope 的清理方法destory()实现清理缓存
    • 在下一次调用对象时,会调用GenericScope的get方法 创建一个新的对象,并存入缓存中,此时新对象因为Spring的装配机制就是新的属性了

nacos实例注册的过程

  1. 服务注册主要是通过NamingService.registerInstance方法来实现的,主要实现:
    1. 将serviceName和GroupName进行字符串的拼接 得到GroupServiceName。
    2. 创建一个心跳任务。
    3. 调用registerService方法注册服务实例。方法内部实现:
      1. 创建一个Map
      2. 将namespaceId、serviceName、groupName、clusterName、IP端口等属性put到map
      3. 调用reqApi(UtilAndComs.nacosUrlInstance,params,HttpMethod.POST)方法,reqApi最终的重载方法主要实现
        1. 其中新增了两个参数,body(默认为空),servers(是我们配置的nacos服务端所在服务的ip和端口的集合)
        2. 注册之前先从servers中随机获取一个进行调用(如果失败则再次获取一个进行重试)。
        3. 假设此时随机了一个服务地址,则会进入callserver方法
          1. callserver方法实现比较简单,主要是将请求地址和请求路径名(UtilAndComs.nacosUrlInstance)进行拼接,然后发送http请求进行服务注册,然后接受客户端的响应。
          2. 至此,客户端的服务注册就完成了。

nacos心跳机制

  1. 进入注册实例方法NamingService.registerInstance(),方法中会给实例添加一个心跳任务,分为两步
    1. BeatInfo beatInfo = beatReactor.buildBeatInfo(groupServiceName, instance);
      1. 主要是构建出beatInfo所需参数
    2. addBeatInfo(groupedServicename, beantInfo);

        

      1. 主要执行schedule方法,通过beatInfo构建一个beatTask,然后扔到调度线程池,等待一定时间后执行(默认5s),即BeatTask就是心跳机制执行的逻辑。 进入BeatTask方法。BeatTask实现了Runable接口,主要看run方法的逻辑

        1. 通过serverProxy发送一个http请求到服务端,服务端进行响应,这个过程完成了跟服务器的心跳,接下来解析服务端的响应数据。根据服务端返回的不同状态码进行判断,进行不同的操作。其中有一个状态码判断 (code == NamingResponseCode.RESOURCE_NOT_FOUND) 标示资源未找到,然后对其进行处理
        2. 其重要作用就是重新向服务端进行服务注册,为什么这样做?
          1. 正常情况下客户端发送心跳,服务实例应该存在于服务端,但是如果出现网络抖动等情况,客户端无法给服务端发送心跳,长时间服务端没有收到客户端的心跳,此时服务端判定这个服务实例出问题了,这样服务端就会主动从服务注册表中剔除该服务实例,该服务实例就不存在服务端了。当客户端网络恢复了,那么此时会恢复跟服务端的心跳机制,就会出现服务找不到的现象,这种情况下主要重新注册一下就行了。
        3. 重新构建一个beatTask,然后重新扔进调度线程池中,等待nextTime之后执行。但有一种情况就是当发送心跳是,服务端可能会相应给客户端一个下一次发送心跳的间隔时间,赋值给nextTime,通过这种形式,就实现了自动发送心跳的功能,当本次发送心跳后,继续构建下一次心跳任务,并扔进调度线程池之后执行,反复如此,就实现了定时发送心跳的机制。

聊一聊nacos是如何进行服务注册的_@zzyang的博客-CSDN博客_nacos怎么实现服务注册

nacos的长轮训

ribbon 负载均衡

feign / openfeign

feign默认的通信方式为http,可以通过配置修改为okHttp或httpClient 设置enable: true ,改成okhttp的原因是默认的urlHttpConnection没有连接池,这样频繁调用会增加服务器的开销

使用:

//准备远程调用的接口

@FeignClient(name = "client-name") //配置成注册中心的应用名

feign和openfeign的区别
底层都是内置了Ribbon,去调用注册中心的服务。
Feign是Netflix公司写的,是SpringCloud组件中的一个轻量级RESTful的HTTP服务客户端,是SpringCloud中的第一代负载均衡客户端。
OpenFeign是SpringCloud自己研发的,在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。是SpringCloud中的第二代负载均衡客户端。
Feign本身不支持Spring MVC的注解,使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。 
 

gateway 网关

  1. 主要作用:
    1. 权限安全、监控,过滤(限流,容错hystrix) ,谓词校验
  2. 路由配置

routes: #配置网关中的一个完成路由,包括命名id,地址,predicates集合,过滤器集合

- id:frist #路由自定义id,唯一即可,命名符合java中的变量命名规则

uri:lb://app-service #当前路由定义对应的微服务转发地址, lb代表 loadbalance 负载均衡

predicates:

- Path=/api/** # 自定义断言

filters:#配置过滤器

- StripPrefix=1 #定义一个过滤器。过滤转发地址前缀,过滤1节

路由流程:

如请求地址 - http://localhost:9999/api/getUserInfo?name=admin&age=29

对应的断言规则是 /api/** ,符合断言规则

对应的uri是 lb://app-service ,转换成http://app-service 并且包含负载均衡

转发地址是 http://app-service/api/getUserInfo?name=admin&age=29

过滤器过滤转发地址前缀1节,即删除api 得到 http://app-service/getUserInfo?name=admin&age=29

zuul 和 spring cloud gateway 的对比

 

过滤器和网关的对比

网关与 nginx 区别

Gateway 的组成

网关路由 openfeign的http调用

config 配置中心

hystrix 熔断器

支持线程池隔离,信号量隔离,基于失败比率熔断,与sentinel比较不支持限流,没有完善的控制台,适配的框架没有sentinel多。

配置@HystrixCommand

线程池隔离

  1. 可以把不同的方法配置使用指定的线程池,多个线程池中的线程互不影响,可以避免个别接口的请求激增,导致线程被全部占用,没有线程处理其他接口的调用,导致程序瘫痪。
  2. 相关配置:
    1. groupKey = "order-service-listPool" 服务名称,相同名称使用同一个线程池
    2. commandKey = "selectProductList" 接口名称,默认为方法名
    3. commandProperties = { @HystrixProperty(name = "thread.timeoutInMilliseconds")} 设置超时时间
    4. threadPoolProperties = {
      1. coreSize 线程池大小
      2. maxQueueSize 等待队列最大值, 默认 -1
      3. keepAliveTimeMinutes 线程存活时间 默认1分钟
      4. queueSizeRejectionThreshold 超出等待时间拒绝策略 }
    5. fallbackMethed 降级方法

信号量隔离

  1. 通过信号量隔离可以限制同一时刻的最大并发量
  2. timeoutInMilliseconds 超时时间
  3. EXECUTION_ISOLATION_STRATEGY ,value = "SEMAPHORE" 配置信号量隔离
  4. SEMAPHORE_MAX_CONCURRER,value = "6" 信号量最大并发

关于超时时间

对于信号量超时模式,如果发生超时,Hystrix任务并不会结束,任务结束还是得依赖于run方法执行完毕。

对于线程池超时模式,如果发生超时,Hystrix任务可以结束,不依赖于run方法执行情况,但是run方法可能还会在执行,这依赖与run方法中的代码逻辑。

熔断

通过配置@HystrixCommand注解,设置commandproperties属性 @HystrixProperty

配置10秒内请求10次错误率大于50%调用fallback方法,还可以配置熔断后多少秒进行重试请求,默认为5秒

sentinel 限流控制,熔断降级

限流的方式有两种

  • 通过配置FlowRule 指定关键字限制QPS,代码中配合使用SphU.entry(KEY) entry.exit(),如单独使用会导致调用链记录异常
  • 通过注解配置异常处理及限流处理
    • 注册Bean --- SentinelResourceAspect
    • @SentinelResource(value = KEY, blockHandler = "限流或降级方法", fallback = "异常方法")

可以通过sentinel控制台控制接口的限流降级

Sentinel的限流怎么实现的 原理是滑动窗口算法

seata的在代码中的使用

  1. 引用seata包
  2. 在需要开始事务的方法上使用注解@GlobalTransactional

-----------------------spring boot------------------------

spring boot的原理

与spring相比,省去了繁琐的配置,可以再spring.io 或者 idea上一键生成spring boot项目,非常便利。

spring boot运行的三大原理:1.springboot的自动装配原理,2.springboot的初始化构造流程,3.springboot run方法运行流程。

SpringBoot原理概述_Leo木的博客-CSDN博客_springboot原理

自动装配原理

鸡肋版本:

  1. springboot自动装配主要依赖@springbootapplication 中的 @EnableAutoConfigration ,
  2. @EnableAutoConfigration接口中标注了@Import({AutoConfigrationImportSelcetor.class})
    1. @import注解 中可以直接导入一个类, 如 User.class
    2. 也可以导入实现了ImportSelector接口的实现类,根据重写selectImports方法得到String[]数组返回值,数组中为类的全限定路径
  3. AutoConfigrationImportSelcetor实现了deferredImportSelector接口
  4. deferredImportSelcetor接口继承了IMportSelector
  5. 在importSelector中有一个selectImports方法,该方法返回一个String数组,接口返回值为类的全路径名称,返回的这些类会被注册到ioc容器中
  6. 在AutoConfigImportSelector中重写了selectImports方法,方法内部最终会调用StringFactoriesLoader.loadFactoryNames 去读取META/spring.factories文件
  7. 文件内容为key=value格式,value可以为多个中间用逗号分隔
  8. 去spring.factories文件获取时的key是springboot中定义好的 EnableAutoConfigration.class
  9. 获取对应的类路径后进行ioc的注入,每个bean会根据自己配置的条件注解(conditionalOn.....)判断是否进行注入

旗舰版:

  1. 当启动SpringBootApplication时,会先创建SpringApplication对象,执行对象的构造方法,在构造方法中会进行某些参数的初始化工作,其中主要是判断应用程序类型,设置初始化器和监听器,然后扫描spring.factories中的bean添加到缓存中,方便后续执行调用。
  2. SpringApplication对象创建完成后,开始执行run()方法,方法中包含了context上下文的创建,banner打印,还有两个核心的方法,第一个是prepareContext,第二个是refreshContext。这两个方法完成springboot自动装配的核心功能。
  3. 在prepareContext方法中,主要完成了对上下文的初始化操作,包括属性值的设置,比如设置环境对象,在prepareContext中有一个重要的方法load,load主要完成一件事儿,就是把启动类当做一个beanDefinition注册到breanDefinitionRegistry中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplication,@EnableAutoConfiguration等注解的解析工作。
  4. 在refreshContext方法中会进行整个容器的刷新,会调用spring中的refresh方法,refresh方法是springioc容器启动的核心,在自动装备过程会中,会调用refresh中的invokeBeanFactoryPostProcessor方法,在此方法中主要对ConfigurationClassPostProcessor类的处理,这个类是BeanFactoryPostProcessor的子类也是BeanDefinitionRegistryPostProcessor的子类。在调用的时候先调用BeanDefinitionRegistryPostProcessor中的postProcessorBeanDefinitionRegistry方法,然后调用postprocessorBeanFactory方法,在执行postProcessorBeanDefinitionRegistry的时候会解析处理各种注解,其中最主要的就是@Import注解。
  5. 在解析@import注解的时候,会有一个getImports方法,从主类开始递归解析注解,把所有包含@import的注解都解析到,然后再processImport方法中对import的类进行分析,此处主要识别的是AutoConfigurationImportSelect归属于ImportSelect的子类,在后续过程中会调用deferredImportSelectorHandler中的process方法,来完成EnableAutoConfiguration的加载。

Springboot集成redis-cache

  1. @Cacheable:查询数据库时先判断缓存中是否有数据,如果有则直接查缓存,如果缓存没有则执行方法查询数据库,然后将结果写入缓存。
  2. @CachePut:更新缓存数据。
  3. @CacheEvict:删除缓存数据。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值