微服务将不同功能模块拆分成多个不同的服务,在业务逻辑集成时候,难免会有一个服务需要依赖调用另一个服务的情况。如订单服务需要通过用户服务查询用户相关信息,这时候微服务之间就需要进行跨服务调用。

要想进行跨服务调用,服务之间需要一种能够找到彼此的方式。这个时候就是通过前面文章介绍的注册中心。服务实例启动后会将自己的网络位置(主机和端口)注册到服务发现服务器上(注册中心),而其他服务可以通过注册中心找到它们需要调用的可用服务实例列表。服务的注册需要在启动类上添加@EnableDiscoveryClient注解来完成,这里以nacos作为注册中心不在过多赘述,前面文章已经说过。

当一个服务的多个实例运行时,你需要将流量分配到这些实例上,以确保高可用性并防止任何单个实例过载。Spring Cloud LoadBalancer提供了客户端负载均衡的功能。

Spring 提供了 RestTemplate(阻塞式)和 WebClient(非阻塞式、响应式)用于发起 HTTP 调用。当与 Spring Cloud LoadBalancer 结合使用时,这些客户端可以使用服务名称(而不是硬编码的 URL),负载均衡器会自动将服务名称解析为可用的实例。

假设这里已经有一个系统服务,注册服务名称为:system-service,有一个接口 /sys/params 可以获取系统配置参数,另一个服务需要跨服务调用该接口

RestTemplate方式

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @GetMapping("/call-service")
    public String callService() {
        String response = restTemplate.restTemplate.getForObject("http://system-service/sys/params", String.class);
        return "来自服务的响应: " + response;
    }

使用RestTemplate跨服务调用,RestTemplate上的@LoadBalanced 注解启用了客户端负载均衡,需要添加LoadBalance依赖spring-cloud-starter-loadbalancer,http://服务名/*** 服务名会自动替换成对应的注册的服务

WebClient方式

    @Bean
    @LoadBalanced
    public WebClient webClient(){
        return WebClient.create();
    }
    /**
    bodyValue()方法可以设置请求body,header()方法可以设置请求头
    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
    */
    @GetMapping("/call-service")
    public String callService() {
        Mono<String> monos = webClient.post().uri("http://system-service/sys/params").retrieve().bodyToMono(String.class);
        return "来自服务的响应: " + response;
    }

WebClient是Spring 5 中引入的非阻塞、响应式 HTTP 客户端,属于 Spring WebFlux 框架的一部分。使用WebClient跨服务调用,除了依赖loadbalance,还需要添加依赖:spring-boot-starter-webflux用来支持非阻塞方式调用。WebClient采用函数式、流式 (Fluent) 的 API 来构建 HTTP 请求。基于 Reactor 库实现,非常适合构建高并发、响应式的应用。

但是WebClient不是声明式的。需要使用其 API 显式地构建请求的各个部分(URI, method, headers, body 等)。

Feign

feign是声明式的 HTTP 客户端绑定器。它的目标是让编写 Web 服务客户端更加简单。通过接口和注解来定义 HTTP 客户端。你只需要声明你需要调用的远程 API 接口,并使用注解(如 @GetMapping, @PostMapping, @RequestParam, @PathVariable 等,与 Spring MVC 的注解类似)来描述请求的细节。Feign 会在运行时动态地生成实现代码。你只需要定义接口,Feign 会帮你处理底层的 HTTP 通信。

首先需要引入依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

然后在启动类上添加@EnableFeignClients注解开启openfeign支持。

下一步需要定义跨服务调用的接口,跨服务调用的接口需要使用@FeignClient注解修饰。

如下:

@FeignClient(value = "system-service",qualifiers = "systemClient")
public interface SystemClient {

    @RequestMapping("/sys/params")
    public String getSysParams();
}

接口定义包路径要@EnableFeignClients注解能够扫描到,可以通过@EnableFeignClients(basePackage=“”)来指定扫描的基础包路径。

@FeignClient 注解默认生成bean的name是当前接口的全路径名,可以从spring容器中获取。注解的value值是可用服务名,要和注册中心注册的服务名保持一致,这样会创建一个loadbalance的服务进行调用,所以这里如果要调用注册中心注册的微服务,一个是要添加注册中心discovery,另一个是添加依赖starter-loadbalance。

loadbalance依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

@FeignClient 修饰的接口类,其接口方法可以通过spring mvc的@RequestMapping注解进行修饰,用来与被调用的服务地址进行匹配对应。如上面定义的接口方法@RequestMapping(“/sys/params”)会和@FeignClient指定的服务名组合。调用SystemClient.getSysParams()方法相当于调用:lb://system-service/sys/params服务。开始也说了@FeignClient标注的同时也会生成一个spring的bean,在使用的地方进行自动注入即可。

@FeignClient还可以指定url属性,如果指定了url属性(服务接口地址),则可以直接调用注册中心以外的服务,不需要在注册中心注册该服务。

熔断降级:

当调用后端服务异常失败时,可以通过在@FeignClient 指定callback进行处理。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐