SpringCloud使用Feign暴露Interface实现RPC

实现生产者中的接口

项目结构组织:项目由多个Module构成,而每个Module又由api模块、model模块、web模块等构成

需求:现在需要将A项目(生产者,或者叫服务提供者)的web模块中的某些接口暴露出去,要求在api模块中定义。使得B项目(消费者,或者叫客户端)能远程调用A项目暴露的接口

首先实现生产者中的接口,在A项目的web模块中,代码如下:

@Api(value = "字典定义", tags = " 字典定义")
@RestController
@RequestMapping(value = "/wcenter/dict/define")
public class WcenterDictDefineRest {
 /**
   * 获取树状字典
   **/
  @ApiOperation(value = "获取树状字典")
  @ApiImplicitParams({
      @ApiImplicitParam(name = "codes", value = "字典编码", paramType = "query", dataType = "String[]", allowMultiple = true),
  })
    //allowMultiple:允许多个,即:数组或集合。
  @GetMapping(value = "/tree")
  public ResponsePacket<RecordsDTO> loadTree(@RequestParam(value = "codes", required = false) String[] codes) {
    //下面为业务逻辑代码
    RecordsDTO<List<TreeEntityDTO>> responseDto = new RecordsDTO<>();
    Condition condition = new Condition();
    List<List<TreeEntityDTO>> treeDict = service.getTreeDict(condition, codes);
    responseDto.setRecords(treeDict);
    return ResponsePacket.generatePacket(responseDto);
  }
}

注意:上面那个类,必须加@RestController注解

暴露接口

首先要引入Feign的依赖,Feign依赖是必须引入的,其余依赖根据自己的接口需要引入其他依赖。如下:

<!-- Feign Form Spring -->
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.8.0</version>
</dependency>
<!-- Feign Form -->
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.8.0</version>
</dependency>
<!-- Feign Core-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>10.10.1</version>
</dependency>
<!-- Feign Hystrix -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hystrix</artifactId>
<version>10.10.1</version>
</dependency>
<!-- Feign Slf4j -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>10.10.1</version>
</dependency>

暴露接口,在A项目的api模块中,代码如下:

@Api("字典定义远程服务接口")
@FeignClient(value = ServiceNameConstants.WISEFLY_WCENTER, path = ServiceNameConstants.WISEFLY_WCENTER_PATH + "/wcenter/dict/define", fallbackFactory = DictDefineFallbackFactory.class)
public interface DictDefineFeign {

    @ApiOperation(value = "获取树状字典")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "codes", value = "字典编码", paramType = "query", dataType = "String[]"),
    })
    @GetMapping(value = "/tree")
    ResponsePacket<RecordsDTO> loadTree(@RequestParam(value = "codes", required = false) String[] codes);
}

注意:

  1. 必须加@FeignClient声明
  2. 其他 value属性的值是yml文件中spring.application.name的值,这里建议用yml语法动态获取,如上面代码中所示 (用name属性也行,value属性也行)
  3. 如果yml文件中配置了server.servlet.context-path,必须配上path属性。否则后面调用这个接口会出现404问题
  4. 关于方法的mapping注解,有人说Feign不支持 GetMapping,笔者测试过 是支持的。这应该与某些依赖的版本有关
  5. 方法参数必须加@RequestParam注解(否则,后面调用该接口会报too many bad parameter错误),若是POST请求方式,需要根据情况加@RequestBody注解

远程调用

需要在B项目中引入A项目的api模块的依赖,例子如下:

<dependency>
<groupId>xxx.xxx</groupId>
<artifactId>business-wcenter-api</artifactId>
<version>2.0.0</version>
</dependency>

B项目的启动类必须加上@EnableFeignClients注解,还必须要加上basePackages的属性,他的值是标注了@FeignClient注解的接口的包路径,如下:


@EnableFeignClients(basePackages = "xx.xx.xx")
@SpringCloudApplication
public class CriterionServerApplication {

}

SpringBoot整合测试

引入SpringBoot的测试依赖,如下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

代码如下:

@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(
    classes = 启动类.class
    )
public class DictFeignTest {
  @Autowired
  DictFeign dictFeign;
  /**
   * 测试 远程调用数据字典
   */
  @Test
  public void testRpcDictFeign(){
    ResponsePacket<ResponseTreeEntityDto> tree = dictFeign.tree(null, null);
    log.info("The dict tree is: {}", tree);
  }
}

这种暴露接口的测试,必须先启动生产者(服务提供者),再启动消费者(客户端)。

我们先启动A项目,再启动B项目,运行上面的测试方法,测试成功。

服务降级处理(fallback)

远程调用失败降级处理,代码如下:

@FeignClient(value = ServiceNameConstants.WISEFLY_WCENTER, path = ServiceNameConstants.WISEFLY_WCENTER_PATH + "/wcenter/dict/define", fallbackFactory = DictDefineFallbackFactory.class)
public interface DictDefineFeign {
}

解释:使用@FeignClient注解的fallbackFactory属性,其值是一个类,代码如下:

@Component
@Slf4j
public class DictDefineFallbackFactory implements FallbackFactory<DictDefineFeign> {

  @Override
  public DictDefineFeign create(Throwable throwable) {
    log.error("字典定义服务调用失败:" + throwable.getMessage());
    return new DictDefineFeign() {

      @Override
      public ResponsePacket<ResponseTreeEntityDto> tree(String[] codes) {
        return ResponsePacket.generateFail("字典定义服务--->获取树状字典失败");
      }

    };
  }
}

踩坑记录

测试暴露接口的过程中,遇到了一系列形如xxxClass can not find、xxxMethod is not define,一般都是对应的依赖没有下载下来。

尤其是依赖中的依赖,有些是设置了<optional>true</optional>,这个作用是只允许此依赖所在的项目使用,即使别的项目引用了这个依赖,都无法下载下来。

因此解决方法是调整依赖,或者单独引入缺失的依赖


   转载规则


《SpringCloud使用Feign暴露Interface实现RPC》 锦泉 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录