当前位置:   article > 正文

spring-cloud学习之路_spring coud学习

spring coud学习

spring-cloud学习

  1. 需要学习的东西
    1.1服务治理Eureka
    1.2客户端负载均衡Ribbon
    1.3服务熔断器Hystrix
    1.4声明式服务调用Feign
    1.5API网关服务Zuul
    1.6配置中心Config
    1.7消息总线Bus
    1.8消息驱动Stream
    1.9服务跟踪
  2. 服务治理
    spring cloud eureka是对netflix eureka的封装,通过在spring boot的 starter poms中定义eureka,使得我们在用maven创建eureka更加方便,在面试的时候一般都会问到eureka和zookeeper有什么区别,那么我们首先来介绍下zookeeper和eureka。
    zookeeper是Hadoop的一个子项目,它保证了cp(一致性,容错性),而eureka保证了ap(可用性,容错性),那么zookeeper如果出现master节点瘫痪,他的处理方式是使用重新选举leader节点,在这个过程中服务是不可用状态,而eureka由于没有master,各个节点是平等的,所以一个节点瘫痪了,客户端可以连接其他注册中心,但是由于不能保证一致性,所以可能拿到的信息不是最新的,另外eureka有自我保护机制,默认15分钟内超过85%节点没有心跳,那么会触发保护,不再移除没有收到心跳的应该过期服务,这个时候它依然是能进行注册和查询的,但是不会同步到其他注册中心上,只有当再次恢复正常时才会进行同步和删除。
    spring cloud eureka注册中心是作为spring封装的一个应用,他的创建非常容易,我们可以新建一个maven管理的spring boot应用,在pom.xml添加starter中的eureka-server,在spring boot的启动类中添加注解@EnableEurekaServer这个时候其实已经可以启动一个注册中心了,只是没有在配置文件中进行相应的配置,通常使用的配置如下图,其中eureka.client.fetch-registry是发现服务的意思,作为注册中心就没必要了,eureka.client.register-with-eureka是注册自己到注册中心,因为目前是单注册中心就关闭了。
    eureka注册中心配置
    服务提供者,eureka服务提供者,只需要在传统spring boot web工程中添加spring-cloud-starter-eureka,然后开发相应的接口,只是在spring boot的启动类上添加@EnableDiscoveryClient注解,在配置文件中添加上注册中心的地址eureka.client.serviceUrl.defaultZone。这样一个服务提供者就创建完成,当服务正常关闭时会主动个注册中心发送rest请求下线。
    服务消费者,基本和服务提供者一样,只是多加一个bean配置,放在那个配置文件无所谓,添加RestTemp,为下面访问做铺垫(注意,启动类一定不能叫EurekaClient,因为会有冲突起不来),当把这三个主要对象启动完成,我们可以访问注册服务中心,来查看当前注册信息,注册服务中心默认http://eureka-registration:port/作为网页端入口,服务消费者会定期到注册中心获取服务清单,我们可以通过修改eureka.client.registry-fetch-interval-seconds,来指定多久才需要更新一次,对于选取服务实例,eureka中存在region和zone得概念,一个region中存在多个zone,一个服务至少是注册到一个zone上的,优先选择同一个zone上的服务,无论消费者或提供者,它们支持的配置可以查看EurekaClientConfigBean类,对于同一个服务的不同实例可以通过类EurekaInstanceConfigBean查看支持的配置。
    当我们单注册中心不够使用的时候,我们可以选择集群,那么前面说的eureka.client.register-with-eureka就要开启了,而注册中心的地址填写其他注册中心,当注册中心有多个时,我们使用逗号分隔。当服务提供者注册到某一个注册中心时,注册中心会将请求转发到集群,从而实现注册中心直接服务的同步。
    服务续约(renew),服务提供者会使用put请求维护一个心跳来告诉注册中心他还活着,注册中心会先更新服务到自身,然后再同步到其他服务中心。可以通过配置类来维护心跳,服务提供者通过eureka.instance.lease-renewal-interval-in-seconds来定义续约任务的调用时机,注册中心通过eureka.instance.lease-expiration-duration-in-seconds来定义服务剔除时间,但是当触发了自我保护,注册中心就不在剔除服务,所以一般本地调试时我们喜欢关闭自我保护eureka.server.enable-self-preservation。
    当我们想要研究服务提供或者服务消费端的源码时,可以从下面入口开始
    eureka客户端
    当我们想研究注册中心的源码时,可以通过上面找到的接口api来当入口,或者直接找netflix.eureka的源码入口,比如它的com.netflix.eureka.resources包下都是api接口,当想配置注册中心的时候,可以查看EurekaServerConfigBean,该类是注册中心支持的配置,都有源码说明
  3. 负载均衡ribbon
    spring cloud ribbon是基于netflix ribbon来实现客户端负载均衡的,这点和其他微服务架构有所不同,不是再注册中心进行负载均衡,这样做的好处是减轻注册中心的负担,同时避免注册中心宕机服务就不可用的情况,在服务消费者中使用ribbon也非常简单首先在pom中添加spring-cloud-starter-ribbon依赖,然后再RestTemplate 配置bean中添加@LoadBalanced注解,这样就可以负载均衡了,对于负载均衡相关的配置,我们一般通过覆盖实现类的方式,当然Camden版本以后优化了配置,直接可以在配置文件中添加需要替换的类的全包名,其底层还是使用覆盖实现类的方式,只是PropertiesFactory类动态的创建了这些接口的实现类。
    常见的配置类有:
    3.1 IRule,负载均衡规则,对应的已存在的实现类有RoundRobinRule(线性负载均衡)、RandomRule(随机选择)、RetryRule(在给定时间内重试线性负载找到服务,超时返回空)、WeightedResponseTimeRule(根据运行实例情况计算权重使用线性负载找到服务)、A类ClientConfigEnabledRoundRobinRule(最为特殊的实现类,一般不直接使用,而是继承它,它可以在子类实现的一些特殊策略失效时使用线性负载作为备选)、BestAvailableRule(继承A类,最空闲实例)、B类PredicateBasedRule(继承A类需要子类实现过滤,基于过滤的线性轮询)、AvailabilityFilteringRule(继承B类,基于故障和并发数阈值过滤)、ZoneAvoidanceRule(继承B类使用区域zone作为过滤条件)
    3.2 IPing,实例检测策略,已存在的实现类有NoOpPing()、PingUrl(使用url真实去ping目标主机)、NIWSDiscoveryPing(使用ping来判断目标主机是否健康,一般不代表应用状态)、PingConstant(假的,返回自己设置的任何值)、A类AbstractLoadBalancerPing(基础用于判断实例活跃性)、DummyPing(继承A类,简单实现)
    3.3 ServerList《Server> 实例清单维护,已存在的实现类有ConfiguraionBasedServerList(从配置列表中加载)、DomainExtractingServerList(通过域获取实例)、A类AbstractServerList(包含过滤机制给子类使用)、DiscoveryEnabledNIWSServerList(继承A类动态获取列表)
    3.4 ServerListFilter《server>实例清单过滤机制已存在的实现类有ZonePreferenceServerListFilter(优先过滤相同区域的实例)、A类AbstractServerListFilter()、B类ZoneAffinityServerListFilter(继承A类,区域过滤,需要设置启用)、ServerListSubsetFilter(继承B类,服务器列表筛选器,用于将负载均衡器使用的服务器数量限制为所有服务器的子集。 它还具有通过比较总网络故障和并发连接来过滤相对不健康的服务器的能力)
    3.5 ILoadBalancer 负载均衡器,已存在的实现类有ZoneAwareLoadBalancer(区域感知)、NoOpLoadBalancer(空的负载均衡,什么不做)、A类AbstractLoadBalancer(包含大多数功能供子类使用)、B类BaseLoadBalancer(继承A,基本负载均衡,使用ping)、DynamicServerListLoadBalancer(继承B,动态改变服务列表)
    以上的配置类,第一个实现类是默认实现。
  4. 熔断器Hystrix
    熔断器的主要作用是防止某一服务发生故障,导致其他服务无限等待,最终形成蝴蝶效应, 同时还具有服务降级,线程和信号隔离,请求缓存,请求合并,以及服务监控等功能,它也是基于netflix hystrix实现的。
    使用hystrix只需要在pxm中添加spring-cloud-starter-hystrix依赖,在启动类中添加@Enable-CircuitBreaker注解(在上面那个消费类基础上)也可以直接添加@SpringCloudApplication注解,这个注解中包括了需要的注解,然后我们在调用接口的
    方法上添加@HystrixCommand(fallbackMethod=“回调方法”),这个时候当服务触发熔断时,会自动调用回调方法。
    熔断器有两种实现,每种实现又有两个方法,一般情况下默认使用的是HystrixCommand的execute方法。不管那种方法使用的都是命令模式且都是使用的HystrixObservableCommand.toObservable,底层使用Rxjava下面分别就两种实现进行介绍。
    4.1HystrixCommand用来返回单操作结果,execute同步返回一个单一对象,使用方法如下图hystrix.execute
    queue异步返回一个Future,其中包含了服务结束时返回的单一对象使用如图

hystrix.queue
4.2HystrixObservableCommand使用观察者模式用来返回Observable,其中包含多个对象,使用他的toBlocking方法获取BlockingObservable,该对象可以使用迭代器遍历,observe方法返回一个Hot Observable,它的特点是不管该事件是否被订阅,他都会将结果返回,使用如图
hystrixObservableCommand.observe
toObservable方法返回一个Cold Observable,它的特点是只有当被订阅时才开始返回结果,使用和上面方法只是 注解参数不同如图
hystrixObservableCommand.toObservable
关于hystrix的配置太多,可以参考官方文档
4.3Hystrix仪表盘,当我们在配置文件中添加spring-cloud-starrter-hystrix-dashboard依赖和spring-cloud-starrter-actuator依赖时,可以从浏览器访问首页,进入仪表盘页面,我们可以看到有三个连接,其中前两个都是结合Turbine实现的集群监控,我们先看看第三个连接,安照要求url访问,可以看到。
4.4结合Turbine实现集群监控,我们新建一个spring-cloud项目,添加spring-cloud-starrter-actuator依赖和spring-cloud-starrter-turbine依赖,在启动类上添加@EnableTurbine注解,配置文件中添加turbine.app-config需要收集监控的服务名,turbine.cluster-name-expression集群名,turbine.combine-host-port是否让同一主机上的服务通过主机名和端口号组合来区分
4.5结合rabbitMq再使用Turbine实现集群监控,需要添加spring-cloud-starrter-turbine-amqp,主类使用@EnableTurbineStream注解,当然服务消费者也得做出改变使得消息可以发送到队列上,添加spring-cloud-netflix-hystrix-amqp依赖,开启mqp队列。
6. 声明式服务调用Feign
除了使用上面提到的RestTemp进行服务访问外,我们可以使用feign进行更方便的获取。首先添加spring-cloud-starter-feign依赖,在主类中添加@EnableFeignClients注解,然后我们通过在接口上添加@FeignClient注解指定服务名来绑定服务,然后使用Spring Mvc注解来绑定具体该服务能够提供的接口,接着在需要使用的地方,自动加载这个接口,然后调用相应方法,它的参数绑定需要借助于@RequestParam(单类型参数)、@RequestHeader(header带参数)、@RequestBody(使用body请求的复杂参数),如果你决定自己定义的接口和服务提供者的接口相似,那么我们也可以使用基础类,消费者和服务者都去继承基础类,这样的话项目开始就必须将相应的接口都定义出来,而且服务提供和基础类有任何一方变化,都得进行同步,维护起来不太方便,dubbo也是存在这个问题。
在使用了feign的工程中,默认继承了hystrix和rebbon的配置,同时添加了对他们的关闭配置,另外还增加了请求压缩配置feign.compression.request.enabled、feign.compression.response.enabled,还有相应的大小类型限制,日志配置等。
7. api网关zuul
由于使用微服务后,各个接口之间不在是安全的了,需要增加一系列权限校验,如果在每个微服务上添加势必太过繁琐,而且代码冗余会导致后来维护不方便,spring cloud提供了zuul进行同一管理接口,使用过滤器对接口请求进行权限安全认证。
使用zuul,首先肯定的是它本身也是一个服务提供者,我们在pom中添加spring-cloud-starter-zuul依赖,它本身包含了断路器和负载均衡,还有监控模块,我们在启动类中添加@EnableZuulProxy注解开启网关功能,此时我们可以配置路由规则,使用固定路径的方式,不推荐了,我们再添加一个spring-cloud-starter-eureka依赖,使它能够获取服务,我们可以添加下面的路由规则zuul.routes.api-路由名.path路由路径,zuul.routes.api-路由名.serviceId路径对于的服务名,我们也可以使用简洁的方式像zuul.routes.服务名=路径,我们发现大多数情况下服务名等于路径,那么zuul联合eureka其实已经自动为我们创建了每个服务实例对应的路径路由了,如果我们需要忽略某些服务实例,需要借助zuul.ignored-services配置,如果我们有特殊需求,觉得自动生成的服务实例路径不好,我们可以自定义PatternServiceRouteMapper bean,它返回的是一个map形式的数据结构,key是你要匹配的路径正则,value是你要生成的路径,一般用来处理带版本号的请求,路由匹配顺序从前往后,一般使用yml配置文件,保证顺序,另外我们还可以添加路由前缀信息,对于代理而言很是重要,使用zuul.prefix配置,默认情况下,路由请求会被api过滤掉http请求头部敏感信息,但是我们可以通过zuul.routes.<路由>.customSensitiveHeaders来控制开关,还可以自定义过滤哪些敏感信息zuul.routes.<路由>.sensitiveHeaders。
网关进行的一系列权限认证都是通过过滤器来实现,我们可以稍微了解下过滤器,如何新建一个过滤器,我们可以继承ZuulFilter需要重新实现它的四个方法,第一个方法filterType返回值共4种类型(pre请求路由器调用,routing路由时调用,post最后调用,error处理错误时调用),第二个方法filterOrder在同种类型过滤器中执行顺序,越小优先级越高,shouldFilter是否需要执行该过滤器,run过滤器执行具体内容,一般通过RequestContext.getCurrentContext获取一个请求上下文。然后将该类作为bean交给spring管理,zuul默认为我们实现了一批过滤器,都放在spring-cloud-netflix-core模块的org.springframework.cloud.netflix.zuul.filters包下,可以自行研究,我们说说异常,zuul设计的异常输出其实是依靠返回响应信息实现的,通过SendErrorFilter过滤器我们知道,只要RequestContext中存在error.status_code就会当成错误处理,一般我们会在上下文中添加三个参数1.error.status_code错误编码2.error.exception异常对象3.error.message错误信息,这些只能针对我们可以预知的异常,对于那些未知异常,我们需要使用error过滤器,在上下文中getThrowable获取异常信息,然后通过在RequestContext中添加那三个参数。由于zuul自带了很多过滤器,那么当我们需要禁用某个过滤器该怎么做呢?使用zuul.类名.类型.disable为true就可以了,
由于网关的特殊性,不易轻易重启,当我们需要添加新的过滤器时,我们可以借助于jvm动态语言,例如Groovy,我们需要添加groovy-all依赖,然后我们需要创建FilterLoader Bean如图
filterLoader
然后我们在path路径添加上一个过滤器,完成动态添加,需要注意的是,动态过滤器中不能注入spring容器加载的实例。
8. 分布式配置中心Spring Cloud Config
在微服务中,由于服务重多,配置非常分散,同一服务的配置文件散落在不同的服务器上,维护起来非常吃力,这个时候我们需要一个集中的配置管理,Spring Cloud Config 支持git存储,当然也提供其他存储方式,我们只关心git方式,因为它的webhook真心不错,使用配置中心只需要在spring boot工程上添加spring-cloud-config-server依赖,同时主程序添加@EnableConfigServer在配置文件中添加spring.cloud.config.server.git.*开头的git基础信息,如仓库路径,文件位置,用户名,密码,这个时候我们再配置一个客户端,在pom中添加spring-cloud-starter-config依赖,在bootstrap.properties配置文件中添加spring.cloud.config.*为前缀的属性,例如uri、profile、master等,为什么是bootstrap配置文件,因为SpringCloud启动时,会先创建一个Bootstrap Context,然后创建一个Application Context,Bootstrap Context是Application Context的父上下文,Bootstrap负责从外部源加载配置并解析,Bootstrap 的优先级更高,Bootstrap属于引导配置,然后该客户端就会采用配置的配置中心获取外部配置信息,配置中心会先去git远程仓库获取配置文件,然后缓存到本地仓库,当下次访问远程仓库失败时可以直接从本地仓库获取配置文件。配置中心也会加载这些配置文件到ApplicationContext实例,再返回给客户端,客户端将获取的配置信息加载到ApplicationContext实例,该外部配置优先级高于jar包内配置,所以jar包重复内容不再加载。在配置中心的配置文件中,可以使用占位符来进行配置,当客户端向配置文件发起获取请求时,配置中心会根据客户端信息来填充占位符定位配置资源的存储位置占位符分为{application}、{profile}、{label},label比较特殊,中间不能带/符号,需要转成(_)。
默认配置中心服务端实现了健康检测,但是默认构建的仓库是app,如果我们使用占位符配置的仓库的化,会检测失败,我们可以关闭检测使用spring.cloud.config.server.health.enabled来控制,或者我们让它检测一个真实存在的仓库,使用spring.cloud.config.server.health.repositories.check.*配置,主要有name,label,profiles配置中心还可以设置默认值,通过spring.cloud.config.server.overrides.*来设置,这样所以使用这个配置中心的客户端都会收到默认配置。
将配置中心注册到注册中心,使用注册中心来管理微服务,只需要在配置文件中添加eureka依赖并在启动类中开启服务注册,客户端配置文件就有所变化了,不在使用spring.cloud.config.server.git配置了,而是改成spring.cloud.config.discovery.enabled开启配置中心发现,使用spring.cloud.config.discovery.serviceId知道服务名。
当配置中心因为某些原因获取不到配置文件时,需要很长时间才能将错误返回给客户端,一般我们需要快速知道发生问题,所以可以开启Spring.cloud.config.failFast,但是如果不进行重试就返回错误,又不能抵抗网络波动,所以我们可以在pom中添加spring-retry,和spring-boot-starter-aop依赖,这样会进行6次重试,我们也可以通过spring.cloud.config.retry.*设置重试策略,multiplier重试时间间隔,initial-interval下次间隔乘的系数,max-attempts重试次数。
使用配置中心的更大好处是我们可以动态的更改服务的配置属性,通过使用spring cloud bus,我们可以很方便的更新各服务对应的配置信息,首先引入spring-boot-starter-actuator依赖它有一个/refresh端点用于实现客户端应用配置信息的重新获取与刷新。我们spring cloud bus使用rabbit mq作为消息中间键,rabbit mq基本使用可以参考,spring boot中使用rabbit可以添加spring-boot-starter-amqp依赖,spring-cloud-starter-bus-amqp依赖,配置文件中使用spring.rabbitmq.*前缀添加rabbit信息,当引入bus后,刷新接口可以使用/bus/refresh,当调用这个接口时,相当于向消息队列推了条消息,所有实例都会收到刷新提示。由于所有的实例都会收到消息,那么如果想刷新具体实例时,我们可以借助于/bus/refresh的destination参数来指明具体的实例和端口,因此我们更新配置的路线是,给配置中心引入bus,将git中的web hookurl配置到配置中心的/bus/refresh,所有需要该配置中心的客户端都需要引入bus,这样才能让配置中心刷新客户端的配置
9. 消息驱动Stream
当我们使用分布式时,会存在异步调用的情况,这个时候我们需要使用消息中间件,但是stream使我们更加便捷,它也是基于消息中间件的,例如我们可以添加spring-cloud-starter-stream-rabbit依赖,我们通过@EnableBinding创建一个绑定器,在该绑定器里,我们可以使用@StreamListener来定义输入或者输出,输入或输出都是用接口表示, 输入接口中需要使用@Input定义一个方法作为消息通道,返回SubscribableChannel接口对象,输出接口需要使用@Output来定义一个方法作为消息通道,返回MessageChannel接口对象,我们还可以在绑定器中使用@Transformer定义一个转换方法,主要是格式化,当然如果我们使用@ServiceListener定义输入输出默认有常见类型格式化,但是我们需要在配置文件中指定接受的参数都是什么spring.cloud.stream.bindings.input.content-type=,如果我们需要消息反馈的话使用@SendTo来定义一个输出通道,对于分布式我们或许有同一个实例只能消费一次相同消息的需求,我们只需要把他们都配置到一个消息组中就可以,spring.cloud.stream.bindings.input.group,或者说我们需要相同的消息都在一个实例上消费,这个时候需要分区,我们需要在消费端配置如下参数,spring.cloud.stream.bindings.input.consumer.partitioned开启分区,spring.cloud.stream.instanceIndex实例的索引号,spring.cloud.stream.instanceCount消费者总实例数,这样就可以自动分区了,生产者我们需要增加spring.cloud.stream.bindings.output.producer.partitionKeyExpression分区表达式规则,spring.cloud.stream.bindings.output.producer.partitionCount分区数量,使用stream时,允许添加MIME类型,来指定自己传输的是什么类型,常见类型或者对象spring都可以自动给完成转换。当然不支持的我们可以通过实现org.springframework.messaging.converter.MessageConverter接口来自定义处理,我们可以使用stream进行响应式编程Rxjava,引入spring-cloud-stream-rxjava依赖,使用注解@EnableRxJavaProcessor配置类提供一个RxJavaProcessor bean。
10. 服务跟踪spring cloud sleuth
当我们需要查看服务日志时,不应该是一个实例一个实例的去看,而应该是集成到一个文件中的,sleuth就可以干这事情,添加spring-cloud-starter-sleuth依赖,将每个实例日志进行标记,同时使用日志分析系统来收集存储日志,可以使用elk平台,我们需要和Logstash进行数据传输,使用json格式,引入logstash-logback-encoder依赖,在spring boot项目中默认使用的是logback进行日志收集,性能更加出色。关于logback详细信息请参考我们通过logback可以直接将日志远程记录下来,但是默认情况下会把所有日志信息都收集起来,对于一个分布式系统来说,流水日志实在太多,我们可以通过抽样的方式来记录,默认抽样策略为sampler其中的isSampled方法就是告诉是否需要收集,我们可以使用PercentageBasedSampler来实现百分百抽样,参数spring.sleuth.sampler.percentage参数值是一个0~1的数,我们也可以使用AlwaysSamper进行全日志收集,或者自定义,实现sampler接口。另外关于zipkin整合,读者可以自行百度,我个人觉得没啥好用的,注册中心的链路状态就挺好用的,zipkin只不过是更细力度的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/682601
推荐阅读
相关标签
  

闽ICP备14008679号