SpringCloud系列教程:微服务的未来(十二)OpenFeign连接池、最佳实践、日志、微服务拆分

本篇博客将讨论如何优化 OpenFeign 的连接池配置,如何使用最佳实践提升服务间通信的效率和可维护性,并探讨如何通过拆分服务来提升微服务架构的灵活性和可扩展性,具体涵盖了用户服务和交易服务的拆分。

目录

前言

OpenFeign

连接池

最佳实践

日志

微服务拆分

用户服务拆分

交易服务

总结


前言

在微服务架构中,服务间的通信通常依赖于 HTTP 协议,而 OpenFeign 是一种广泛使用的声明式 HTTP 客户端,简化了微服务之间的调用。然而,随着微服务数量的增加,OpenFeign 的性能和稳定性问题逐渐显现,尤其是在高并发的场景下。为了提升 OpenFeign 的性能,合理配置连接池和日志策略显得尤为重要。

本篇博客将讨论如何优化 OpenFeign 的连接池配置,如何使用最佳实践提升服务间通信的效率和可维护性,并探讨如何通过拆分服务来提升微服务架构的灵活性和可扩展性,具体涵盖了用户服务和交易服务的拆分。


OpenFeign

连接池

OpenFeign对Http请求做了优雅的伪装,不过其底层发起http请求,依赖于其它的框架。这些框架可以自己选择,包括以下三种:

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

具体源码可以参考FeignBlockingLoadBalancerClient类中的delegate成员变量。

OpenFeign整合OKHttp步骤如下:

(1)引入依赖

java 复制代码
        <!--OK http的依赖-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

(2)开启连接池功能

java 复制代码
feign:
 okhttp:
  enabled:tiue#开启0KHttp连接池支持

在cart-service模块中添加依赖

在cart-service模块的application.yaml文件中开启连接池功能

最佳实践

因为order-service订单微服务中也需要根据ids批量查询商品信息,和cart-service服务中的查询商品信息的功能一样,因此要避免重复编码。

创建hm-api模块

pom.xml导入依赖

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>11</maven.compiler.source>
        <maven.compiler.target>11</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>

将ItemClient接口和ItemDTO类导入hm-api模块

然后再将hm-api服务导入cart-service服务

此时运行的话报错。Parameter 0 of constructor in com.hmall.cart.service.impl.CartServiceImpl required a bean of type 'com.hmall.api.client.ItemClient' that could not be found.

CartApplication只能扫描com.hmall.cart下面的包,然而ItemClient在com.hmall.api的包下面。

当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:

方式一:指定FeignClient所在包

XML 复制代码
@EnabieFeignclients(basePackages ="com.hmall.api.clients")

方式二:指定FeignClient字节码

XML 复制代码
@EnableFeignClients(clients={Userclient.class})

日志

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

  • ONONE:不记录代码任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

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

要自定义日志级别需要声明一个类型为Logger.Level的Bean,在其中定义日志级别:

java 复制代码
public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){return Logger.Level.FULL;}

}

但此时这个Bean并未生效,要想配置某个FeignClient的日志,可以在@FeignClient注解中声明:

java 复制代码
@FeignClient(value = "item-service",configuration = DefaultFeignConfig.class)

如果想要全局配置,让所有FeignClient都按照这个日志配置,则需要在@EnableFeignClients注解中声明:

java 复制代码
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

编写配置类DefaultFeignConfig

java 复制代码
package com.hmall.api.config;

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



public class DefaultFeignConfig {

    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

在cart-service服务的启动类上加全局配置注解

如何利用OpenFeign实现远程调用?

  • 引入OpenFeign和SpringCloudLoadBalancer依赖
  • 利用@EnableFeignClients注解开启OpenFeign功能
  • 编写FeignClient

如何配置OpenFeign的连接池?

  • 引入http客户端依赖,例如OKHttp、HttpClient
  • 配置yaml文件,打开OpenFeign连接池开关

OpenFeign使用的最佳实践方式是什么?

  • 由服务提供者编写独立module,将FeignClient及DTO抽取

如何配置OpenFeign输出日志的级别?

  • 声明类型为Logger.Level的Bean
  • 在@FeignClient或@EnableFeignClient&注解上使用

微服务拆分

用户服务拆分

创建user-service模块

pom.xml文件:

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>user-service</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--common-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-common</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--api-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-api</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>
        <!--nacos 服务注册发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>

application.yaml文件:

XML 复制代码
server:
  port: 8084
spring:
  application:
    name: user-service # 服务名称
  profiles:
    active: dev
  datasource:
    url: jdbc:mysql://${hm.db.host}:3306/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: ${hm.db.pw}
  cloud:
    nacos:
      server-addr: 192.168.244.132 # nacos地址
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"
knife4j:
  enable: true
  openapi:
    title: 用户服务接口文档
    description: "信息"
    email: zhanghuyi@itcast.cn
    concat: 虎哥
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.hmall.user.controller
hm:
  jwt:
    location: classpath:hmall.jks
    alias: hmall
    password: hmall123
    tokenTTL: 30m

最终的代码结构为:

导入准备的hm-user数据库

交易服务

创建trade-service交易模块

pom.xml文件如下:

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">
    <parent>
        <artifactId>hmall</artifactId>
        <groupId>com.heima</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>trade-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>
        <!--api-->
        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-api</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>
        <!--nacos 服务注册发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</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>

application.yaml配置文件内容如下:

XML 复制代码
server:
  port: 8085
spring:
  application:
    name: trade-service # 服务名称
  profiles:
    active: dev
  datasource:
    url: jdbc:mysql://${hm.db.host}:3306/hm-trade?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: ${hm.db.pw}
  cloud:
    nacos:
      server-addr: 192.168.244.132:8848 # nacos地址
mybatis-plus:
  configuration:
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
  global-config:
    db-config:
      update-strategy: not_null
      id-type: auto
logging:
  level:
    com.hmall: debug
  pattern:
    dateformat: HH:mm:ss:SSS
  file:
    path: "logs/${spring.application.name}"
knife4j:
  enable: true
  openapi:
    title: 交易服务接口文档
    description: "信息"
    email: zhanghuyi@itcast.cn
    concat: 虎哥
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.hmall.trade.controller

在trade-service服务中创建启动来TradeApplication

XML 复制代码
package com.hmall.trade;

import com.hmall.api.config.DefaultFeignConfig;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients(basePackages = "com.hmall.api.client", defaultConfiguration = DefaultFeignConfig.class)
@MapperScan("com.hmall.trade.mapper")
@SpringBootApplication
public class TradeApplication {
    public static void main(String[] args) {
        SpringApplication.run(TradeApplication.class, args);
    }
}

交易服务的文件结构如下:

用户下单时需要做下列事情:

  • 根据id查询商品列表

  • 计算商品总价

  • 保存订单

  • 扣减库存

  • 清理购物车商品

查询商品、扣减库存都与商品服务相关联;清理购物车商品与购物车服务有关。

在hm-api服务中添加购物车客户端接口,根据id清理购物车内商品

java 复制代码
package com.hmall.api.client;

import io.swagger.annotations.ApiImplicitParam;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Collection;


@FeignClient("cart-service")
public interface CartClient {

    @ApiImplicitParam(name = "ids", value = "购物车条目id集合")
    @DeleteMapping("/carts")
    void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids);
}

在hm-api服务中的商品客户端中添加扣减库存

java 复制代码
package com.hmall.api.client;

import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
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> queryItemsByIds(@RequestParam("id") Collection<Long> ids);

    @PutMapping("/items/stock/deduct")
    void deductStock(@RequestBody List<OrderDetailDTO> items);
}

总结

通过合理的 OpenFeign 连接池配置、日志优化和服务拆分,可以显著提升微服务系统的性能和可靠性。连接池的配置帮助减少连接建立的开销,日志策略则提供了更好的可观测性和故障排查能力,服务拆分则让系统更具扩展性和灵活性。希望本篇博客中的实践和技巧,能够为你在构建微服务架构时提供有效的参考和帮助。

相关推荐
许苑向上1 小时前
Spring中三级缓存详细讲解
java·spring·三级缓存机制
大强的博客1 小时前
《Spring Framework实战》11:4.1.4.2.详细的依赖和配置2
java·后端·spring
王子良.2 小时前
Spring Boot 和微服务:快速入门指南
spring boot·后端·微服务
荆州克莱3 小时前
ref useRef React.createRef React.forwardRef
spring boot·spring·spring cloud·css3·技术
@小匠3 小时前
微服务的CAP定理与数据一致性抉择
微服务·云原生·架构
言之。3 小时前
【微服务】面试 8、分布式任务调度
分布式·微服务·面试
小陈的知识分享3 小时前
微服务的配置共享
微服务
言之。3 小时前
【微服务】面试 2、负载均衡
微服务·面试·负载均衡
荆州克莱3 小时前
React - router的使用 && 结合react-redux的路由守卫
spring boot·spring·spring cloud·css3·技术
言之。4 小时前
【微服务】面试 4、限流
微服务·面试·架构