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 来实现。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐