当前位置: 首页 > article >正文

SpringCloud 运用(2)—— 跨服务调度

上一篇:SpringCloud 入门(1)—— nacos 注册中心-CSDN博客


1.RestTemplate 跨服务请求

RestTemplate 是 Spring 框架中的一个同步客户端,用于与 HTTP 服务进行交互。它简化了与 HTTP 服务器通信的过程,并且提供了对多种 HTTP 方法(如 GET、POST、PUT、DELETE 等)的支持,用于发送跨服务请求。

  <!--负载均衡器-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  </dependency>

 1.1 配置Bean

在 Spring Boot 2.0 及以上版本中,RestTemplate 不再自动配置,因此需要自己创建 RestTemplate Bean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

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

 1.2 构造函数注入RestTemplate

spring不推荐使用@AutoWired注解,进行自动注入。我们可以自己书写构造函数注入

public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {

     private  RestTemplate restTemplate;

    public CartServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
}

 也可以通过lombok注解,自动生成构造函数,进行注入

 @AllArgsConstructor全参构造注解,但使用该注解可能导致一些不需要通过构造传参的变量,也会生成构造函数

@RequiredArgsConstructor注解,只有通过final修饰值,才会生成构造函数。 因为通过final修饰后,必需在定义时进行赋初始值,或者通过构造函数初始化

@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {

    private final RestTemplate restTemplate;
}

1.3 RestTemplate的使用

ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
        "http://localhost:8081/items?ids={ids}",//请求路径
        HttpMethod.GET,//请求方式
        null,//请求实体
      new ParameterizedTypeReference<List<ItemDTO>>() { },//返回值类型List<ItemDTO>
        Map.of("ids", CollUtil.join(itemIds, ","))//请求参数, 
);
//CollUtil.join(itemIds, ",")将集合转换成字符串,用逗号分隔。即集合123转化为字符串1,2,3。
​​​​​​​//Map.of() "ids"是键,字符串1,2,3是值

 我们可以看到http://localhost:8081/items中存在硬编码,这里我们可以使用上一篇学习到的nacos服务注册中心,将该微服务注册到nacos中,然后通过服务名发送请求。

当你通过 RestTemplate 发起请求时,Spring Cloud 提供了客户端负载均衡机制来决定具体发送到哪台计算机。默认的负载均衡策略是轮询(Round Robin)

这样如果该微服务在多台计算机都进行部署,并在nacos注册后,就可以实现负载均衡了

nacos注册中心地址教程:SpringCloud 入门(1)—— nacos 注册中心-CSDN博客

注册中心搭建完成后,使用构造函数将注入

@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {

    private final RestTemplate restTemplate;
    private  final DiscoveryClient discoveryClient;//注册中心
}

 默认情况下,采用轮询的方式进行负载均衡

// 发起请求时,直接使用服务名称
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
        "http://item-service/items?ids={ids}", // 使用服务名称而不是具体实例 URI
        HttpMethod.GET,
        null,
        new ParameterizedTypeReference<List<ItemDTO>>() {},
        Map.of("ids", CollUtil.join(itemIds, ","))
);

 指定服务实例方式为随机

 // 查找 item-service 服务的实例列表
    List<ServiceInstance> instances = discoveryClient.getInstances("item-service");

    if (instances.isEmpty()) {
        throw new RuntimeException("No instances available for item-service");
    }

    // 随机选择一个服务实例
    Random random = new Random();
    ServiceInstance instance = instances.get(random.nextInt(instances.size()));

    // 发起请求,查询商品
    ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
            instance.getUri() + "/items?ids={ids}",
            HttpMethod.GET,
            null,
            new ParameterizedTypeReference<List<ItemDTO>>() {},
            Map.of("ids", CollUtil.join(itemIds, ","))
    );

利用Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了,下面介绍一款更简单的方法OpenFeign。

2.OpenFeign 跨服务请求

OpenFeign 是一个声明式的 Web 服务客户端,它使得编写 HTTP 客户端变得更加简单。它是 Netflix Feign 的增强版本,并且与 Spring Cloud 深度集成,允许开发者通过创建接口并用注解描述 HTTP 请求来定义和使用 RESTful 客户端。这简化了代码的编写,因为你不需要构建 URL、手动解析 JSON 或处理其他繁琐的任务,对跨服务请求进行简化了。

2.1 设计思路

为了避免重复编码,下面有两种抽取思路:

  • 思路1:抽取到微服务之外的公共module(适用与聚合工程)

  • 思路2:每个微服务自己抽取一个module

如图:

方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。(适用于maven聚合模块中使用)

方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。

 下面我们采用第一个思路,新建一名模板api模板,单独存放openFeign请求

 2.2 导入依赖

  <!--openFeign-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>
  <!--负载均衡器-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  </dependency>

2.3 编写Feign客户端

import com.heima.cart.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Collection;
import java.util.List;

@FeignClient("item-service") //远程请求的服务
public interface ItemClient {

    @GetMapping("/items")//请求的服务路径
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

 2.4 启动OpenFeign功能

在需要发送跨服务请求(即:需要用到openFeign功能模块)的微服务的pom.xml中添加hm-api模块,

在启动类上添加注解@EnableFeignClients,开启openFeign功能 ,并且指明所在客户端的位置(类)

  • 方式1:声明扫描包:

  • 方式2:声明要用的API客户端

  将客户端注入,发起请求

 //注入
 private final ItemClient itemClient;


//发起请求
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);

2.5 openFeign日志

默认情况下,openFeign请求中,后台是没有日志的,一旦出错,很难发现。

需要手动创建config包,配置日志类

import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }
}

 在启动类中,开启日志,

全局生效:在@EnableFeignClients中配置,针对所有FeignClient生效。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

2.6 openFeign请求头

利用openFeign发送请求时,需要携带当前发起请求的用户信息。

这里我们将用户id放到请求头中,转发给另一个微服务。前端对后端发起的请求,交给网关处理,网关负责对jwt进行解析验证。网关验证完成后,才会转交给其他微服务。

后续更新网关处理方案.....

import com.hmall.common.utils.UserContext;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }
    @Bean
    public RequestInterceptor userInfoRequestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                // 获取登录用户
                Long userId = UserContext.getUser();
                if(userId == null) {
                    // 如果为空则直接跳过
                    return;
                }
                // 如果不为空则放入请求头中,传递给下游微服务
                template.header("user-info", userId.toString());
            }
        };
    }
}

2.7 openFeign连接池

Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现

  • HttpURLConnection:默认实现,不支持连接池

  • Apache HttpClient :支持连接池

  • OKHttp:支持连接池

因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http.

导入OKHttp依赖

<!--OK http 的依赖 -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>

application.yml配置文件中开启Feign的连接池功能,重启服务,连接池就生效了。

feign:
  okhttp:
    enabled: true # 开启OKHttp功能

下一篇

SpringCloud 入门(3)—— Nacos配置中心-CSDN博客


http://www.kler.cn/a/449560.html

相关文章:

  • 解决 Docker 中 DataLoader 多进程错误:共享内存不足
  • v3s点RGB屏 40pin 800x480,不一样的点屏,不通过chosen。
  • 【AI日记】24.12.24 kaggle 比赛 2-12
  • Linux快速入门-Linux文件系统管理
  • 递归查询全量分页数据问题
  • 玩转OCR | 探索腾讯云智能结构化识别新境界
  • Conda 使用全解析:从入门到精通
  • JavaWeb Servlet的反射优化、Dispatcher优化、视图(重定向)优化、方法参数值获取优化
  • Qt for Python (PySide6)设置程序图标和任务栏图标
  • 【求职面试】大学转专业面试自我介绍模板7篇
  • 解决:websocket 1002 connection rejected 426upgrade required
  • 路径规划之启发式算法之二十:麻雀搜索算法(Sparrow Search Algorithm,SSA)
  • 搭建简易版本的git私有仓库--运用git和gitea
  • 灭屏情况下,飞行模式+静音模式+插耳,播放音乐,电流异常
  • 层序遍历练习
  • 重温设计模式--组合模式
  • 【FFmpeg】解封装 ① ( 封装与解封装流程 | 解封装函数简介 | 查找码流标号和码流参数信息 | 使用 MediaInfo 分析视频文件 )
  • 终章:DevOps实践总结报告
  • 鸿蒙人脸识别
  • RISC-V架构的压缩指令集介绍
  • 【Quartz】任务调度
  • Qt C++ 下网络通信与文件发送的实现
  • 黑马商城项目—服务注册、服务发现
  • C++ STL CookBook
  • 拥有人类情感的AI:未来还是幻想?
  • 蓝桥杯刷题——day9