Spring Cloud的5大组件
Eureka:注册中心
Ribbon:负载均衡
Feign:远程调用
Hystrix:服务熔断
Zuul/Gateway:网关
特别地,在Spring Cloud Alibaba中
Nacos:注册中心/配置中心
Ribbon:负载均衡
Feign:服务调用
Sentinel:服务保护
Gateway:服务网关
1、注册中心
注册中心的核心作用:服务注册和发现
常见的注册中心有:Eureka、Nocas、Zookeeper
1.1、Eureka
Eureka是Netflix开发的一个用于实现服务注册和发现的服务。Eureka主要由两部分组成:Eureka服务器和Eureka客户端。
(1)服务注册
服务方需要把自己的信息注册到eureka,由eureka来保持这些信息(如服务名称、IP、端口等)。
(2)服务发现
消费者向eureka拉取服务列表信息,如果服务方有集群,则消费方会利用负载均衡算法,选择一个发起调用。
(3)服务监控
服务方会每隔30秒向eureka发送心跳,报告健康状态,如果eureka服务90秒没收到心跳,从eureka中剔除。
1.2、Nacos
Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它支持多种服务治理能力,包括服务注册与发现、动态配置管理、动态DNS服务等。
(1)注册服务
Nacos作为注册中心,接收客户端(服务实例)发起的注册请求,并将注册信息存放到注册中心进行管理。注册请求的处理包括客户端组装注册请求、随机选择集群中的一个Nacos节点发起注册、实现负载均衡、路由转发、处理请求、保证数据最终一致性等步骤。
(2)服务发现
Nacos通过维护服务实例的列表,使得服务消费者可以通过Nacos查询到可用服务的列表,进而进行服务调用。
(3)临时实例与永久实例
Nacos中区分了临时实例和永久实例。
临时实例在注册到注册中心后仅保存在服务端内部缓存中,不会持久化到磁盘。当服务实例异常或下线时,会从服务注册表中剔除。
而永久实例不仅存在于服务注册表中,还会被持久化到磁盘文件中。即使服务实例异常或下线,Nacos也不会将其从服务注册表中剔除,而是将其健康状态设置为不健康。
1.3、nacos和eureka的区别
在都作为注册中心的前提下
(1)共同点
1)都支持服务注册和服务拉取
2)都支持服务方心跳方式做健康检测
(2)不同点
1)nacos支持“服务端”主动检测“服务方”状态:临时实例采用心跳模式,非临时实例采用主动检测模式
2)临时实例心跳不正常会被剔除,非临时实例则不会被剔除
3)nacos支持服务列表变更的消息推送模式,服务列表更新更及时
4)nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;eureka采用AP方式
AP模式(Availability Priority Mode)高可用性
CP模式(Consistency Priority Mode)一致性
特别地,nacos还支持了配置中心,eureka则只有注册中心。
2、负载均衡
负载均衡ribbon,发起远程调用feign
2.1、Ribbon负载均衡策略有哪些
RoundRobinRule:简单轮询服务列表来选择服务器
WeightedResponseTimeRule:按照权重来选择服务器,响应时间越长,权重越小
RandomRule:随机选择一个可用的服务器
BestAvailableRunle:忽略那些短路的服务器,并选择并发数较低的服务器
RetryRule:重试机制的选择逻辑
AvailabilityFilteringRule:可用性敏感策略,先过滤非健康的,再选择连接数较小的实例
ZoneAvoidanceRule:以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后对Zone内的多个服务做轮询。
2.2、自定义负载均衡策略的实现
自定义负载均衡策略的实现,有两种方式
方式一:创建类实现IRule接口,可以指定负载均衡策略(全局)
方式二:在客户端的配置文件中,可以配置某一个服务调用的负载均衡策略(局部)
2.3、示例代码
自定义负载均衡策略可以通过实现com.netflix.loadbalancer.IRule
接口来完成。
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomLoadBalancerConfiguration {
@Bean
public IRule randomRule() {
return new RandomRule(); // 自定义的轮询策略
}
}
class RandomRule extends RoundRobinRule {
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// 初始化逻辑,如果需要的话
}
@Override
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
// 随机从服务器列表中选择一个
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int index = new Random().nextInt(serverCount);
server = upList.get(index);
if (server == null) {
Thread.yield();
}
}
return server;
}
}
在这个示例中,我们定义了一个RandomRule类,它继承自RoundRobinRule(轮询策略),并覆盖了choose方法,使其实现随机选择服务器的逻辑。这个自定义策略可以替代默认的轮询策略。通过将RandomRule作为一个Bean注册到Spring上下文中,它将会被应用到使用Ribbon进行负载均衡的服务调用中。
3、远程调用
3.1、Feign
Feign是一个声明式的Web服务客户端,用来简化HTTP远程调用。在Spring Cloud中,Feign可以用来封装HTTP调用的接口,使得调用远程服务就像调用本地方法一样简单。通过创建一个接口并注解,Feign可以生成实现该接口的动态代理,从而允许调用其他服务。
(1)Feign支持HTTP的各种方法
包括GET、POST、PUT、DELETE和PATCH等。通过使用@RequestMapping注解及其变种(如@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping)来指定HTTP方法。
(2)如何配置Feign的超时时间
可以通过在Feign客户端的配置文件中设置超时时间来配置Feign的超时时间。例如,将连接超时时间和读取超时时间都设置为5000毫秒(5秒),可以在application.properties或application.yml文件中进行如下配置:
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000
这将确保如果请求在5秒内没有连接成功或没有返回结果,Feign将会超时。
(3)Feign如何处理服务降级
Feign可以通过集成Hystrix来实现服务降级。要启用服务降级,可以执行以下步骤:首先,添加Hystrix依赖到项目中;其次,在Feign客户端的方法上使用@HystrixCommand注解;最后,配置Hystrix的属性以实现降级逻辑,如设置回退方法等。
(4)Feign与Ribbon的关系
Feign与Ribbon结合使用可以实现客户端负载均衡。Feign本身不直接支持负载均衡策略,而是通过集成Ribbon来实现。因此,Feign可以使用Ribbon支持的各种负载均衡策略,包括轮询、随机、权重、最佳可用等。要配置Feign使用特定的负载均衡策略,可以在Feign客户端的配置文件中设置Ribbon的负载均衡策略
3.2、示例代码-Feign
(1)添加依赖(pom.xml)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2)启用Feign客户端(在启动类上添加@EnableFeignClients注解)
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringCloudApplication
@EnableFeignClients
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
(3)创建Feign客户端接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "remote-service", url = "http://localhost:8080")
public interface RemoteServiceClient {
@GetMapping("/data/{id}")
String getData(@PathVariable("id") Long id);
}
(4)使用Feign客户端进行调用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@Autowired
private RemoteServiceClient remoteServiceClient;
@GetMapping("/localData/{id}")
public String getLocalData(@PathVariable("id") Long id) {
return remoteServiceClient.getData(id);
}
}
RemoteServiceClient是一个Feign客户端接口,用来定义远程服务remote-service的调用方法。在MyController中,我们通过注入RemoteServiceClient来进行远程调用,并将结果返回给客户端。
注意:上述代码中的url属性是可选的,如果需要调用多个URL,可以使用@FeignClient注解的contextId属性,然后在application.properties或application.yml中配置不同的URL。
4、服务熔断
(1)服务雪崩
一个服务失败,导致整条链路的服务都失败的情形
(2)服务降级
服务自我保护的一种方式,或者保护下游服务的一种方式,用于确保服务不会受请求突多影响变得不可用,确保服务不会崩溃,一般在实际开发中与feign接口整合,编写降级逻辑。
(3)服务熔断
默认关闭,需要手动打开,如果检测到10秒内接口失败率超过了50%,就触发熔断机制。之后每隔5秒重新尝试请求微服务,如果微服务不能响应,继续走熔断机制。如果微服务可达,则关闭熔断机制,恢复正常请求。
4.1、sentinel和hystrix区别
Sentinel和Hystrix都是用于微服务架构中的熔断降级框架,但它们在设计理念、功能实现和应用场景上存在显著差异。具体如下:
隔离策略和动态调节: Sentinel提供了基于线程池和信号量的隔离方式,并且能够根据系统负载情况动态调整资源的并发度,这使得它更加灵活且适应性强。相比之下,Hystrix主要采用线程池隔离,虽然也支持信号量隔离,但在动态调节方面相对静态,需要通过配置进行调整。
功能和适用场景: Sentinel不仅提供熔断降级功能,还包括流量控制、实时监控和动态规则配置等,使其适用于需要流量控制和系统负载保护的复杂场景。Hystrix则专注于熔断和降级功能,更适用于需要快速响应和高并发控制的场景。
实时指标统计: 两者都基于滑动窗口进行实时指标统计,但Sentinel的默认实现是基于LeapArray的高性能滑动窗口,而Hystrix在1.5版本后对实时指标统计的实现进行了重构,采用了基于RxJava的事件驱动模式。
总结来说,选择Sentinel还是Hystrix应根据项目的具体需求和技术栈来决定,如果项目需要高度的灵活性和动态调节能力,以及流量控制功能,Sentinel可能是更好的选择;如果项目主要关注快速失败和资源隔离,以及对配置的精细控制,Hystrix可能更适合。
4.2、示例代码-Sentinel
(1)添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
(2)配置Sentinel控制台地址,在application.properties或application.yml中添加:
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.transport.port=8719
(3)启动Sentinel控制台
(4)在服务中添加服务熔断的逻辑。例如,使用@SentinelResource注解标记需要进行服务熔断的方法:
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
@SentinelResource(value = "test", blockHandler = "handleException")
public String test() {
return "Test";
}
public String handleException(BlockException ex) {
return "Service is blocked, please try again later.";
}
}
上述,当服务熔断触发时,Sentinel会调用handleException
方法来处理请求,并返回一个错误消息。
4.3、示例代码-Hystrix
在Spring Cloud中,Hystrix被整合到了Spring Cloud Netflix中,通过使用@HystrixCommand
注解,可以为远程服务调用(如使用Ribbon的服务调用)定义熔断逻辑。
一个简单的使用Hystrix服务熔断的例子:
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallbackMethod")
public String getHelloMessage() {
return restTemplate.getForObject("http://HELLO-SERVICE/hello", String.class);
}
public String fallbackMethod() {
return "Hello Service is not available";
}
}
上述,getHelloMessage
方法调用了HELLO-SERVICE
服务的/hello
端点。如果该服务不可用,Hystrix会执行定义的回退方法fallbackMethod
,而不是抛出异常或导致线程阻塞。这样可以保证调用服务的客户端即使服务不可用,也会收到一个响应,而不是等待或者产生更多的级联错误。
5、网关
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
5.1、示例代码
Spring Cloud 的网关组件是 Spring Cloud Gateway。以下是一个简单的 Spring Cloud Gateway 示例配置,它使用了 Netty 作为底层通信框架,并且通过路由配置来转发请求。
(1)在 pom.xml
中添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 如果想使用 consul 作为路由配置的源,还需要添加 consul 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
(2)配置 application.yml
spring:
cloud:
gateway:
routes:
- id: my_route
uri: http://localhost:8081
predicates:
- Path=/myservice/**
- id: my_route2
uri: http://localhost:8082
predicates:
- Path=/myservice2/**
在这个配置中,我们定义了两条路由规则:
-
当请求路径匹配
/myservice/**
时,转发到http://localhost:8081
-
当请求路径匹配
/myservice2/**
时,转发到http://localhost:8082
(3)启动类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
这样就配置了一个简单的 Spring Cloud Gateway,它能够根据配置的路由规则转发请求。如果需要更复杂的路由逻辑,可以通过编写 Predicate 和 Filter 来实现。