浅聊一下微服务远程通信

发布于:2025-09-09 ⋅ 阅读:(17) ⋅ 点赞:(0)

在微服务架构里,服务间通信是绕不开的环节。之前咱们用 RestTemplate+Ribbon 实现过服务调用,但实际开发中会发现有些不便。这篇就重点讲更易用的 Feign 远程调用,包括它的基本使用、自定义配置和性能优化,最后再简单提下 Dubbo 的基础,帮你搞定微服务通信难题。

一、Feign 远程调用

先回想下之前用 RestTemplate 调用服务的代码,比如订单服务查商品:

String url = "service-product";
Product product = restTemplate.getForObject("http://" + url + "/product/" + pid, Product.class);

这种写法有两个明显问题:一是代码可读性差,URL 里拼参数容易出错;二是要是服务接口多了,维护一堆 URL 会很麻烦。而 Feign 就是为解决这些问题而生的。

1.1 Feign 简介

Feign 是 Spring Cloud 提供的声明式伪 HTTP 客户端,核心特点是 “像调用本地服务一样调用远程服务”—— 不用写复杂的 URL 拼接,只要定义一个接口,加几个注解就行。

另外,Feign 和 Nacos 兼容性特别好,而且默认集成了 Ribbon,所以用 Feign 调用服务时,不用额外配置就能实现负载均衡,省了不少事。

Feign 的官方地址在这里:GitHub - OpenFeign/feign: Feign makes writing java http clients easier,感兴趣的可以去看源码或文档。

1.2 Feign 基本使用

Feign 的使用步骤很简单,咱们以 “订单服务调用商品服务” 为例,一步步来:

第一步:加 Feign 依赖

在订单服务(服务消费者)的 pom.xml 里,引入 Feign 的 starter 依赖:

<!-- Feign组件依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
第二步:开启 Feign 功能

在订单服务的主类上,加@EnableFeignClients注解,告诉 Spring “要启用 Feign 的远程调用功能”:

@SpringBootApplication
@EnableFeignClients // 开启Feign远程调用
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
第三步:定义 Feign 接口

创建一个 Service 接口(比如叫ProductService),用注解指定要调用的远程服务名和接口路径:

// @FeignClient("服务名"):指定要调用的远程服务(Nacos里的服务名)
@FeignClient("service-product")
public interface ProductService {
    // 这里的注解和远程服务的Controller接口保持一致
    // 路径、请求方式、参数都要对应,相当于“映射”远程接口
    @GetMapping(value = "/product/{pid}")
    Product findByPid(@PathVariable("pid") Integer pid);
}

这里要注意:@FeignClient里的 “service-product” 是商品服务在 Nacos 中的服务名;@GetMapping的路径和参数,要和商品服务 Controller 里的接口完全匹配(比如商品服务的查详情接口就是/product/{pid})。

第四步:调用远程服务

在订单服务的 Controller 里,注入刚才定义的ProductService接口,直接调用方法就行,跟调用本地 Service 没区别:

@RestController
@RequestMapping("/order")
public class OrderController {
    // 注入Feign接口
    @Autowired
    private ProductService productService;

    @GetMapping("/{pid}")
    public Order createOrder(@PathVariable Integer pid) {
        // 直接调用Feign接口方法,底层会自动发起远程请求
        Product product = productService.findByPid(pid);
        // 后续创建订单的逻辑...
        return new Order(1, "订单-" + pid, product);
    }
}
第五步:启动验证

重启订单服务,调用订单接口试试 —— 会发现能正常拿到商品服务返回的数据,而且因为 Feign 集成了 Ribbon,要是商品服务开了多个实例,还会自动做负载均衡,特别方便。

1.3 Feign 自定义配置

Feign 默认的配置能满足大部分场景,但如果有特殊需求(比如想打印详细的请求日志、自定义参数编码),也可以做自定义配置。先看下 Feign 支持哪些自定义项:

配置类型 作用 说明
feign.Logger.Level 修改日志级别 有 4 种级别:NONE(默认,不打日志)、BASIC、HEADERS、FULL(最详细)
feign.codec.Decoder 响应结果解析器 把远程服务返回的 JSON 等数据解析成 Java 对象
feign.codec.Encoder 请求参数编码器 把 Java 参数编码成 HTTP 请求能传输的格式(比如表单、JSON)
feign.Contract 支持的注解格式 默认支持 SpringMVC 注解(如 @GetMapping),也能改成 Feign 原生注解
feign.Retryer 失败重试机制 默认没有重试,但会沿用 Ribbon 的重试逻辑

最常用的自定义配置是 “日志级别”,下面重点讲怎么配:

方式一:配置文件方式(推荐)

可以针对单个服务配置,也能全局配置,灵活度高。

1. 针对单个服务(比如只给商品服务配日志)

在订单服务的 application.yml 里加:

feign:
  client:
    config:
      service-product: # 要配置的远程服务名(Nacos里的服务名)
        loggerLevel: FULL # 日志级别,这里配FULL看详细日志
2. 全局配置(所有 Feign 调用都生效)

把上面的 “service-product” 改成 “default” 就行:

feign:
  client:
    config:
      default: # default表示全局配置
        loggerLevel: BASIC # 全局日志级别设为BASIC
注意:还要开启基础日志

不管是单个服务还是全局配置,都要在 yml 里把 Feign 接口所在包的日志级别设为 debug,否则 Feign 的日志打不出来:

logging:
  level:
    com.apesource.service: debug # 这里填你的Feign接口所在包路径
方式二:代码配置(自定义 Bean)

如果需要更复杂的配置(比如自定义重试机制),可以用代码创建 Bean 覆盖默认配置。比如自定义日志级别:

@Configuration
public class FeignConfig {
    // 定义日志级别Bean,全局生效
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.HEADERS; // 日志级别设为HEADERS
    }
}

如果想让这个配置只对某个服务生效,就在@FeignClient里指定配置类:

@FeignClient(value = "service-product", configuration = FeignConfig.class)
public interface ProductService {
    // ...接口方法
}

1.4 Feign 使用优化

Feign 底层发起 HTTP 请求时,默认用的是 JDK 的URLConnection,它有个缺点:不支持连接池。每次调用都要新建连接,频繁调用时性能会比较差。

优化思路很简单:用支持连接池的 HTTP 客户端替换默认的 URLConnection,常用的有 Apache HttpClient 和 OKHttp。这里以 Apache HttpClient 为例,讲具体优化步骤:

第一步:引入 HttpClient 依赖

在订单服务的 pom.xml 里加依赖:

<!-- Feign集成Apache HttpClient的依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
第二步:配置连接池参数

在 application.yml 里开启 HttpClient 支持,并设置连接池大小(根据实际业务调整):

feign:
  client:
    config:
      default: # 全局配置
        loggerLevel: BASIC # 日志级别建议用BASIC,兼顾性能和排查问题
  httpclient:
    enabled: true # 开启Feign对HttpClient的支持
    max-connections: 200 # 最大连接数,比如设200
    max-connections-per-route: 50 # 每个路径的最大连接数,比如设50
优化总结

Feign 优化就记住两点:

  1. 日志级别尽量用BASIC:FULL 级别日志太详细,会影响性能,BASIC 只打请求方法、URL、响应状态和执行时间,足够排查问题。
  2. 用连接池客户端:优先选 Apache HttpClient 或 OKHttp,减少连接创建开销,提升并发性能。

二、Dubbo 基本简介与基础实现

除了 Feign,Dubbo 也是微服务通信的常用框架(由阿里开源,现在捐给了 Apache)。不过 Feign 是基于 HTTP 的 REST 风格调用,而 Dubbo 默认是基于 TCP 的 RPC 调用,性能上会更好一些,适合对调用延迟要求高的场景。

2.1 Dubbo 基本简介

Dubbo 的核心架构包含几个关键角色:

  • 服务提供者(Provider):暴露服务的微服务(比如商品服务)。
  • 服务消费者(Consumer):调用服务的微服务(比如订单服务)。
  • 注册中心(Registry):存储服务地址信息(Dubbo 支持 Zookeeper、Nacos 等作为注册中心,和 Feign 兼容 Nacos 类似)。
  • 监控中心(Monitor):统计服务调用次数、耗时等,方便监控。

Dubbo 的调用流程很清晰:

  1. 服务提供者启动后,把自己的服务信息注册到注册中心。
  2. 服务消费者启动后,从注册中心订阅自己需要的服务。
  3. 消费者拿到服务地址后,直接调用提供者的服务(基于 TCP 的 RPC 调用)。
  4. 调用过程中,提供者和消费者会把调用数据上报给监控中心。

2.2 Dubbo 基础实现(简单 demo)

Dubbo 的基础使用也离不开 “依赖 + 配置 + 接口定义” 这几步,这里以 “订单服务调用商品服务” 为例,简单说下步骤(前提是已经有 Nacos 作为注册中心):

第一步:加 Dubbo 依赖

在提供者(商品服务)和消费者(订单服务)的 pom.xml 里,都加 Dubbo 和 Nacos 注册中心的依赖:

<!-- Dubbo Spring Cloud Starter -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-cloud-starter</artifactId>
    <version>2.2.7.RELEASE</version> <!-- 版本根据实际情况选 -->
</dependency>
<!-- Dubbo集成Nacos注册中心 -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-nacos</artifactId>
    <version>2.7.15</version>
</dependency>
第二步:服务提供者配置与实现
  1. 配置 Dubbo:在商品服务的 application.yml 里,配置 Dubbo 的应用名、协议、注册中心地址:
dubbo:
  application:
    name: service-product # 服务名,要唯一
  protocol:
    name: dubbo # 协议名,默认dubbo
    port: -1 # 端口-1表示随机端口,避免端口冲突
  registry:
    address: nacos://127.0.0.1:8848 # Nacos注册中心地址
  scan:
    base-packages: com.apesource.service # 扫描Dubbo服务的包路径

  1. 定义并实现服务接口
    • 先定义一个接口(比如ProductDubboService),放在公共模块(或单独的 API 模块),让提供者和消费者都能引用:
    public interface ProductDubboService {
        Product findByPid(Integer pid);
    }
    
     
    • 在商品服务里实现这个接口,加@DubboService注解(表示这是 Dubbo 暴露的服务):
    @DubboService // Dubbo服务注解,暴露这个服务
    public class ProductDubboServiceImpl implements ProductDubboService {
        @Autowired
        private ProductMapper productMapper;
    
        @Override
        public Product findByPid(Integer pid) {
            return productMapper.selectById(pid);
        }
    }
    
第三步:服务消费者配置与调用
  1. 配置 Dubbo:在订单服务的 application.yml 里,配置 Dubbo 应用名和注册中心地址:
dubbo:
  application:
    name: service-order # 消费者服务名
  registry:
    address: nacos://127.0.0.1:8848 # 同样指向Nacos注册中心

  1. 调用 Dubbo 服务:在订单服务里,用@DubboReference注解注入 Dubbo 服务接口,直接调用:
@RestController
@RequestMapping("/order")
public class OrderController {
    // @DubboReference:注入Dubbo远程服务
    @DubboReference
    private ProductDubboService productDubboService;

    @GetMapping("/{pid}")
    public Order createOrder(@PathVariable Integer pid) {
        // 调用Dubbo服务,和本地调用一样
        Product product = productDubboService.findByPid(pid);
        return new Order(1, "Dubbo订单-" + pid, product);
    }
}
第四步:启动验证

先启动 Nacos,再启动商品服务(提供者)和订单服务(消费者),调用订单接口就能通过 Dubbo 拿到商品数据了。

总结

这篇主要讲了微服务通信的两种常用方案:

  • Feign:声明式 HTTP 客户端,用法简单,默认集成 Ribbon,适合 REST 风格的服务调用,优化重点是用连接池替换默认的 URLConnection。
  • Dubbo:RPC 框架,基于 TCP 调用,性能更好,适合对延迟敏感的场景,核心是 “服务暴露 + 服务引用”,支持 Nacos 等注册中心。

实际项目中选哪种?如果团队熟悉 REST 风格,或者服务需要对外提供 HTTP 接口,选 Feign;如果追求更高的调用性能,内部服务通信优先选 Dubbo。两种方案都能和 Nacos 无缝集成,根据业务需求选就行~


网站公告

今日签到

点亮在社区的每一天
去签到