SpringCloud微服务开发与实战

news2025/5/17 15:34:28

本节内容带你认识什么是微服务的特点,微服务的拆分,会使用Nacos实现服务治理,会使用OpenFeign实现远程调用(通过黑马商城来带你了解实际开发中微服务项目)

前言:从谷歌搜索指数来看,国内从自2016年底开始,微服务热度突然暴涨

那么:到底什么是微服务?


目录

主要内容

1.认识微服务

1.1.单体架构

1.2.微服务

1.3.SpringCloud

2.微服务拆分

2.1.熟悉黑马商城

2.1.1.登录

2.2.2.搜索商品

2.2.3.购物车

2.2.4.下单

2.2.5.支付

2.2.服务拆分原则

2.2.1.什么时候拆

2.2.2.怎么拆

2.3.拆分购物车、商品服务

2.3.1.商品服务

2.4.服务调用

2.4.1.RestTemplate

2.4.2.远程调用

3.服务注册和发现

3.1.注册中心原理

3.2.Nacos注册中心

3.3.服务注册

3.3.1.添加依赖

3.3.2.配置Nacos

3.3.3.启动服务实例

3.4.服务发现

3.4.1.引入依赖

3.4.2.配置Nacos地址

3.4.3.发现并调用服务

4.OpenFeign

4.1.快速入门

4.1.1.引入依赖

4.1.2.启用OpenFeign

4.1.3.编写OpenFeign客户端

4.1.4.使用FeignClient

4.2.连接池

4.2.1.引入依赖

4.2.2.开启连接池

4.3.最佳实践

4.3.1.思路分析

4.3.2.抽取Feign客户端

4.3.3.扫描包

4.4.日志配置

4.4.1.定义日志级别

4.4.2.配置


主要内容

  • 知道单体架构的特点

  • 知道微服务架构的特点

  • 学会拆分微服务

  • 会使用Nacos实现服务治理

  • 会使用OpenFeign实现远程调用


1.认识微服务

1.1.单体架构

单体架构(monolithic structure):顾名思义,整个项目中所有功能模块都在一个工程中开发;项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单。

当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期小项目都采用这种模式。

但随着项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题

  • 团队协作成本高:团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。

  • 系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。

  • 系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。


1.2.微服务

微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点

  • 单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。

  • 团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超10人

  • 服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响

那么,单体架构存在的问题有没有解决呢?

  • 团队协作成本高?

    • 由于服务拆分,每个服务代码量大大减少,参与开发的后台人员较少,协作成本大大降低

  • 系统发布效率低?

    • 每个服务都是独立部署,当有某个服务有代码变更时,只需要打包部署该服务即可

  • 系统可用性差?

    • 每个服务独立部署,并且做好服务隔离,使用自己的服务器资源,不会影响到其它服务

当然,微服务架构在拆分的过程中,还会面临很多其它问题:

  • 如果出现跨服务的业务该如何处理?

  • 页面请求到底该访问哪个服务?

  • 如何实现各个服务之间的服务隔离?

这些问题,我们在下面会给大家逐一解答。


1.3.SpringCloud

微服务拆分以后碰到的各种问题都有对应的解决方案和微服务组件,而SpringCloud框架可以说是目前Java领域最全面的微服务组件的集合了。

而且SpringCloud依托于SpringBoot的自动装配能力,大大降低了其项目搭建、组件使用的成本。对于没有自研微服务组件能力的中小型企业,使用SpringCloud全家桶来实现微服务开发可以说是最合适的选择!

SpringCloud官网https://b11et3un53m.feishu.cn/wiki/R4Sdwvo8Si4kilkSKfscgQX0niB#N8judeJvGors0yxVEU7cCudwnjp


2.微服务拆分

接下来,我们就一起将黑马商城这个单体项目拆分为微服务项目,并解决其中出现的各种问题。

2.1.熟悉黑马商城

首先,我们需要熟悉黑马商城项目的基本结构:

2.1.1.登录

首先来看一下登录业务流程:

2.2.2.搜索商品

在首页搜索框输入关键字,点击搜索即可进入搜索列表页面:

2.2.3.购物车

在搜索到的商品列表中,点击按钮加入购物车,即可将商品加入购物车:

2.2.4.下单

在购物车页面点击结算按钮,会进入订单结算页面:

点击提交订单,会提交请求到服务端,服务端做3件事情:

  • 创建一个新的订单

  • 扣减商品库存

  • 清理购物车中商品

2.2.5.支付

下单完成后会跳转到支付页面,目前只支持余额支付

在选择余额支付这种方式后,会发起请求到服务端,服务端会立刻创建一个支付流水单,并返回支付流水单号到前端。

当用户输入用户密码,然后点击确认支付时,页面会发送请求到服务端,而服务端会做几件事情:

  • 校验用户密码

  • 扣减余额

  • 修改支付流水状态

  • 修改交易订单状态


2.2.服务拆分原则

服务拆分一定要考虑几个问题:

  • 什么时候拆?

  • 如何拆?

2.2.1.什么时候拆

对于多数小型项目,一般是先采用单体架构,随着用户规模扩大、业务复杂后再逐渐拆分为微服务架构。问题就是后期服务拆分时,可能会遇到代码耦合带来的问题,拆分较为困难(前易后难

对于一些大型项目,在立项之初目的就很明确,为了长远考虑,在架构设计时就直接选择微服务架构。虽然前期投入较多,但后期就少了拆分服务的烦恼前难后易

2.2.2.怎么拆

之前我们说过,微服务拆分时粒度要小,这其实是拆分的目标。

高内聚首先是单一职责,同时保证微服务内部业务的完整性为前提,一旦微服务做到了高内聚,那么服务之间的耦合度自然就降低了。

当然,微服务之间不可避免的会有或多或少的业务交互,比如下单时需要查询商品数据。这个时候我们不能在订单服务直接查询商品数据库,否则就导致了数据耦合。而应该由商品服务对应暴露接口,并且一定要保证微服务对外接口的稳定性(尽量保证接口外观不变)。虽然出现了服务间调用,但此时无论你如何在商品服务做内部修改,都不会影响到订单微服务,服务间的耦合度就降低了。


明确了拆分目标,接下来就是拆分方式了。我们在做服务拆分时一般有两种方式:

  • 纵向拆分

  • 横向拆分

所谓纵向拆分,就是按照项目的功能模块来拆分。例如黑马商城中,就有用户管理功能、订单管理功能、购物车功能、商品管理功能、支付功能等。那么按照功能模块将他们拆分为一个个服务,就属于纵向拆分。这种拆分模式可以尽可能提高服务的内聚性。

横向拆分,是看各个功能模块之间有没有公共的业务部分,如果有将其抽取出来作为通用服务。例如用户登录是需要发送消息通知,记录风控数据,下单时也要发送短信,记录风控数据。因此消息发送、风控数据记录就是通用的业务功能,因此可以将他们分别抽取为公共服务:消息中心服务、风控管理服务。这样可以提高业务的复用性,避免重复开发。同时通用业务一般接口稳定性较强,也不会使服务之间过分耦合。


2.3.拆分购物车、商品服务

一般微服务项目有两种不同的工程结构:

  • 完全解耦:每一个微服务都创建为一个独立的工程,项目完全解耦。

    • 优点:服务之间耦合度低

    • 缺点:每个项目都有自己的独立仓库,管理麻烦

  • Maven聚合:整个项目为一个Project,然后每个微服务是其中的一个Module

    • 优点:项目代码集中,管理和运维方便

    • 缺点:服务之间耦合,编译时间较长

2.3.1.商品服务

引入依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>hmall</artifactId>
        <groupId>com.heima</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>item-service</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

代码如下:

package com.hmall.item;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.hmall.item.mapper")
@SpringBootApplication
public class ItemApplication {
    public static void main(String[] args) {
        SpringApplication.run(ItemApplication.class, args);
    }
}

需要改变的地方

这也是因为ItemMapper的所在包发生了变化,因此这里代码必须修改包路径。

最后,还要导入数据库表。默认的数据库连接的是虚拟机,在你docker数据库执行课前资料提供的SQL文件:(idea自带的数据库)

开启启动类:

接着,启动项目,访问商品微服务的swagger接口文档:http://localhost:8081/doc.html

然后测试其中的根据id批量查询商品这个接口:

2.4.服务调用

在拆分的时候,我们发现一个问题:就是购物车业务中需要查询商品信息,但商品信息查询的逻辑全部迁移到item-service服务,导致我们无法查询。

最终结果就是查询到的购物车数据不完整,因此要想解决这个问题,我们就必须改造其中的代码,把原本本地方法调用,改造成跨微服务的远程调用(RPC,即Remote Produce Call)。

因此,现在查询购物车列表的流程变成了这样:

那么:我们该如何用Java代码发送Http的请求呢?

2.4.1.RestTemplate

Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。

同步客户端执行HTTP请求,在底层HTTP客户端库(如JDK HttpURLConnection、Apache HttpComponents等)上公开一个简单的模板方法API。RestTemplate通过HTTP方法为常见场景提供了模板,此外还提供了支持不太常见情况的通用交换和执行方法。 RestTemplate通常用作共享组件。然而,它的配置不支持并发修改,因此它的配置通常是在启动时准备的。如果需要,您可以在启动时创建多个不同配置的RestTemplate实例。如果这些实例需要共享HTTP客户端资源,它们可以使用相同的底层ClientHttpRequestFactory。 注意:从5.0开始,这个类处于维护模式,只有对更改和错误的小请求才会被接受。请考虑使用webclient,它有更现代的API,支持同步、异步和流场景。

先将RestTemplate注册为一个Bean:

package com.hmall.cart.config;

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

@Configuration
public class RemoteCallConfig {

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

重点:

此处不用@Resource和@Autowired,是为了避免循环依赖

故而可以采用构造方法的方式来使用bean对象

但是,这样代码非常繁琐,故而可以使用@AllArgsConstructor来自动生成构造函数

然而,这样还会出现一个问题,并不是每一个成员变量都需要生成对应的构造方法

因此,可以在需要的成员变量上面添加final关键字,同时加上@RequiredArgsConstructor,使其变为常量

2.4.2.远程调用

完整代码如下:

private void handleCartItems(List<CartVO> vos) {
    // TODO 1.获取商品id
    Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
    // 2.查询商品
    // List<ItemDTO> items = itemService.queryItemByIds(itemIds);
    // 2.1.利用RestTemplate发起http请求,得到http的响应
    ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
            "http://localhost:8081/items?ids={ids}",
            HttpMethod.GET,
            null,
            new ParameterizedTypeReference<List<ItemDTO>>() {
            },
            Map.of("ids", CollUtil.join(itemIds, ","))
    );
    // 2.2.解析响应
    if(!response.getStatusCode().is2xxSuccessful()){
        // 查询失败,直接结束
        return;
    }
    List<ItemDTO> items = response.getBody();
    if (CollUtils.isEmpty(items)) {
        return;
    }
    // 3.转为 id 到 item的map
    Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
    // 4.写入vo
    for (CartVO v : vos) {
        ItemDTO item = itemMap.get(v.getItemId());
        if (item == null) {
            continue;
        }
        v.setNewPrice(item.getPrice());
        v.setStatus(item.getStatus());
        v.setStock(item.getStock());
    }
}

3.服务注册和发现

3.1.注册中心原理

在微服务远程调用的过程中,包括两个角色:

  • 服务提供者:提供接口供其它微服务访问

  • 服务消费者:调用其它微服务提供的接口

在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:

3.2.Nacos注册中心

目前开源的注册中心框架有很多,国内比较常见的有:

  • Eureka:Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用

  • Nacos:Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java应用

  • Consul:HashiCorp公司出品,目前集成在SpringCloud中,不限制微服务语言

以上几种注册中心都遵循SpringCloud中的API规范,因此在业务开发使用上没有太大差异。由于Nacos是国内产品,中文文档比较丰富,而且同时具备配置管理功能(后面会学习),因此在国内使用较多,课堂中我们会Nacos为例来学习。

官方网站如下:

我们基于Docker来部署Nacos的注册中心,首先我们要准备MySQL数据库表,用来存储Nacos的数据。由于是Docker部署,所以大家需要将资料中的SQL文件导入到你Docker中的MySQL容器中:

然后,将课前资料中的nacos目录上传至虚拟机的/root目录。

进入root目录,然后执行下面的docker命令:

docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

执行下面代码

 docker logs -f nacos

启动完成后,访问下面地址:http://192.168.150.101:8848/nacos/,注意将192.168.150.101替换为你自己的虚拟机IP地址。

首次访问会跳转到登录页,账号密码都是nacos

3.3.服务注册

接下来,我们把item-service注册到Nacos,步骤如下:

  • 引入依赖

  • 配置Nacos地址

  • 重启

3.3.1.添加依赖

item-servicepom.xml中添加依赖:

<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3.3.2.配置Nacos

item-serviceapplication.yml中添加nacos地址配置:

spring:
  application:
    name: item-service # 服务名称
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址

3.3.3.启动服务实例

3.4.服务发现

服务的消费者要去nacos订阅服务,这个过程就是服务发现,步骤如下:

  • 引入依赖

  • 配置Nacos地址

  • 发现并调用服务

3.4.1.引入依赖

服务发现除了要引入nacos依赖以外,由于还需要负载均衡,因此要引入SpringCloud提供的LoadBalancer依赖。

我们在cart-service中的pom.xml中添加下面的依赖:

<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

可以发现,这里Nacos的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。

因此,等一会儿cart-service启动,同样会注册到Nacos

3.4.2.配置Nacos地址

cart-serviceapplication.yml中添加nacos地址配置:

spring:
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848

3.4.3.发现并调用服务

接下来,服务调用者cart-service就可以去订阅item-service服务了。不过item-service有多个实例,而真正发起调用时只需要知道一个实例的地址。

因此,服务调用者必须利用负载均衡的算法,从多个实例中挑选一个去访问。常见的负载均衡算法有:

  • 随机

  • 轮询

  • IP的hash

  • 最近最少访问

这里我们可以选择最简单的随机负载均衡。

另外,服务发现需要用到一个工具,DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用:

我们就可以对原来的远程调用做修改了,之前调用时我们需要写死服务提供者的IP和端口:

但现在不需要了,我们通过DiscoveryClient发现服务实例列表,然后通过负载均衡算法,选择一个实例去调用:

4.OpenFeign

在上一章,我们利用Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了:

而且这种调用方式,与原本的本地方法调用差异太大,编程时的体验也不统一,一会儿远程调用,一会儿本地调用。

因此,我们必须想办法改变远程调用的开发模式,让远程调用像本地方法调用一样简单。而这就要用到OpenFeign组件了。

其实远程调用的关键点就在于四个:

  • 请求方式

  • 请求路径

  • 请求参数

  • 返回值类型

所以,OpenFeign就利用SpringMVC的相关注解来声明上述4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。

接下来,我们就通过一个快速入门的案例来体验一下OpenFeign的便捷吧。

4.1.快速入门

我们还是以cart-service中的查询我的购物车为例。因此下面的操作都是在cart-service中进行。

4.1.1.引入依赖

cart-service服务的pom.xml中引入OpenFeign的依赖和loadBalancer依赖:

  <!--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>

4.1.2.启用OpenFeign

接下来,我们在cart-serviceCartApplication启动类上添加注解,启动OpenFeign功能:

4.1.3.编写OpenFeign客户端

cart-service中,定义一个新的接口,编写Feign客户端:

其中代码如下:

package com.hmall.cart.client;

@FeignClient("item-service")
public interface ItemClient {

    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

这里只需要声明接口,无需实现方法。接口中的几个关键信息:

  • @FeignClient("item-service"):声明服务名称

  • @GetMapping:声明请求方式

  • @GetMapping("/items"):声明请求路径

  • @RequestParam("ids") Collection<Long> ids:声明请求参数

  • List<ItemDTO>:返回值类型

有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且发送一个GET请求,携带ids为请求参数,并自动将返回值处理为List<ItemDTO>

我们只需要直接调用这个方法,即可实现远程调用了。

4.1.4.使用FeignClient

最后,我们在cart-servicecom.hmall.cart.service.impl.CartServiceImpl中改造代码,直接调用ItemClient的方法:

修改前

修改之后

feign替我们完成了服务拉取、负载均衡、发送http请求的所有工作,是不是看起来优雅多了。

而且,这里我们不再需要RestTemplate了,还省去了RestTemplate的注册。

4.2.连接池

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

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

  • Apache HttpClient :支持连接池

  • OKHttp:支持连接池

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

4.2.1.引入依赖

cart-servicepom.xml中引入依赖:

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

4.2.2.开启连接池

cart-serviceapplication.yml配置文件中开启Feign的连接池功能:

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

重启服务,连接池就生效了。

4.3.最佳实践

4.3.1.思路分析

相信大家都能想到,避免重复编码的办法就是抽取。不过这里有两种抽取思路:

  • 思路1:抽取到微服务之外的公共module

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

思路1

优点:项目耦合度大幅度降低

缺点:项目结构变得更加复杂

思路2

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

方案2抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。

4.3.2.抽取Feign客户端

由于item-service已经创建好,无法继续拆分,因此这里我们采用方案2

hmall下定义一个新的module,命名为hm-api

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.heima</groupId>
        <artifactId>hmall</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>hm-api</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--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>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.6</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

4.3.3.扫描包

接下来,我们在cart-servicepom.xml中引入hm-api模块:

  <!--feign模块-->
  <dependency>
      <groupId>com.heima</groupId>
      <artifactId>hm-api</artifactId>
      <version>1.0.0</version>
  </dependency>

这里因为ItemClient现在定义到了com.hall.api.client包下,而cart-service的启动类定义在com.hall.cart包下,扫描不到ItemClient,所以报错了。

4.4.日志配置

OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。

4.4.1.定义日志级别

在hm-api模块下新建一个配置类,定义Feign的日志级别:

package com.hmall.api.config;

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

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

4.4.2.配置

接下来,要让日志级别生效,还需要配置这个类。有两种方式:

  • 局部生效:在某个FeignClient中配置,只对当前FeignClient生效

@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
  • 全局生效:在@EnableFeignClients中配置,针对所有FeignClient生效。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2377783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WAS和Tomcat的对比

一、WAS和Tomcat的对比 WebSphere Application Server (WAS) 和 Apache Tomcat 是两款常用的 Java 应用服务器&#xff0c;但它们有许多显著的区别。在企业级应用中&#xff0c;它们扮演不同的角色&#xff0c;各自有其特点和适用场景。以下是它们在多个维度上的详细对比&…

IntelliJ IDEA打开项目后,目录和文件都不显示,只显示pom.xml,怎样可以再显示出来?

检查.idea文件夹 如果项目目录中缺少.idea文件夹&#xff0c;可能导致项目结构无法正确加载。可以尝试删除项目根目录下的.idea文件夹&#xff0c;然后重新打开项目&#xff0c;IDEA会自动生成新的.idea文件夹和相关配置文件&#xff0c;从而恢复项目结构。 问题解决&#xff0…

Hot100-链表-JS

160.相交链表 160. 相交链表 已解答 简单 相关标签 相关企业 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整…

事件驱动架构:从传统服务到实时响应的IT新风潮

文章目录 事件驱动架构的本质&#xff1a;从请求到事件的范式转变在EDA中&#xff1a; 事件驱动架构的演进&#xff1a;从消息队列到云原生标配核心技术&#xff1a;事件驱动架构的基石与工具链1. 消息队列&#xff1a;事件传递的枢纽2. 消费者&#xff1a;异步处理3. 事件总线…

网络流量分析 | NetworkMiner

介绍 NetworkMiner 是一款适用于Windows&#xff08;也适用于Linux/Mac&#xff09;的开源网络取证分析工具。它可被用作被动网络嗅探器/数据包捕获工具&#xff0c;也可被用于检测操作系统、会话、主机名、开放端口等&#xff0c;还能被用于解析pcap文件进行离线分析。点击此…

深入理解 Git 分支操作的底层原理

在软件开发的世界里&#xff0c;Git 已经成为了版本控制的标配工具。而 Git 分支功能&#xff0c;更是极大地提升了团队协作和项目开发的效率。我们在日常开发中频繁地创建、切换和合并分支&#xff0c;但是这些操作背后的底层原理是怎样的呢&#xff1f;在之前的博客探秘Git底…

Excel MCP: 自动读取、提炼、分析Excel数据并生成可视化图表和分析报告

最近&#xff0c;一款Excel MCP Server的开源工具火了&#xff0c;看起来功能很强大&#xff0c;咱们今天来一探究竟。 基础环境 最近两年&#xff0c;大家都可以看到AI的发展有多快&#xff0c;我国超10亿参数的大模型&#xff0c;在短短一年之内&#xff0c;已经超过了100个&…

C语言:深入理解指针(4)

目录 一、字符指针变量 二、数组指针变量 三、二维数组传参的本质 四、函数指针变量 五、typedef 类型重命名 六、函数指针数组 一、字符指针变量 我们常见的字符指针变量是这样的&#xff1a; char a w; char* p &a; char arr[] "abcd"; char* pa ar…

【更新】全国省市县-公开手机基站数据集(2006-2025.3)

手机基站是现代通信网络中的重要组成部分&#xff0c;它们为广泛的通信服务提供基础设施。随着数字化进程的不断推进&#xff0c;手机基站的建设与布局对优化网络质量和提升通信服务水平起着至关重要的作用&#xff0c;本分享数据可帮助分析移动通信网络的发展和优化。本次数据…

基于MNIST数据集的手写数字识别(CNN)

目录 一&#xff0c;模型训练 1.1 数据集介绍 1.2 CNN模型层结构 1.3 定义CNN模型 1.4 神经网络的前向传播过程 1.5 数据预处理 1.6 加载数据 1.7 初始化 1.8 模型训练过程 1.9 保存模型 二&#xff0c;模型测试 2.1 定义与训练时相同的CNN模型架构 2.2 图像的预处…

idea Maven 打包SpringBoot可执行的jar包

背景&#xff1a;当我们需要坐联调测试的时候&#xff0c;需要对接前端同事&#xff0c;则需要打包成jar包直接运行启动服务 需要将项目中的pom文件增加如下代码配置&#xff1a; <build><plugins><plugin><groupId>org.springframework.boot</gr…

HarmonyOs开发之——— ArkWeb 实战指南

HarmonyOs开发之——— ArkWeb 实战指南 谢谢关注!! 前言:上一篇文章主要介绍HarmonyOs开发之———合理使用动画与转场:CSDN 博客链接 一、ArkWeb 组件基础与生命周期管理 1.1 Web 组件核心能力概述 ArkWeb 的Web组件支持加载本地或在线网页,提供完整的生命周期回调体…

国标GB/T 12536-90滑行试验全解析:纯电动轻卡行驶阻力模型参数精准标定

摘要 本文以国标GB/T 12536-90为核心框架&#xff0c;深度解析纯电动轻卡滑行试验的完整流程与数据建模方法&#xff0c;提供&#xff1a; 法规级试验规范&#xff1a;从环境要求到数据采集全流程详解行驶阻力模型精准标定&#xff1a;最小二乘法求解 ( FAv^2BvC ) 的MATLAB实…

初识——QT

QT安装方法 一、项目创建流程 创建项目 入口&#xff1a;通过Qt Creator的欢迎页面或菜单栏&#xff08;文件→新建项目&#xff09;创建新项目。 项目类型&#xff1a;选择「Qt Widgets Application」。 路径要求&#xff1a;项目路径需为纯英文且不含特殊字符。 构建系统…

kkFileView文件文档在线预览镜像分享

kkFileView为文件文档在线预览解决方案&#xff0c;该项目使用流行的spring boot搭建&#xff0c;易上手和部署&#xff0c;基本支持主流办公文档的在线预览&#xff0c;如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等 开源项目地址 https://gitee.com/kek…

实例分割AI数据标注 ISAT自动标注工具使用方法

文章目录 🌕ISAT安装和启动方法🌕下载和使用AI分割模型🌙SAM模型性能排行🌙手动下载sam模型 & sam模型下载路径🌕使用方法🌙从file中导入图片🌙点击左上角的图标进入分割模式🌙鼠标左键点击画面中的人则自动标注🌙点击右键该区域不标注🌙一个人一个人的…

Qt图表绘制(QtCharts)- 性能优化(13)

文章目录 1 批量替换代替追加1.1 测试11.2 测试21.3 测试3 2 开启OpenGL2.1 测试12.2 测试22.3 测试32.4 测试4 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;Qt开发 &#x1f448;&#x1f449;QtCharts绘图 &#x1f448;&#x1f449;python开发 &#x1f…

如何在 Windows 10 或 11 上使用命令提示符安装 PHP

我们可以在 Windows 上从其官方网站下载并安装 PHP 的可执行文件,但使用命令提示符或 PowerShell 更方便。 PHP 并不是一种新的或不为人知的脚本语言,它已经存在并被全球数千名网络开发人员使用。它以开源许可并分发,广泛用于 LAMP 堆栈中。然而,与 Linux 相比,它在 Wind…

RK3588 ADB使用

安卓adb操作介绍 adb&#xff08;Android Debug Bridge&#xff09;是一个用于与安卓设备进行通信和控制的工具。adb可以通过USB或无线网络连接安卓设备&#xff0c;执行各种命令&#xff0c;如安装和卸载应用&#xff0c;传输文件&#xff0c;查看日志&#xff0c;运行shell命…

Please install it with pip install onnxruntime

无论怎么安装都是 Please install it with pip install onnxruntime 我python 版本是3.11 &#xff0c;我换成3.10 解决了