Eureka配置
Eureka Server 的配置
① pom引入依赖
<!--eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
② yml文件配置
server:
port: 7001
spring:
application:
name: registry # 将来会作为微服务名称注入到eureka容器中
eureka:
client:
service-url: # ${server.port} 也可写死成7001,这么写更灵活一些
defaultZone: http://localhost:${server.port}/eureka # eureka客户端的服务地址
# 以下两项一定要是false,表明自己是服务器,而不需要从其他主机发现服务
register-with-eureka: false # false表示不向注册中心注册自己。
fetch-registry: false # false表示自己就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
server:
enable-self-preservation: false # 关闭自我保护模式
eviction-interval-timer-in-ms: 10000 # 每隔10秒钟,进行一次服务列表的清理
③ 启动类
@SpringBootApplication
@EnableEurekaServer //启用Eureka服务端
public class registryApplication {
public static void main(String[] args) {
SpringApplication.run(registryApplication.class);
}
}
Eureka Client 的配置
① pom引入依赖
<!--将微服务provider注册进eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
② yml 文件配置
server:
port: 8001
spring:
application:
name: registry # 将来会作为微服务名称注入到eureka容器中
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka # 注册进对应此地址的eureka服务中心
registry-fetch-interval-seconds: 5 # 设置拉取服务的间隔时间5秒
instance:
lease-renewal-interval-in-seconds: 5 # 心跳时间:5秒钟发送一次心跳
lease-expiration-duration-in-seconds: 15 # 过期时间:15秒不发送就过期
③ 启动类
@SpringBootApplication
@EnableDiscoveryClient //启用Eureka客户端,@EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient可以是其他注册中心。
public class leyouGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(leyouGatewayApplication.class);
}
}
④ 注册成功,在7001的Eureka服务站中会显示8001微服务
actuator与注册微服务信息完善
actuator:在springboot中主管监控和配置
- 主机名称、服务名称修改
① 修改8001的yml配置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
#服务实例名称修改
instance:
instance-id: study-springcloud-dept8001
- 访问信息有ip信息提示
② 修改8001yml配置
#客户端注册进eureka服务列表
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
#服务实例名称修改
instance-id: study-springcloud-dept8001
#访问路径显示IP地址
prefer-ip-address: true
Eureka自我保护机制
- Netflix在设置Eureka时,遵循AP原则
- 某时刻某一微服务不可用时,eureka不会立刻清理,依旧会对改微服务的信息进行保存。服务失去心跳、名称变更、网络拥堵
- **自我保护机制:**应对网络异常的安全措施
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险——因为微服务本身其实是健康的,此时本不应该注销这个服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点再短时间内丢失过多客户端时(可能发生网络故障),那么这个节点就会进入自我保护模式。一旦进去该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也即不会注销任何微服务)。当网络故障恢复后,改EurekaServer节点会自动退出自我保护模式。
宁可保护错误的注册信息,也不盲目注销任何可能健康的微服务实例。
禁用自我保护机制
可以使用eureka.server.enable-self-preservation=false
Eureka服务发现
对于注册进eureka里面的服务,可以通过服务发现获得该服务的信息。
① 添加服务发现接口
在8001工程的Controller类中增加DiscoveryClient。
@Autowired
private DiscoveryClient discoveryClient;
② 增加自己服务描述的接口
@RequestMapping(value = "/dept/discovery",method = RequestMethod.GET)
public Object discovery(){
List<String> list = discoveryClient.getServices();
List<ServiceInstance> instances = discoveryClient.getInstances("STUDY-SPRINGCLOUD-DEPT");
for (ServiceInstance element : instances){
System.out.println(element.getServiceId());
System.out.println(element.getHost());
System.out.println(element.getPort());
System.out.println(element.getUri());
}
return this.discoveryClient;
}
③ 8001主启动类中增加 @EnableDiscoveryClient 注解
④ 启动,调用 http://localhost:8001/dept/discovery
Ribbon负载均衡
概述:
基于Netflix ribbon实现的一套 客户端、负载均衡的工具。
重要功能是提供客户端的软件负载均衡算法,Ribbon客户端组件提供一系列的配置项如链接超时、重试等。
通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:
- 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
- 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。
这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。
Ribbon配置:
① 修改pom配置,增加相关引用
<!--Ribbon相关引用,与eureka有关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
② 修改application.yml ,追加Eureka的注册服务地址
eureka:
client:
service-url:
defaultZone: eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
# 将Ribbon的负载均衡策略修改为随机
service_provider: #采用Ribbon的服务实例
ribbon:
NFLoadBalanceRuleClassName:com.netflix.loadbalancer.RandomRule
③ 主启动类中增加注解@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer80App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80App.class,args);
}
}
④ 主启动类中增加getRestTemplate方法,并增加@LoadBalance注解,开启客户端负载均衡
/**
* 配置RestTemplate
* 通过RestTemplate调用提供者服务 ,发送rest请求
* 提供了多种访问http服务的方法,
* 针对于访问rest服务客户端的调用的模板类
*/
@Bean
@LoadBalanced //ribbon实现的一套 客户端、负载均衡的工具
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
⑤ 将地址改为注册在EurekaServer中的微服务名称
// 注册在EurekaServer中的微服务名称
@GetMapping
@ResponseBody
public User queryUserById(Long id){
return this.restTemplate.getForObject("http://service-provider/user/"+id,User.class);
}
Ribbon核心组件IRule
IRule:根据特定算法中从服务列表中选取一个要访问的服务。
Feign 负载均衡
简介:
Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。
在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。
Spring Cloud Feign帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
Spring Cloud Feign具备可插拔的注解支持,支持Feign注解、JAX-RS注解和Spring MVC的注解。
① 引入Feign 依赖
<!--增加Feign的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
② 创建一个Feign接口,并添加@FeignClient注解,定义Feign客户端,并指定微服务id。
/**
*Feign通过接口的方法调用Rest服务(之前是Ribbon+RestTemplate)
*/
@FeignClient("service-provider")
//@RequestMapping("user") //不建议写在这
public interface UserClient{
@GetMapping("{user/id}")
public User queryUserById(@PathVariable("id")Long id);
}
③ 主启动类添加 @EnableFeignClients注解,启用Feign客户端
Feign集成了Ribbon
利用Ribbon维护了微服务列表,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅的而简单的实现了服务调用。
Hystrix 熔断
简介:
分布式面临的问题:
复杂分布式体系结构复杂的依赖关系,不可避免的存在服务宕机,网络中断的问题。
服务雪崩:
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的服务,这就是所谓的扇出。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的雪崩效应。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源的紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
Hystrix介绍:
Hystrix是Netflix开源的一个延迟和容错库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix解决雪崩问题的手段有两个:
- 线程隔断
- 服务熔断
Hystrix为每个依赖服务调用分配一个小的线程池,如果线程池已满调用将被立即拒绝,默认不采用排队,加速失败判断时间。
用户的请求将不再直接访问服务,而是通过线程池中的空闲线程来访问服务,如果线程池已满,或者请求超时,则会进行降级处理——优先保证核心服务,而非核心服务不可用或弱可用。
用户的请求故障时,不会被立即阻塞,更不会无休止的等待或者看到系统崩溃,至少可以看到一个执行结果(例如返回一个提示信息)。
服务降级虽然会导致请求失败,但是不会导致阻塞,而且最多会影响这个以来服务对应的线程池中的资源,对其他服务没有响应。
触发Hystrix服务降级的情况:
- 线程池已满
- 请求超时
服务降级:
整体资源不够了,忍痛将某些服务先关掉,待度过难关后再开启。
资源的抢占和分配
所谓降级,一般是从整体负荷考虑,当某个服务熔断后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值,这样做,虽然服务水平下降,好歹能用,比直接改掉要强。
服务降级是在客户端(消费者)处理完成的,与服务端没关系。
① 引入Hystrix依赖
<!--Hystrix依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
② 主启动类上添加 @EnableCircuitBreaker 注解开启熔断
//@SpringBootApplication
//@EnableDiscoveryClient
//@EnableCircuitBreaker
@SpringCloudApplication //组合注解,相当于上面三个注解的组合
public class ItcastServiceConsumerApplication {
③ 定义一个局部熔断方法(要和被熔断的方法返回值和参数列表一致)
public String queryUserByIdFallback(Long id){
return "服务正忙,请稍后再试!";
}
④ @HystrixCommand 声明被熔断的方法
一旦服务调用失败并抛出错误信息后,被熔断方法会自动调用@HystrixCommand标注好的fallBackMethod调用类中知道的方法
@GetMapping
@ResponseBody
@HystrixCommand(fallbackMethod="queryUserByIdFallback")
public String queryUserById(Long id){
return this.restTemplate.getForObject("http://service-provider/user/"+id,User.class);
}
⑤ 也可以在类上定义一个全局的熔断方法(返回值类型要和被熔断的方法一致,参数列表必须为空)
@DefaultProperties(defaultFallback = queryUserByIdFallback )
@Controller
public class UserController{
@HystrixCommand //哪个方法需要熔断就加在哪个方法上面
public String queryUserById(Long id){
}
}
设置超时
在之前的案例中,请求在超过1秒后都会返回错误信息,这是因为Hystrix的默认超时时长为1,我们可以通过设置修改这个值:
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds:6000 # 设置hystrix的超时时间为6000ms
服务熔断
熔断原理:
熔断器,也叫断路器,其英文单词为:Circult Breaker。
不同于电路熔断只能断不能自动重连,Hystrix可以实现弹性容错,当情况好转之后,可以自动重连。通过短路的方式,可以将后续请求直接拒绝掉,一段时间之后允许部分请求通过,如果调用成功则回到电路闭合状态,否则继续断开。
熔断机制3个状态:
- Closed:关闭状态,所有请求都正常访问。
- Open:打开状态,所有请求都会被降级。Hystrix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔 断,断路器会完全打开。默认失败的阈值是50%,请求次数最少不低于20次。
- Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5秒)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持断开,再次进行休眠计时。
通过配置修改熔断策略:
circuitBreaker.requestVolumThreshold=20 # 触发熔断的最小请求次数,默认20
circuitBreaker.sleepWindowInMillisseconds=50000 # 休眠时长,默认是5000毫秒
circuitBreaker.errorThresholdPercentage=50 # 出发熔断的失败请求最小占比,默认50%
zuul路由网关
概述:
服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API的过程中,除了具备服务路由、均衡负载功能之外,它还具备了权限控制等功能。Spring Cloud Neflix中的Zuul就担任了这样的一个角色,为微服务架构提供了前门保护的作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务路由层面,使得服务集群主体能够具备更高的可复用性和可测试性。
① 引入Zuul依赖
<!--zuul依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
② yml文件配置
server:
port: 10010
spring:
application:
name: itcast-zuul
eureka:
client:
service-url:
defaultZone:http://localhost:10086/eureka
① 通过路径路由
zuul:
routes:
service-provider: # 路由名称,可以随便写,习惯上写服务名
path:/service-provider/** # 这里是映射路径
url:http://localhost:8082 # 映射路径对应的实际url地址
# 我们将符合path规则的一切请求,都代理到url参数指定的地址。
# 本例中我们将/service-provider/** 开头的请求,代理到http://localhost:8082
② 通过服务id路由
zuul:
routes:
service-provider:/service-provider/**
serviceId:service-provider # 路由到对应serviceId
③ 简化参数
zuul:
routes: # 由于路由名称默认就是服务名,干脆就把它看成服务id,对/service-provider/**的请求路由到service-provider
prefix: /api # 路由前缀,完整访问路径变为 /api/service-provider/**
service-provider:/service-provider/**
④ 不配置,默认就是服务id开头的路径路由到对应服务
③ 主启动类添加注解@EnableZuulProxy,启用Zuul组件
@SpringBootApplication
@EnableZuulProxy
public class ZuulGatewayApp {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApp.class,args);
}
}