首页 文章 精选 留言 我的

精选列表

搜索[SpringCloud],共1289篇文章
优秀的个人博客,低调大师

【转】springcloud:配置中心git示例

随着线上项目变的日益庞大,每个项目都散落着各种配置文件,如果采用分布式的开发模式,需要的配置文件随着服务增加而不断增多。某一个基础服务信息变更,都会引起一系列的更新和重启,运维苦不堪言也容易出错。配置中心便是解决此类问题的灵丹妙药。 市面上开源的配置中心有很多,BAT每家都出过,360的QConf、淘宝的diamond、百度的disconf都是解决这类问题。国外也有很多开源的配置中心Apache的Apache Commons Configuration、owner、cfg4j等等。这些开源的软件以及解决方案都很优秀,但是我最钟爱的却是spring Cloud Config,因为它功能全面强大,可以无缝的和spring体系相结合,够方便够简单颜值高我喜欢。 Spring Cloud Config 在我们了解spring cloud config之前,我可以想想一个配置中心提供的核心功能应该有什么 提供服务端和客户端支持 集中管理各环境的配置文件 配置文件修改之后,可以快速的生效 可以进行版本管理 支持大的并发查询 支持各种语言 Spring Cloud Config可以完美的支持以上所有的需求。 Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud使用Git或svn存放配置文件,默认情况下使用git,我们先以git为例做一套示例。 首先在github上面创建了一个文件夹config-repo用来存放配置文件,为了模拟生产环境,我们创建以下三个配置文件: // 开发环境 neo-config-dev.properties // 测试环境 neo-config-test.properties // 生产环境 neo-config-pro.properties 每个配置文件中都写一个属性neo.hello,属性值分别是 hello im dev/test/pro 。下面我们开始配置server端 server 端 1、添加依赖 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> </dependencies> 只需要加入spring-cloud-config-server包引用既可。 2、配置文件 server: port: 8040 spring: application: name: spring-cloud-config-server cloud: config: server: git: uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 username: # git仓库的账号 password: # git仓库的密码 3、启动类 启动类添加@EnableConfigServer,激活对配置中心的支持 @EnableConfigServer @SpringBootApplication public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } } 到此server端相关配置已经完成 4、测试 首先我们先要测试server端是否可以读取到github上面的配置信息,直接访问:http://localhost:8001/neo-config/dev 返回信息如下: { "name": "neo-config", "profiles": [ "dev" ], "label": null, "version": null, "state": null, "propertySources": [ { "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", "source": { "neo.hello": "hello im dev" } } ] } 上述的返回的信息包含了配置文件的位置、版本、配置文件的名称以及配置文件中的具体内容,说明server端已经成功获取了git仓库的配置信息。 如果直接查看配置文件中的配置信息可访问:http://localhost:8001/neo-config-dev.properties, 返回:neo.hello: hello im dev 修改配置文件neo-config-dev.properties中配置信息为:neo.hello=hello im dev update,再次在浏览器访问http://localhost:8001/neo-config-dev.properties,返回:neo.hello: hello im dev update。说明server端会自动读取最新提交的内容 仓库中的配置文件会被转换成web接口,访问可以参照以下的规则: /{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties 以neo-config-dev.properties为例子,它的application是neo-config,profile是dev。client会根据填写的参数来选择读取对应的配置。 client 端 主要展示如何在业务项目中去获取server端的配置信息 1、添加依赖 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 引入spring-boot-starter-web包方便web测试 2、配置文件 需要配置两个配置文件,application.properties和bootstrap.properties application.properties如下: spring.application.name=spring-cloud-config-client server.port=8002 bootstrap.properties如下: spring.cloud.config.name=neo-config spring.cloud.config.profile=dev spring.cloud.config.uri=http://localhost:8001/ spring.cloud.config.label=master spring.application.name:对应{application}部分 spring.cloud.config.profile:对应{profile}部分 spring.cloud.config.label:对应git的分支。如果配置中心使用的是本地存储,则该参数无用 spring.cloud.config.uri:配置中心的具体地址 spring.cloud.config.discovery.service-id:指定配置中心的service-id,便于扩展为高可用配置集群。 特别注意:上面这些与spring-cloud相关的属性必须配置在bootstrap.properties中,config部分内容才能被正确加载。因为config的相关配置会先于application.properties,而bootstrap.properties的加载也是先于application.properties。 3、启动类 启动类添加@EnableConfigServer,激活对配置中心的支持 @SpringBootApplication public class ConfigClientApplication { public static void main(String[] args) { SpringApplication.run(ConfigClientApplication.class, args); } } 启动类只需要@SpringBootApplication注解就可以 4、web测试 使用@Value注解来获取server端参数的值 @RestController class HelloController { @Value("${neo.hello}") private String hello; @RequestMapping("/hello") public String from() { return this.hello; } } 启动项目后访问:http://localhost:8002/hello,返回:`hello im dev update说明已经正确的从server端获取到了参数。到此一个完整的服务端提供配置服务,客户端获取配置参数的例子就完成了。 我们在进行一些小实验,手动修改neo-config-dev.properties中配置信息为:neo.hello=hello im dev update1提交到github,再次在浏览器访问http://localhost:8002/hello,返回:neo.hello: hello im dev update,说明获取的信息还是旧的参数,这是为什么呢?因为spirngboot项目只有在启动的时候才会获取配置文件的值,修改github信息后,client端并没有在次去获取,所以导致这个问题。如何去解决这个问题呢?留到下一章我们在介绍。 示例代码 出处:http://www.ityouknow.com/

优秀的个人博客,低调大师

springcloud(五):熔断监控Hystrix Dashboard

Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。但是只使用Hystrix Dashboard的话, 你只能看到单个应用内的服务信息, 这明显不够. 我们需要一个工具能让我们汇总系统内多个服务的数据并显示到Hystrix Dashboard上, 这个工具就是Turbine.愿意了解源码的朋友直接求求交流分享技术:二一四七七七五六三三 Hystrix Dashboard我们在熔断示例项目spring-cloud-consumer-hystrix的基础上更改,重新命名为:spring-cloud-consumer-hystrix-dashboard。 1、添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> 这三个包必须添加 2、启动类启动类添加启用Hystrix Dashboard和熔断器 @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableHystrixDashboard @EnableCircuitBreaker public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } 3、测试启动工程后访问 http://localhost:9001/hystrix,将会看到如下界面:图中会有一些提示: Cluster via Turbine (default cluster): http://turbine-hostname:port/turbine.stream Cluster via Turbine (custom cluster): http://turbine-hostname:port/turbine.stream?cluster=[clusterName] Single Hystrix App: http://hystrix-app:port/hystrix.stream 大概意思就是如果查看默认集群使用第一个url,查看指定集群使用第二个url,单个应用的监控使用最后一个,我们暂时只演示单个应用的所以在输入框中输入: http: //localhost:9001/hystrix.stream ,输入之后点击 monitor,进入页面。 如果没有请求会先显示Loading ...,访问http:// localhost:9001/hystrix.stream 也会不断的显示ping。 请求服务http:// localhost:9001/hello/neo,就可以看到监控的效果了,首先访问http:// localhost:9001/hystrix.stream,显示如下: ping: data: {"type":...} data: {"type":...} 说明已经返回了监控的各项结果 到监控页面就会显示如下图:其实就是http:/ /localhost:9001/hystrix.stream返回结果的图形化显示,Hystrix Dashboard Wiki上详细说明了图上每个指标的含义,如下图:到此单个应用的熔断监控已经完成。技术架构图如下:资料和源码来源

优秀的个人博客,低调大师

springcloud(三):服务提供与调用

上一篇文章我们介绍了eureka服务注册中心的搭建,这篇文章介绍一下如何使用eureka服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用的案例。愿意了解源码的朋友直接求求交流分享技术:二一四七七七五六三三 案例中有三个角色:服务注册中心、服务提供者、服务消费者,其中服务注册中心就是我们上一篇的eureka单机版启动既可,流程是首先启动注册中心,服务提供者生产服务并注册到服务中心中,消费者从服务中心中获取服务并执行。 服务提供我们假设服务提供者有一个hello方法,可以根据传入的参数,提供输出“hello ,this is first messge”的服务 1、pom包配置创建一个springboot项目,pom.xml中添加如下配置: <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 2、配置文件application.properties配置如下: server.port=9000 eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka 3、启动类启动类中添加@EnableDiscoveryClient注解 @SpringBootApplication @EnableDiscoveryClient public class ProducerApplication { public static void main(String[] args) { SpringApplication.run(ProducerApplication.class, args); } } 4、controller提供hello服务 @RestController public class HelloController { @RequestMapping("/hello") public String index(@RequestParam String name) { return "hello "+name+",this is first messge"; } } 添加@EnableDiscoveryClient注解后,项目就具有了服务注册的功能。启动工程后,就可以在注册中心的页面看到SPRING-CLOUD-PRODUCER服务。 到此服务提供者配置就完成了。 服务调用1、pom包配置和服务提供者一致 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 2、配置文件application.properties配置如下: spring.application.name=spring-cloud-consumer server.port=9001 eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka 3、启动类启动类添加@EnableDiscoveryClient和@EnableFeignClients注解。 @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } } @EnableDiscoveryClient:启用服务注册与发现@EnableFeignClients:启用feign进行远程调用Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。 4、feign调用实现 @FeignClient(name= "spring-cloud-producer") public interface HelloRemote { @RequestMapping(value = "/hello") public String hello(@RequestParam(value = "name") String name); } name:远程服务名,及spring.application.name配置的名称此类中的方法和远程服务中contoller中的方法名和参数需保持一致。 5、web层调用远程服务将HelloRemote注入到controller层,像普通方法一样去调用即可。 @RestController public class ConsumerController { @Autowired HelloRemote HelloRemote; @RequestMapping("/hello/{name}") public String index(@PathVariable("name") String name) { return HelloRemote.hello(name); } } 到此,最简单的一个服务注册与调用的例子就完成了。整体代码结构如下:资料和源码来源

优秀的个人博客,低调大师

SpringCloud学习之eureka集群配置

一。集群方案及部署思路: 如果是单节点的注册中心,是无法保证系统稳定性的,当然现在项目部署架构不可能是单节点的。 集群节点的部署思路:通过运行多个实例并请求他们相互注册,来完成注册中心的高可用性(结伴注册) 注意: 用于本机模拟的前期准备工作:将电脑上hosts 添加如下配置 (linux下位置:/etc/hosts): 127.0.0.1 localhost server1 server2 View Code 二 设计步骤 在这里简单创建一个项目:register-center-node1的项目工程,和我们先前的register-center项目工程一模一样 register-center-node1的application.yml配置: 1 server: 2 port: 8081 3 spring: 4 application: 5 name: register-center 6 eureka: 7 client: 8 service-url: 9 defaultZone: http://server1:8080/eureka 10 fetch-registry: true 11 instance: 12 hostname: server2 View Code register-center的application.yml配置: 1 server: 2 port: 8080 3 spring: 4 application: 5 name: register-center 6 eureka: 7 client: 8 service-url: 9 defaultZone: http://server2:8081/eureka 10 instance: 11 hostname: server1 View Code 注意以下几点: 与先前独立运行register-center不同,大家注意defaultZone属性,两个注册中心地址都指向对方进行结伴注册 去掉fetch-registry 与register-with-eureka配置(其实这样做就会取对应的默认值,两个值均为true) 启动第一个注册中心时会报Cannot execute request on any known server的错误,暂时不管它,实际上eureka注册中心的ui界面是能打开的 所有注册中心的节点的spring.application.name必须保持一致。 当需要往注册中心集群注册服务时的写法:defaultZone:http://server1:8080/eureka,http://server2:8081/eureka 启动完毕后,访问地址:http://localhost:8080 得到如下界面: 我们可以看到注册中心地址已经标记为集群模式了

优秀的个人博客,低调大师

SpringCloud的限流、降级和熔断——Hystrix

一、前言 分布式系统环境中,服务间类似依赖非常常见,一个业余调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象称为雪崩效应。 二、雪崩效应 1、常见场景 (1)硬件故障:如服务器宕机,机房断电,光纤被挖断等。 (2)流量激增:如异常流量,重试加大流量等。 (3)缓存穿透:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用。 (4)程序bug:如程序逻辑导致内存泄漏,JVM长时间FullGC等。 (5)同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽。 2、应对策略 针对造成雪崩效应的不同场景,可以使用不同的应对策略,没有一种通用所有场景的策略。 (1)硬件故障:多机房容灾、异地多活等。 (2)流量激增:服务自动扩容、流量控制(限流、关闭重试)等。 (3)缓存穿透:缓存预加载、缓存异步加载等。 (4)程序bug:修改程序bug、及时释放资源等。 (5)同步等待:资源隔离、MQ解耦。、不可用服务调用快速失败等。资源隔离通常指不同服务调用采取不同的线程池;不可用服务调用快速失败一般通过熔断模式结合超时机制实现。 综上所述,如果一个应用不能对来自依赖的故障进行隔离,那该应用本身就处在被拖垮的风险中。因此,为了构建稳定、可靠的分布式系统,我们的服务应当具有自我保护能力,当依赖服务不可用时,当前服务启动自我保护功能,从而避免发生雪崩效应。本文将重点介绍使用Hystrix解决同步等待的雪崩问题。 三、初探Hystrix Hystrix,中文含义是豪猪,因其背上长满荆棘,从而拥有了自我保护的能力。本文所说的Hystrix是Netflix公司开源的一款容错框架,同样具有自我保护能力。为了实现容错和自我保护,下面我们看看Hystrix如何设计和实现的。 Hystrix设计目标: 对来自依赖的延迟和故障进行防护和控制,这些依赖通常都是通过网络访问的。 阻止失败并迅速恢复 回退并优雅降级 提供近实时的监控与告警 Hystrix遵循的设计原则: 防止任何单独的依赖耗尽资源(线程) 过载立即切断并快速失败,防止排队 尽可能提供回退以保护用户免受故障 使用隔离技术(例如隔板、泳道和断路器模式)来限制任何一个依赖的影响 通过近实时的指标,监控和告警,确保故障被及时发现 通过动态修改配置属性,确保故障及时恢复 防止整个依赖客户端执行失败,而不仅仅是网络通信 Hystrix如何实现这些设计目标? 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或 HystrixObservableCommand对象中,并将该对象放在单独的线程中执行。 每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。 记录请求成功,失败,超时和线程拒绝。 服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。 请求失败,被拒绝,超时或熔断时执行降级逻辑。 近实时地监控指标和配置的修改。 四、Hystrix处理流程 (一)Hystrix 整个工作流程如下: 1、构造一个 HystrixCommand或HystrixObservableCommand对象, 用于封装请求,并在构造方法配置请求被执行需要的参数; 2、执行命令, Hystrix 提供了4种执行命令的方法,后面详述; 3、判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。 Hystrix 支持请求缓存,但需要用户自定义启动; 4、判断熔断器是否打开,如果打开,调到第8步; 5、判断线程池、队列、信号量是否已满,已满则调到第8步; 6、执行 HystrixObservableCommand.construct()或HystrixCommand.run(), 如果执行失败或者超时,跳到第8步;否者,跳到第9步; 7、统计熔断器监控指标; 8、走Fallback降级方法; 9、返回请求响应。 从流程图上可知道,第5步线程池、队列、信号量已满时,还会执行第7步逻辑,更新熔断器统计信息,而第6步无论成功与否,都会更新熔断器统计信息。 (二)执行命令的几种方法: Hystrix提供了4种执行命令的方法,execute()和queue()适用于 HystrixCommand 对象,而observer()和toObservable()适用于 HystrixObservableCommand对象。 1、execute() 以同步阻塞方法执行run(),只支持接收一个值对象。 Hystrix会从线程池中取一个线程来执行run(),并等待返回值。 2、queue() 以异步非阻塞方法执行run(),只支持接收一个值对象。调用queue()就直接返回一个Future对象。可通过Future.get()拿到run()的返回结果,但 Future.get() 是阻塞执行的。若执行成功, Future.get() 返回单个返回值。当执行失败时,如果没有重写fallback, Future.get() 抛出异常。 3、observe() 事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。调用observe()会返回一个hot Observable,也就是说,调用 observe()自动触发执行run()/construct(),无论是否存在订阅者。 如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以调用线程阻塞执行construct()。 observe()使用方法: (1)调用 observe()会返回一个Observable对象 (2)调用这个 Observable对象的subscribe()方法完成事件注册,从而获取结果 4、toObservable() 事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。调用 toObservable() 会返回一个cold Observable,也就是说,调用 toObservable() 不会立即触发执行run()/construct(),必须有订阅者订阅 Observable时才会执行。 如果继承的是 HystrixComman,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是 HystrixObservableCommand ,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。 toObservable()使用方法: (1)调用observe()会返回一个Observable对象 (2)调用这个 Observable对象的subscribe()方法完成事件注册,从而获取结果 需注意的是, HystrixCommand也支持 toObservable()和observe(),但是即使将 HystrixCommand转换成Observable,它也只能发射一个值对象。只有 HystrixObservableCommand才支持发射多个值对象。 (三)几种方法的关系 execute()实际是调用了queue().get() queue()实际调用了toObservable().toBlocking().toFuture() observe()实际调用toObservable()获得一个coldObservable,再创建一个ReplaySubject对象订阅Observable,将源Observable转化为hot Observable。因此调用observe()会自动触发执行run()/construct()。 Hystrix 总是以Observable的形式作为相应返回,不同执行命令的方法只是进行了相应的转换。 五、 Hystrix 容错 Hystrix 的容错主要是通过添加容许延迟和容错方法,帮助控制这些分布式服务之间的交互。还通过隔离服务之间的访问点,阻止它们之间的级联故障以及提供退回选项来实现这一点,从而提高系统的整体弹性。 Hystrix主要提供了一下几种容错方法: 资源隔离 熔断 降级 (一)资源熔断 资源隔离主要指对线程的隔离。 Hystrix提供了两种线程隔离的方式:线程池和信号量。 1、线程隔离-线程池 Hystrix还通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。如订单服务查询商品,查询商品请求->商品command;商品服务查询库存,查询库存请求->库存command。并且为每个类型的command配置一个线程池,当第一次创建command时,根据配置创建一个线程池,并放入ConcurrentHashMap,如商品command: final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>(); ... if (!threadPools.containsKey(key)) { threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)); } 后续查询商品的请求创建command时,将会重用已创建的线程池。线程池隔离之后的服务依赖关系: 通过发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。 线程池隔离优点: 保护应用程序以免受来自依赖故障的影响,指定依赖线程池饱和不会影响应用程序的其余部分。 当引入新客户端lib时,即使发生问题,也是在lib中,并不会影响其他内容。 当依赖从故障恢复正常时,应用程序会立即恢复正常的性能。 当应用程序一些配置参数错误时,线程池的运行状况会很快检测到这一点(通过增加错误、延迟、超时、拒绝等),同时可以通过动态属性进行实时纠正错误的参数配置。 如果服务的性能有变化,需要实时调整,比如增加或减少超时时间,更改重试次数,可以通过线程池指标状态属性修改,而且不会影响到其它调用请求。 除了隔离优势外, Hystrix 拥有专门的线程可提供内置的并发功能,使得可以在同步调用之上构建异步门面(外观模式),为异步编程提供了支持( Hystrix 引入了R小Java异步框架)。 注意:尽管线程池提供了线程隔离,我们的客户端底层代码也必须要有超时设置或响应线程中断,不能无限制的阻塞以致线程池一直饱和。 缺点: 线程池的主要缺点是增加了计算开销。每个命令的执行都在单独的线程完成,增加了排队、调度和上下文切换的开销。因此,要使用 Hystrix ,就必须接受它带来的开销,以换取它所提供的的好处。 通常情况下,线程池引入的开销足够小,不会有重大的成本和性能影响。但对于一些访问延迟极低的服务,如只依赖内存缓存,线程池引入的开销就比较明显了,这时候使用线程池隔离技术就不合适了,我们需要考虑更轻量级的方式,如信号量隔离。 2、线程隔离-信号量 上面提到了线程池隔离的缺点,当依赖延迟极低的服务时,线程池隔离技术引入的开销超过了它所带来的好处。这时候可以使用信号量隔离技术来代替,通过设置信号量来限制对任何给定依赖的并发调用量。下图说明了线程池隔离和信号量隔离的主要区别: 使用线程池时,发送请求的线程和执行依赖服务的线程不是同一个,而使用信号量时,发送请求的线程和执行依赖服务的线程时同一个, 都是发起请求的线程。 3、线程隔离总结 线程池和信号量都可以做线程隔离,但各有各的优缺点和支持的场景,对比如下: 线程切换 支持异步 支持超时 支持熔断 限流 开销 信号量 否 否 否 是 是 小 线程池 是 是 是 是 是 大 线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务, Hystrix会通过响应中断的方式通知线程立即结束并返回。 (二)熔断器 现实生活中,可能大家都有注意到家庭电路中通常会安装一个保险盒,当负载过载时,保险盒中的保险丝会自动熔断,以保护电路及家里的各种电器,这就是熔断器的一个常见例子。Hystrix中的熔断器(Circuit Breaker)也是起类似作用,Hystrix在运行过程中会向每个commandKey对应的熔断器报告成功、失败、超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计信息来决策熔断开关是否打开。如果打开,熔断后续请求,快速返回。隔一段时间(默认是5s)之后熔断器尝试半开,放入一部分流量请求进来,相当于对依赖服务进行一次健康检查,如果请求成功,熔断器关闭。 熔断器配置,Circuit Breaker主要包括如下6个参数: 1、circuitBreaker.enabled 是否启用熔断器,默认是TRUE。 2 、circuitBreaker.forceOpen 熔断器强制打开,始终保持打开状态,不关注熔断开关的实际状态。默认值FLASE。 3、circuitBreaker.forceClosed 熔断器强制关闭,始终保持关闭状态,不关注熔断开关的实际状态。默认值FLASE。 4、circuitBreaker.errorThresholdPercentage 错误率,默认值50%,例如一段时间(10s)内有100个请求,其中有54个超时或者异常,那么这段时间内的错误率是54%,大于了默认值50%,这种情况下会触发熔断器打开。 5、circuitBreaker.requestVolumeThreshold 默认值20。含义是一段时间内至少有20个请求才进行errorThresholdPercentage计算。比如一段时间了有19个请求,且这些请求全部失败了,错误率是100%,但熔断器不会打开,总请求数不满足20。 6、circuitBreaker.sleepWindowInMilliseconds 半开状态试探睡眠时间,默认值5000ms。如:当熔断器开启5000ms之后,会尝试放过去一部分流量进行试探,确定依赖服务是否恢复。 (三)熔断器工作原理 下图展示了HystrixCircuitBreaker的工作原理: 熔断器工作的详细过程如下: 第一步,调用 allowRequest() 判断是否允许将请求提交到线程池 1、允许熔断器强制打开, circuitBreaker.forceOpen为true,不允许放行,返回。 2、如果熔断器强制关闭, circuitBreaker.forceOpen为true,允许放行。 此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。 第二步,调用isOpen()判断熔断器开关是否打开 1、 如果熔断器开关打开,进入第三步,否则继续; 2、 如果一个周期内总的请求数小于circuitBreaker.requestVolumeThreshold的值,允许请求放行,否则继续; 3、 如果一个周期内错误率小于circuitBreaker.errorThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。 第三步, 调用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复 如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。 此外,为了提供决策依据,每个熔断默认维护了10个bucket,每秒一个bucket,当心的bucket被创建时,最旧的bucket会被抛弃。其中每个bucket维护了请求、失败、超时、拒绝的计数器,Hystrix负责收集并统计这些计数器。 (四)回退降级 降级,通常指事务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。 Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提高服务的健壮性。要支持回退或降级处理,可以重写 HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法。 1、Hystrix在以下几种情况下会走降级逻辑: 执行construct()或run()抛出异常 熔断器打开导致命令短路 命令的线程池和队列或信号量的容量超额,命令被拒绝 命令执行超时 2、降级回退方式 (1)Fail Fast快速失败 快速失败是最普通的命令执行方法,命令没有重写降级逻辑。如果命令执行发生任何类型的故障,它将直接抛出异常。 (2)Fail Fast无声失败 指在降级方法中通过返回null,空Map,空List或其他类似的响应来完成。 (3)FallBack:Static 指在降级方法中返回静态默认值。 这不会导致服务以“无声失败”的方式被删除,而是导致默认行为发生。如:应用根据命令执行返回true / false执行相应逻辑,但命令执行失败,则默认为true。 (4)FallBack:Stubbed 当命令返回一个包含多个字段的复合对象时,适合以Stubbed 的方式回退。 (5)FallBack:Cache via Network 有时,如果调用依赖服务失败,可以从缓存服务(如redis)中查询旧数据版本。由于又会发起远程调用,所以建议重新封装一个Command,使用不同的ThreadPoolKey,与主线程池进行隔离。 (6)Primary+Secondary with FallBack 有时系统具有两种行为- 主要和次要,或主要和故障转移。主要和次要逻辑涉及到不同的网络调用和业务逻辑,所以需要将主次逻辑封装在不同的Command中,使用线程池进行隔离。为了实现主从逻辑切换,可以将主次command封装在外观HystrixCommand的run方法中,并结合配置中心设置的开关切换主从逻辑。由于主次逻辑都是经过线程池隔离的HystrixCommand,因此外观HystrixCommand可以使用信号量隔离,而没有必要使用线程池隔离引入不必要的开销。原理图如下: 主次模型的使用场景还是很多的。如当系统升级新功能时,如果新版本的功能出现问题,通过开关控制降级调用旧版本的功能。 通常情况下,建议重写getFallBack或resumeWithFallback提供自己的备用逻辑,但不建议在回退逻辑中执行任何可能失败的操作。 六、总结 本文介绍了Hystrix及其工作原理,还介绍了Hystrix线程池隔离、信号量隔离和熔断器的工作原理,以及如何使用Hystrix的资源隔离,熔断和降级等技术实现服务容错,从而提高系统的整体健壮性。 虽然Hystrix已经停更很久了,Spring Cloud体系的使用者和拥护者一片哀嚎,实际上,spring作为java最大的家族,根本不需要担心其中一两个组件的废弃, Hystrix的停更,只会催生更多更好的组件替代它,但是 Hystrix既然存在过,就一定就它存在的价值,既然存在,我就必须搞懂它。 每一篇博客都是一种经历,程序猿生涯的痕迹,知识改变命运,命运要由自己掌控,愿你游历半生,归来仍是少年。 欲速则不达,欲达则欲速! 更多精彩内容,首发公众号【素小暖】,欢迎关注。

优秀的个人博客,低调大师

微服务SpringCloud之注册中心Consul

Consul 介绍 Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。 Consul 的优势: 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft。 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。 zookeeper 和 etcd 均不提供多数据中心功能的支持。 支持健康检查。 etcd 不提供此功能。 支持 http 和 dns 协议接口。 zookeeper 的集成较为复杂, etcd 只支持 http 协议。 官方提供 web 管理界面, etcd 无此功能。 综合比较, Consul 作为服务注册和配置管理的新星, 比较值得关注和研究。 特性: 服务发现 健康检查 Key/Value 存储 多数据中心 Consul 角色 client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群。 server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其它数据中心通讯。 每个数据中心的 server 数量推荐为 3 个或是 5 个。 Consul 客户端、服务端还支持夸中心的使用,更加提高了它的高可用性。 Consul 工作原理: 1、当 Producer 启动的时候,会向 Consul 发送一个 post 请求,告诉 Consul 自己的 IP 和 Port 2、Consul 接收到 Producer 的注册后,每隔10s(默认)会向 Producer 发送一个健康检查的请求,检验Producer是否健康 3、当 Consumer 发送 GET 方式请求 /api/address 到 Producer 时,会先从 Consul 中拿到一个存储服务 IP 和 Port 的临时表,从表中拿到 Producer 的 IP 和 Port 后再发送 GET 方式请求 /api/address 4、该临时表每隔10s会更新,只包含有通过了健康检查的 Producer Consul的安装 从官网https://www.consul.io/downloads.html下载对应的版本,这里下载的是window版本,然后cmd命令启动,在浏览器输入http://localhost:8500,有正常页面输出则启动成功 consul agent -dev # -dev表示开发模式运行,另外还有-server表示服务模式运行 Consul 服务端 1.引入依赖 这里需要引入spring-cloud-starter-consul-discovery、spring-cloud-starter-consul-all、consul-api、spring-boot-starter-actuator,参考博客未引入spring-cloud-starter-consul-all、consul-api两个依赖,在我测试的时候报阿里云仓库找不到502的错误。 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>SpringCloudConsulProducer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringCloudConsulProducer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-all</artifactId> <version>2.1.3.RELEASE</version></dependency><dependency> <groupId>com.ecwid.consul</groupId> <artifactId>consul-api</artifactId> <version>1.4.2</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <packaging>war</packaging></project> 2.配置文件 server.port=8764spring.application.name=consul-providerspring.cloud.consul.host=127.0.0.1spring.cloud.consul.port=8500spring.cloud.consul.discovery.service-name=consul-provider 3.在main类中添加@EnableDiscoveryClient注解 4.增加Controller 这里创建了HelloController,这里需要添加@RestController注解,如果没添加在客户端调用的时候就会报404的错误。 package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController { @RequestMapping("/hello") public String hello(@RequestParam(value = "name") String name) { return "helle consul "+name; }} Consul消费者 1.引入依赖 在上面的基础上增加feign的引入 <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>SpringCloudConsulConsumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>SpringCloudConsulConsumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-all</artifactId> <version>2.1.3.RELEASE</version> </dependency> <dependency> <groupId>com.ecwid.consul</groupId> <artifactId>consul-api</artifactId> <version>1.4.2</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-openfeign-core --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-openfeign-core</artifactId> <version>2.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project> 2.配置文件 spring.application.name=spring-cloud-consul-consumerserver.port=8507spring.cloud.consul.host=127.0.0.1spring.cloud.consul.port=8500#设置不需要注册到 consul 中spring.cloud.consul.discovery.register=falsefeign.hystrix.enabled=false 3.增加注解 在main类中增加@EnableFeignClients注解 4.参考Eureka中feign的调用 首先创建HelloRemote接口 package com.example.demo;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;@FeignClient(name= "consul-provider")public interface HelloRemote { @RequestMapping(value = "/hello") public String hello(@RequestParam(value = "name") String name);} 然后创建接口的实现 package com.example.demo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class CallHelloController { @Autowired HelloRemote HelloRemote; @RequestMapping("/hello") public String index(@RequestParam String name) { return HelloRemote.hello(name); }} 测试 依次启动SpringCloudConsulProducer、SpringCloudConsulConsumer,在浏览器输入http://localhost:8507/hello?name=cuiyw 负载均衡 修改SpringCloudConsulProducer项目的端口,在HelloController中修改hello方法以区分调用 package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController { @RequestMapping("/hello") public String hello(@RequestParam(value = "name") String name) { return "helle consul two"+name; }} 启动实例之后浏览器刷新http://localhost:8507/hello?name=cuiyw,可以看到下面两个响应内容,从而实现负载均衡的效果。 扩展阅读:https://www.roncoo.com/view/41

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册