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

相关推荐
sg_knight21 分钟前
Ribbon负载均衡实战指南:7种策略选择与生产避坑
java·spring boot·spring·spring cloud·微服务·ribbon·负载均衡
书语时2 小时前
Spring @Autowired解析
java·后端·spring
HGW6893 小时前
为什么已经有 Nginx 了,还需要服务网关?
nginx·spring cloud·微服务·架构
迢迢星万里灬5 小时前
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
java·spring boot·spring·mybatis·spring mvc·面试指南
肥仔哥哥19305 小时前
最新SpringBoot+SpringCloud+Nacos微服务框架分享
spring boot·spring cloud·微服务·nacos微服务·最新nacos微服务
面朝大海,春不暖,花不开5 小时前
Spring AI与Spring Modulith核心技术解析
人工智能·spring·flask
有梦想的攻城狮6 小时前
spring中的ImportSelector接口详解
java·后端·spring·接口·importselector
LUCIAZZZ8 小时前
Java设计模式基础问答
java·开发语言·jvm·spring boot·spring·设计模式
IsPrisoner8 小时前
Go 语言实现高性能 EventBus 事件总线系统(含网络通信、微服务、并发异步实战)
开发语言·微服务·golang
KotlinKUG贵州9 小时前
Spring开发,从Kotlin开始
spring boot·spring·kotlin