SpringCloud介绍
spring cloud 是一系列框架的集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起=,通过 spring boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
spring cloud 对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用 spring cloud 一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和 docker 容器概念的火爆,也会让 spring cloud 在未来越来越“云”化的软件开发风格中立有一席之地,尤其是在目前五花八门的分布式解决方案中提供了标准化的、一站式的技术方案,意义可能会堪比当年 servlet 规范的诞生,有效推进服务端软件系统技术水平的进步。
SpringCloud技术组成
- eureka 微服务治理,服务注册和发现
- ribbon 负载均衡、请求重试
- hystrix 断路器,服务降级、熔断
- feign ribbon + hystrix 集成,并提供声明式客户端
- hystrix dashboard 和 turbine hystrix 数据监控
- zuul API 网关,提供微服务的统一入口,并提供统一的权限验证
- config 配置中心
- bus 消息总线, 配置刷新
- sleuth+zipkin 链路跟踪
Spring Cloud 对比 Dubbo
- Dubbo
- Dubbo只是一个远程调用(RPC)框架
- 默认基于长连接,支持多种序列化格式
- Spring Cloud
- 框架集
- 提供了一整套微服务解决方案(全家桶)
- 基于http调用, Rest API
Service服务(暂留)
Spring MVC接收参数的几个注解
1.eureka注册于发现(2001)
spring:
application:
name: eureka-server
server:
port: 2001
eureka:
server:
#eureka 的自我保护状态:心跳失败的比例,在15分钟内是否超过85%,如果出现了低于的情况,
#Eureka Server会将当前的实例注册信息保护起来,同时提示一个警告,一旦进入保护模式,
#Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据。也就是不会注销任何微服务
enable-self-preservation: false
instance:
#eureka 集群服务器之间,通过 hostname 来区分
hostname: eureka1
client:
#不向自身注册
register-with-eureka: false
#不从自身拉取注册信息
fetch-registry: false
- eureka.instance.lease-expiration-duration-in-seconds
最后一次心跳后,间隔多久认定微服务不可用,默认90
- eureka.instance.lease-renewal-interval-in-seconds
心跳间隔时间,默认 30 秒
- eureka.client.registry-fetch-interval-seconds
拉取注册信息间隔时间,默认 30 秒
主程序
添加@EnableEurekaServer
修改hosts文件,添加eureka域名映射
127.0.0.1 eureka1
127.0.0.1 eureka2
…
把user-service 和 order-service等微服务注册到eureka服务器
yml添加eureka注册配置
eureka:
client:
service-url:
#defaultZone,默认位置,可以修改为具体地理位置,比如:beiJing, shangHai, shenZhen 等,表示 eureka 服务器的部署位置, 需要云服务器提供
defaultZone: http://eureka1:2001/eureka
user-service 和 order-service的主程序
添加@EnableDiscoveryClient
2.eureka 和 “服务提供者”的高可用
item-service 高可用
启动参数 --server.port 可以覆盖yml中的端口配置
–server.port=8001
eureka 高可用
添加两个服务器的 profile 配置文件
application-eureka1.yml
eureka:
instance:
hostname: eureka1
client:
register-with-eureka: true #profile的配置会覆盖公用配置
fetch-registry: true #profile的配置会覆盖公用配置
service-url:
defaultZone: http://eureka2:2002/eureka #eureka1启动时向eureka2注册
application-eureka2.yml
eureka:
instance:
hostname: eureka2
client:
register-with-eureka: true #profile的配置会覆盖公用配置
fetch-registry: true #profile的配置会覆盖公用配置
service-url:
defaultZone: http://eureka1:2001/eureka #eureka2启动时向eureka1注册
配置启动参数 --spring.profiles.active 和 --server.port
- eureka1 启动参数:
–spring.profiles.active=eureka1 --server.port=2001 - eureka2 启动参数:
–spring.profiles.active=eureka2 --server.port=2002
eureka客户端注册时,向两个服务器注册
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
3.ribbon服务消费者(3001)
ribbon 提供了负载均衡和重试功能, 它底层是使用 RestTemplate 进行 Rest api 调用
RestTemplate
RestTemplate 是StringBoot提供的一个Rest远程调用工具
它的常用方法:
- getForObject() - 执行get请求
- postForObject() - 执行post请求
之前的系统结构是浏览器直接访问后台服务
后面我们通过一个Demo项目演示 Spring Cloud 远程调用
我们先不使用ribbon, 单独使用RestTemplate来执行远程调用
主程序
- 创建 RestTemplate 实例
RestTemplate 是用来调用其他微服务的工具类,封装了远程调用代码,提供了一组用于远程调用的模板方法,例如:getForObject()、postForObject() 等
//创建 RestTemplate 实例,并存入 spring 容器
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
RibbonController
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
//向指定微服务地址发送 get 请求,并获得该服务的返回结果
//{1} 占位符,用 orderId 填充
return rt.getForObject("http://localhost:8001/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
//发送 post 请求
return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
}
4.ribbon负载均衡和重试
Ribbon 负载均衡
RestTemplate 设置 @LoadBalanced
@LoadBalanced 负载均衡注解,会对 RestTemplate 实例进行封装,创建动态代理对象,并切入(AOP)负载均衡代码,把请求分发到集群中的服务器
@LoadBalanced //负载均衡注解
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
访问路径设置为服务id
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
//这里服务器路径用 service-id 代替,ribbon 会向服务的多台集群服务器分发请求
return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
ribbon 重试
application.yml 配置 ribbon 重试
ribbon:
MaxAutoRetriesNextServer: 2
MaxAutoRetries: 1
OkToRetryOnAllOperations: true
- ConnectionTimeout
- ReadTimeout
- OkToRetryOnAllOperations=true
默认只对GET请求重试, 当设置为true时, 对POST等所有类型请求都重试 - MaxAutoRetriesNextServer
更换实例的次数 - MaxAutoRetries
当前实例重试次数,尝试失败会更换下一个实例
主程序设置 RestTemplate 的请求工厂的超时属性
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
f.setConnectTimeout(1000);
f.setReadTimeout(1000);
return new RestTemplate(f);
//RestTemplate 中默认的 Factory 实例中,两个超时属性默认是 -1,
//未启用超时,也不会触发重试
//return new RestTemplate();
}
item-service 的 ItemController 添加延迟代码,以便测试 ribbon 的重试机制
@Value("${server.port}")
private int port;
@GetMapping("/{orderId}")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws Exception {
log.info("server.port="+port+", orderId="+orderId);
///--设置随机延迟
if(Math.random()<0.6) {
long t = new Random().nextInt(5000);
log.info("item-service-"+port+" - 暂停 "+t);
Thread.sleep(t);
}
///~~
List<Item> items = itemService.getItems(orderId);
return JsonResult.ok(items).msg("port="+port);
}
5.Hystrix 断路器(3001)
主程序添加 @EnableCircuitBreaker 启用 hystrix 断路器
启动断路器,断路器提供两个核心功能:
-
降级,超时、出错、不可到达时,对服务降级,返回错误信息或者是缓存数据
-
熔断,当服务压力过大,错误比例过多时,熔断所有请求,所有请求直接降级
-
可以使用 @SpringCloudApplication 注解代替三个注解(@EnableCircuitBreaker@EnableDiscoveryClient@SpringBootApplication)
RibbonController 中添加降级方法
- 为每个方法添加降级方法,例如 getItems() 添加降级方法 getItemsFB()
- 添加 @HystrixCommand 注解,指定降级方法名
@Autowired
private RestTemplate rt;
@GetMapping("/item-service/{orderId}")
@HystrixCommand(fallbackMethod = "getItemsFB") //指定降级方法的方法名
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
return rt.getForObject("http://item-service/{1}", JsonResult.class, orderId);
}
@PostMapping("/item-service/decreaseNumber")
@HystrixCommand(fallbackMethod = "decreaseNumberFB")
public JsonResult decreaseNumber(@RequestBody List<Item> items) {
return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
hystrix 超时设置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
hystrix等待超时后, 会执行降级代码, 快速向客户端返回降级结果, 默认超时时间是1000毫秒
为了测试 hystrix 降级,我们把 hystrix 等待超时设置得非常小(500毫秒)
此设置一般应大于 ribbon 的重试超时时长,例如 10 秒
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 500
6.hystrix dashboard 断路器仪表盘
hystrix 对请求的降级和熔断,可以产生监控信息,hystrix dashboard可以实时的进行监控
sp07-hystrix 项目添加 actuator,并暴露 hystrix 监控端点(3001)
actuator 是 spring boot 提供的服务监控工具,提供了各种监控信息的监控端点
management.endpoints.web.exposure.include 配置选项,
可以指定端点名,来暴露监控端点
如果要暴露所有端点,可以用 “*”
调整 application.yml 配置,并暴露 hystrix.stream 监控端点
management:
endpoints:
web:
exposure:
include: hystrix.stream
Hystrix dashboard 仪表盘(4001)
主程序添加 @EnableHystrixDashboard 和 @EnableDiscoveryClient
访问 hystrix dashboard
http://localhost:4001/hystrix
填入 hystrix 的监控端点,开启监控
http://localhost:3001/actuator/hystrix.stream
hystrix 熔断
整个链路达到一定的阈值,默认情况下,10秒内产生超过20次请求,则符合第一个条件。
满足第一个条件的情况下,如果请求的错误百分比大于阈值,则会打开断路器,默认为50%。
Hystrix的逻辑,先判断是否满足第一个条件,再判断第二个条件,如果两个条件都满足,则会开启断路器
断路器打开 5 秒后,会处于半开状态,会尝试转发请求,如果仍然失败,保持打开状态,如果成功,则关闭断路器