springcloud
1.1简介
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
==Spring Cloud从技术架构上降低了对大型系统构建的要求和难度==,使我们以非常低的成本(技术或者硬件)搭建一套高效、分布式、容错的平台,但Spring Cloud也不是没有缺点,小型独立的项目不适合使用。
1.2一般开发中存在的问题
provider_service:对外提供用户查询接口
consumer_service:通过RestTemplate访问接口查询用户数据
存在的问题:
-
在消费者服务中,访问提供者服务URL地址硬编码,万一地址端口变化了怎么办?提供者服务死掉了消费者怎么才能知道?
-
在消费者服务中,是不清楚服务提供者状态的!
-
为了增加服务并发访问量,我们搭建集群,集群的负载均衡怎么实现?
-
服务提供者如果出现故障,会不会向用户抛出异常页面,该不该抛出错误页面?
-
RestTemplate这种请求调用方式是否还有优化空间?复用,管理,可读性角度来思考
-
多服务权限拦截如何实现?怎么保证所有微服务服务的安全性?
-
众多微服务的配置文件,每次都修改很多个,是不是很麻烦!?
其实上面说的部分问题,概括一下就是微服务架构必然面临的一些问题。
-
服务管理:自动注册与发现、状态监管
-
服务负载均衡
-
熔断
-
面向接口的远程调用
-
网关拦截、路由转发
-
统一配置修改
1.3注册中心 Spring Cloud Eureka
1.3.1整合注册中心Eureka
步骤分三步:
-
第一步:搭建eureka服务,创建eureka_server工程
-
第二步:服务提供者provider_service,注册到eureka注册中心
-
第三步:服务消费者consumer_service,注册到eureka注册中心
eureka_server:
1.创建eureka_server的springboot工程。
2.勾选坐标
3.在启动类EurekaServerApplication声明当前应用为Eureka服务使用@EnableEurekaServer
注解
4.编写配置文件application.yml
# 端口
server.port: 10086
# 应用名称,会在Eureka中作为服务的id标识(serviceId)
spring.application.name: eureka-server
# EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
5.启动EurekaServerApplication
6.测试访问地址http://127.0.0.1:10086,如下信息代表访问成功
7.关闭注册自己
# 是否抓取注册列表
eureka.client.fetch-registry: false
# 是否注册服务中心Eureka
eureka.client.register-with-eureka: false
provider_service:
1.在服务提供者provider_service工程中添加Eureka客户端依赖
<!--eureka客户端starter-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!--SpringCloud,BOM,依赖清单导入,所有依赖管理的坐标-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.在启动类上开启Eureka客户端发现功能@EnableDiscoveryClient
@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端发现功能
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
3.修改配置文件:spring.application.name指定应用名称,作为服务ID使用
# 端口
server.port: 9091
# DB 配置
spring.datasource.driver-class-name: com.mysql.cj.jdbc.Driver
spring.datasource.url: jdbc:mysql://127.0.0.1:3306/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.password: root
spring.datasource.username: root
# 应用名称
spring.application.name: user-service
# 注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
4.完成之后重启项目
5.客户端代码会自动把服务注册到EurekaServer中,在Eureka监控页面可以看到服务注册成功信息
consum-service:
-
通过注册中心客户端对象DiscoveryClient,获取Eureka中注册的user-service实例列表
-
获取user-service服务实例对象
-
从实例对象中获取host地址和端口,拼接请求地址
-
使用RestTemplate发送请求
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("{id}")
public User queryById(@PathVariable Long id){
String url = String.format("http://localhost:9091/user/%d", id);
//1、通过注册中心客户端对象DiscoveryClient,获取Eureka中注册的user-service实例列表
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("user-service");
//2、获取user-service服务实例对象
ServiceInstance serviceInstance = serviceInstanceList.get(0);
//3、从实例对象中获取host地址和端口,拼接请求地址
url = String.format("http://%s:%s/user/%d", serviceInstance.getHost(), serviceInstance.getPort(), id);
//4.使用RestTemplate发送请求
return restTemplate.getForObject(url,User.class);
}
}
# 默认注册时使用的是主机名,想用ip进行注册添加如下配置
# ip地址
eureka.instance.ip-address: 127.0.0.1
# 更倾向于使用ip,而不是host名
eureka.instance.prefer-ip-address: true
服务续约:
# 租约续约间隔时间,默认30秒
eureka.instance.lease-renewal-interval-in-seconds: 30
获取服务中心
# 每隔多久获取服务中心列表,(只读备份)
eureka.client.registry-fetch-interval-seconds: 30
失效剔除:
# 租约到期,服务时效时间,默认值90秒
eureka.instance.lease-expiration-duration-in-seconds: 90
自我保护:Eureka会统计服务实例最近15分钟心跳续约的比例是否低于85%,如果低于则会触发自我保护机制。
#向Eureka服务中心集群注册服务
eureka.server.enable-self-preservation: false # 关闭自我保护模式(默认值是打开)
1.3.2负载均衡 Spring Cloud Ribbon
实现步骤:
第一步:启动多个user_service服务
-
修改配置文件端口获取方式
-
编辑应用启动配置
-
启动两个提供者服务
-
在注册中心查询是否启动成功
第二步:开启消费者负载均衡
-
在RestTemplate的注入方法上加入@LoadBalanced注解
@Bean @LoadBalanced//开启负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); }
-
修改调用请求的Url地址,改为服务名称调用
@RequestMapping("/ribbonconsumer/{id}") public String queryByIdByRibbon(@PathVariable Integer id){ String url = "http://user-service/user/findById?id="+id; String user = restTemplate.getForObject(url, String.class); return user; }
-
访问页面查看效果
# 修改服务地址轮询策略,默认是轮询,配置之后变随机,RoundRobinRule
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
1.3.3熔断器 Spring Cloud Hystrix
Hystrix被设计的目标是:
-
对通过第三方客户端库访问的依赖项(通常是通过网络)的延迟和故障进行保护和控制。
-
在复杂的分布式系统中阻止雪崩效应。
-
快速失败,快速恢复。
-
回退,尽可能优雅地降级。
目标:服务提供者的服务出现了故障,服务消费者快速失败给用户友好提示。体验==服务降级==
实现步骤:
-
引入熔断的starter依赖坐标
-
开启熔断的注解@EnableCircuitBreaker
-
编写服务降级处理的方法
-
配置熔断的策略
-
模拟异常代码
-
测试熔断服务效果
实现过程:
引入熔断的依赖坐标:
-
consumer_service中加入依赖
<!--熔断Hystrix starter--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
开启熔断的注解
-
//注解简化写法:微服务中,注解往往引入多个,简化注解可以使用组合注解。@SpringCloudApplication =等同于@SpringBootApplication+@EnableDiscoveryClient+@EnableCircuitBreaker @SpringBootApplication @EnableDiscoveryClient//开启服务发现 @EnableCircuitBreaker//开启熔断 public class ConsumerApplication { @Bean @LoadBalanced//开启负载均衡 public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class,args); } }
编写服务降级处理方法:使用@HystrixCommand定义fallback方法。
@RequestMapping("/ribbonconsumer/{id}") @HystrixCommand(fallbackMethod ="queryByIdFallback") public String queryById(@PathVariable Long id){ String url = String.format("http://user-service/user/%d", id); return restTemplate.getForObject(url,String.class); } public String queryByIdFallback(Long id){ return "对不起,网络太拥挤了!"; }
配置熔断策略
-
常见熔断策略配置
-
熔断后休眠时间:sleepWindowInMilliseconds
-
熔断触发最小请求次数:requestVolumeThreshold
-
熔断触发错误比例阈值:errorThresholdPercentage
-
熔断超时时间:timeoutInMilliseconds
-
# 配置熔断策略: # 强制打开熔断器 默认false关闭的。测试配置是否生效 hystrix.command.default.circuitBreaker.forceOpen: false # 触发熔断错误比例阈值,默认值50% hystrix.command.default.circuitBreaker.errorThresholdPercentage: 20 # 熔断后休眠时长,默认值5秒 hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds: 60000 # 熔断触发最小请求次数,默认值是20 hystrix.command.default.circuitBreaker.requestVolumeThreshold: 5 # 熔断超时设置,默认为1秒 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 2000
-
方法上服务降级的fallback兜底方法
-
使用HystrixCommon注解,定义
-
@HystrixCommand(fallbackMethod="queryByIdFallBack")用来声明一个降级逻辑的fallback兜底方法
-
-
类上默认服务降级的fallback兜底方法
-
刚才把fallback写在了某个业务方法上,如果方法很多,可以将FallBack配置加在类上,实现默认FallBack
-
@DefaultProperties(defaultFallback=”defaultFallBack“),在类上,指明统一的失败降级方法;
-