开发结构逐步发展,随便梳理一下
单机版,全封闭,黑盒子。
主体的表现就是单机黑盒
,显著的表现就是兄弟义气
。
兄弟义气:有福同享,有难同当,个体即集体,集体及个体,我为人人,人人为我
反正出现些鸡毛蒜皮,想要修改,就不得不停服务。
更关键的,服务只有一个。
模块化之后,分工确定了,容易管理,也更容易排错。
这算是开发管理,但是根本问题还是没有解决,服务仍然是单例。
即使容易定位问题,修改时还是不得不停掉服务。
模块服务化,把细小的模块进行放大,以后修改时,可以停掉某个服务,不影响客户的体验。
好比用
RabbitMq
进行了分隔的两个模块服务。当任务处理服务停掉时,仍然能够接收,创建任务。
创建任务服务不行了,但是已经接收的任务依旧会进行处理。
而且,这还带来了两个好处
拥有模块化的优点:专精
同时,也更加容易进行维护,且不影响用户体验。
提升服务效率的同时还不影响服务体验。
数据已经在队列里面了,即使处理服务坏掉了,来不及修的情况下,我们可以启动同服务的新实例。
也就是分布式中的多实例,或者
负载均衡
来容灾也可以。
分布式的话,可以说把问题进一步放大了。
目光不再局限于单机,转向了多机器的综合稳定上面了。
回顾一下发展
- 功能拆分
从
单功能
转向功能模块
,容易组合,容易维护。
- 服务拆分
从
单进程
到多进程
,服务专精,提升效率,提升体验,提高容灾。
- 集群综合
从
单机
到分布式
,不局限于服务的多机器部署,防止单一机器故障。而是多服务,多实例的共同维护,比如
redis
哨兵的集群。保证了服务的
可用性
。
之前也说了,虽然粒度越拆越细,但是管理是越来越高。
原来都是单机服务自己干,虽然不容易管理,但是牵扯真的很少。
一个人做很多,哪怕很累,但是都了然于心;人多了,你就不得不去依赖别人。
然后被别人阻塞。
服务始终是整体的,而不是片面的。
拆分之后,从功能上单一了,独立了,但是从服务上却不得不说更依赖其他模块进行补充了。
当任务接入进来的时候,依赖于其他模块的执行。
把两种模式进行对比的话
A作为入口,Z作为出口
- 单机
A -> Z
- 分布
A -> B -> C -> D -> … -> X -> Y -> Z
虽然流程都是相同的,但是服务化
让模块
独立了,服务周期明显拉长了。
可谓失之毫厘,谬以千里
,随着调用的加深,影响逐渐的扩大、加深、拓宽,这就是扇出
。
扇出
是针对服务周期
而言的,接下来说一下专精
的破坏力。
假设我的中间队列大小是500,接收速度是200, 处理速度是200。
服务化,分布式以后,更加专精了。
接收速度翻倍成400,处理速度翻倍成400。
或者1000-1000,很好很强大。
但是处理模块坏掉的话,留给我们的反应时间是
500/200 = 2.5
500/400 = 1.25
500/1000 = 0.5
简而言之,留给我的反应时间越来越少了。
由于扇出,对后面的服务依赖加重。
加上专精
,如果后面的服务出现问题,服务就会层级的崩溃。
正如扇出
的向后依赖加重,雪崩
是向前影响加深。
人有力尽时
,办法虽然很多,问题却总不能够全部排尽。
即使多实例
,即使分布式
,我们也只能高可用
,却做不到必可用
。
回顾最初的根源,我们只能尽力的维护快速应答
这一逻辑联系。
于此,我们转换思路,变成快速失败
。
当服务异常的时候,为了防止
扇出
引起的依赖加重由于异常引起阻塞
进而导致的雪崩
。我们需要维护
快速应答
,即使处理方式是错误的,但是只要能够维持正确的应答方式
,就能够为我们争取到足够的时间进行补救。
也不一定是快速失败
,主要是不能够阻塞
,必须尽力维护正常通信
。
也就是说,我们必须设置一个服务失败时的备用处理方案,进而防止雪崩
。
正如一个链接,你能够等待多久。
如果需要半小时,而且事先不知道的情况下,你会继续等下去么。
即使结果出来了,体验不好,也没啥好心情了。
更别说后续还有好多人也等你等完然后来访问,你等的越久,后面的人等的越久。
排队的时间可能比服务的时间更久。
但是如果超过五秒就返回
500
,至少心情没那么坏,服务器压力也没那么大,更没有阻塞
。尽快的修复服务,也就没啥大问题了,哪怕是个
500
。
hystrix
客户端
@RestController public class MyController { @GetMapping("/div") public String div(@RequestParam("a") String a, @RequestParam("b") String b){ return String.valueOf(Integer.valueOf(a) / Integer.valueOf(b)); } }
拿这个作为开头,如果我这样访问会怎样
curl http://8080/div?a=10&b=0
作为微服务,需要等待你的返回结果,但是异常了,阻塞了。
前面的不断积压,后面的闲的要死,该当何罪。
hystrix
改动一下。
pom
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency>
controller
@RestController public class MyController { @GetMapping("/div") @HystrixCommand(fallbackMethod = "error") public String div(@RequestParam("a") String a, @RequestParam("b") String b){ return String.valueOf(Integer.valueOf(a) / Integer.valueOf(b)); } public String error(String a, String b){ return "error"; } }
HystrixCommand
:断路器
fallbackMethod
:指定失败时的处理方法
error
:经过试验,需同参同返回
main
@EnableEurekaClient @SpringBootApplication @EnableHystrix public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
然后,即使是这样
curl http://8080/div?a=10&b=0
给我的虽然是error
,但是服务确实正常的,正常通信得以维护。
有一个盲点,不知道注意到没有。
客户端使用
hystrix
的确有效果,但是针对的是服务异常
。但是分布式中,焦点从
逻辑
转移到了服务
。也就是说,如果坏掉的不是程序,不是异常情况,而是服务器宕机了怎么办。
如果服务本身就挂掉了,怎么还能够给你
备选方案
的响应呢。最终,这种方案,还能没能够维护
正常通信
,保证快速交互
。
不说了,服务端来一发。
pom
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> <version>1.4.6.RELEASE</version> </dependency>
service
@FeignClient(value = "PROVIDER") public interface Service { @RequestMapping(value = "/div") public String calc(@RequestParam("a") String a, @RequestParam("b") String b); }
Feign
的rest接口
,没忘记吧
controller
@RestController public class MyController { Service service; @GetMapping("/calc") @HystrixCommand(fallbackMethod = "error") public String calc(@RequestParam("a") String a, @RequestParam("b") String b){ String result = service.calc(a, b); return result; } public String error(String a, String b){ return "godme"; } }
客户端的记得去掉,要不实验结果不准
main
@EnableDiscoveryClient @SpringBootApplication @EnableFeignClients @EnableHystrix public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }
然后访问一下
curl http://localhost:8802/calc?a=10&b=0
不报错了,给了个godme
。
不过,注意到了么,概念又转换了。
- 阻塞
最开始出现问题,就会阻塞服务,这是根源问题。
- 异常应答
然后异常发源地自己管理,异常时候维护
正常通信
,避免阻塞
。
- 异常处理
不过
provider
即使具备异常排除的功能,也不能够一定的维护正常通信
。因为它本身作为服务,就可能会宕掉,不是不信任你,这已经超出了它自身的能力范围。
这种情况下,调用方自己处理,不再依赖于
provider
的信息反馈
。就和访问连接一样,服务异常的时候,它自动提示
500
,这种办法是有好处的。但是,它全部都挂掉了,不给我
500
,我还在等着呢,等到啥时候啊。所以设置好超时以后,规定时间范围内,等它返回。
超过一定时间,我就自己处理了,不干等。
我们使用浏览器访问服务时就是这样,找不到的超时就报错,绝不傻傻的等。
先斩后奏,事急从权,顾客是上帝,不等你了,就是这样。
如果对于controller
中的每个接口都@HystrixCommand
,也很繁琐啊,介绍一个,不,两个好方法。
fallback
@Component public class MyFallback implements Service { @Override public String calc(String a, String b) { return "godme"; } }
恩,实现
Service
接口,里面的就是错误时候的备选方案。记得
Component
,要不行不通的哦
@FeignClient(name = "PROVIDER", fallback = MyFallback.class) public interface Service { @RequestMapping(value = "/div") public String calc(@RequestParam("a") String a, @RequestParam("b") String b); }
@FeignClient(name = "PROVIDER", fallback = MyFallback.class)
指定微服务名称时候直接指定错误时回调的
Service
即可,会自动找到对应接口进行处理的。
feign: hystrix: enabled: true
这个一定要配置上,我会连续说三遍的
fallbackFactory
@Component public class MyFallback implements FallbackFactory<Service> { @Override public Service create(Throwable cause) { return new Service() { @Override public String calc(String a, String b) { return "godme"; } }; } }
FallbackFactory
:泛型,记得是自己rest接口
哦相比较于
fallback
,这个会吧cause
传进来,信息更丰富一些
@FeignClient(name = "PROVIDER", fallbackFactory = MyFallback.class) public interface Service { @RequestMapping(value = "/div") public String calc(@RequestParam("a") String a, @RequestParam("b") String b); }
@FeignClient(name = "PROVIDER", fallbackFactory = MyFallback.class)
不用怀疑,不一样的。
fallback
变成了fallbaclfactory
feign: hystrix: enabled: true
配置文件,第二遍了
因为是
feign
中配置的hystrix
,默认是关闭的,一定要开启啊
yaml
feign: hystrix: enabled: true
第三遍了,为什么要说三遍呢,不仅是因为它很重要。
更因为有个傻瓜干调了三个小时,翻阅了好多文档、博客、
github
。结果是自己二逼手写
enabled
自信写成了enable
。
feign
的配置也没有个自动提示,也就这样过去了,然后就是hystrix
死活不生效。配置文件的字段都是
圣经
啊,理解可以不同,写出来一个字母都不能有偏差啊。
dashboard
pom
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.0.2.RELEASE</version> </dependency>
这个东西很重要,为什么呢,看看这个
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> <version>1.4.6.RELEASE</version> </dependency>你可能会遇见
java.lang.NoSuchMethodError: org.springframework.boot.builder.SpringApplicationBuilder
这种错误,大部分是版本不匹配。不过放心好了,还可能是包倒错了,一个单词之差。
enable
@SpringBootApplication @EnableHystrixDashboard public class DashboardApplication { public static void main(String[] args) { SpringApplication.run(DashboardApplication.class, args); } }
@EnableHystrixDashboard
:使能,我会告诉你导错包的话除了启动会报错,其他完全正确么。
yml
server: port: 9001
给个端口就行,其他没啥。
- 无代码
- 不注册
因为就是一个监控服务,起来就行,连接的时候再指定即可,无需其他操作,不用注册。
http://localhost:port/hystrix不过,对需要监察的服务有要求,必须开启
actuator
,否则状态是看不到的。
actuator
pom
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.1.0.RELEASE</version> </dependency>
yml
management: endpoints: web: exposure: include: "*" server: port: 54301 servlet: context-path: / ssl: enabled: false endpoint: health: show-details: always然后就没有问题了.
- 检查
- 状态
http://localhost:port/actuator # 先访问自身确定是否开启访问
- 流访问
http://localhost:port/actuator/hystrix.stream # 流访问,会不停的 ping 去获取信息
- 统计页面
http://localhost:port/actuator/hystrix.stream # 别问为什么和上面一样,因为就是上面那个啊 # 不过它会自身进行统计并绘图,那就好看很多
恩,先说一说
先决条件
actuator
hystrix
如果其中一个没配置,就免谈。
换句话说,如果我们忽视
hystrix
,不为了断路
,单纯为了监控,也必须把它配上。所以
hystrix
这个东西的两面啊,断路
可能只是装饰,如果我们指向要分析的话。然后说一下配置
pom
enable
使能是家常了,关键是
pom
啊,太像了,太麻糊人了。
问题都是一层层的递进的,不管是雪崩的渊源,还是客户端或者服务端的断路,仔细思考总能抓住。
很有意思,也很重要。
不过最后的dashboard
一出来,我想我可能只用来监控了,哈哈哈。
也不算是玩闹,因为hystrix
可以算作是服务管理
的一个组件,所谓雪崩
或者其他,都是为了保障服务运行。
客户端转移到服务端的断路
,单机变分布,都是管理
上的考量。
不仅专注于服务的功能
,目光转向稳定
,然后还要能够监控
。
实现固然是好的,但是不能沉迷半途,把服务监控
提升一下,不要只是一个能实现
服务监控的程序员。
如何监控,指标,描述,标准,这些东西同样有价值,或许更高。