赞
踩
4.2HystrixObservableCommand使用观察者模式用来返回Observable,其中包含多个对象,使用他的toBlocking方法获取BlockingObservable,该对象可以使用迭代器遍历,observe方法返回一个Hot Observable,它的特点是不管该事件是否被订阅,他都会将结果返回,使用如图
toObservable方法返回一个Cold Observable,它的特点是只有当被订阅时才开始返回结果,使用和上面方法只是 注解参数不同如图
关于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如图
然后我们在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只不过是更细力度的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。