Feign在K8s中的使用

news/2024/7/8 14:02:02

之前在SpringCloud中使用过@FeignClient的方式对服务进行调用,感觉使用起来还是很方便的,所以想要探索一下是否可以把@FeignClient用在K8s集群中进行服务间的调用;

feign是一个声明式web服务调用的客户端,创建一个接口并加上注解就能使用Feign了(同时支持JAX-RS类型的注解,可插入式的编码和解码),Spring Cloud Feign组件为他加入了spring mvc的注解(@RequestMappging, @RequestBody, @ResponseBody, @RequestParam, @PathVariable等)支持,以及在spring web开发过程中默认使用同样的HttpMessageConverters ,同时Spring Cloud整合了Ribbon和Eureka为使用feign的过程中提供了一个负载均衡的http客户端。

简单示例 

Feign示例(github-OpenFeign-feign):

//feign客户端声明
interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("POST /repos/{owner}/{repo}/issues")
  void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);

}
//DTO定义
public static class Contributor {
  String login;
  int contributions;
}

public static class Issue {
  String title;
  String body;
  List<String> assignees;
  int milestone;
  List<String> labels;
}

//启动类
public class MyApp {
  public static void main(String... args) {
    //注册feign客户端
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                          //注册feign客户端,并指定服务URL
                         .target(GitHub.class, "https://api.github.com");
  
    //feign客户端进行服务调用
    List<Contributor> contributors = github.contributors("OpenFeign", "feign");
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}

SpringCloud @FeignClient示例(Declarative REST Client: Feign):

//FeignClient客户端声明(其中"stores"为SpringCloud中服务名称)
@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);
}

//启动类
@EnableAutoConfiguration
@EnableEurekaClient
//启动FeignClient自动配置
@EnableFeignClients
public class Application {

    //自动注入FeignClient代理
    @Resource
    private StoreClient storeClient;
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        
        //FeignClient进行服务调用
        storeClient.update(1, new Store());
    }

}

//maven依赖
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
   <version>2.1.1.RELEASE</version>
</dependency>

优化

结合Maven多module的特性,可以将Controller抽象出接口定义,即在单独的一个module(如api)中,定义Controller的接口,然后在其他模块(如web) 中去实现Controller的接口,而后将api打包后上传到maven私服供公司内其他服务依赖并调用;

例如Maven项目多module结构如下:

api:对外提供服务的参数、返回结果DTO定义,Controller接口定义
domain:domain对象定义(entity, vo, bo, dto等)
dao:数据访问层(数据库访问、缓存访问)
service:具体服务的定义
web:controller实现层的定义

优化示例

即在api模块中进行controller接口定义:

@RequestMapping("/base")
public interface BaseController {

    @RequestMapping(value = "/reqParam", method = RequestMethod.POST)
    @ResponseBody
    Object reqParam(@SpringQueryMap ParamVo paramVo);

    @RequestMapping(value = "/reqBody", method = RequestMethod.POST)
    @ResponseBody
    Object reqBody(@RequestBody ParamVo paramVo);

    @RequestMapping(value = "/reqPath/{batteryCode}/{page}/{rows}", method = RequestMethod.POST)
    @ResponseBody
    Object reqPath(@PathVariable("batteryCode") String batteryCode, @PathVariable("page") Integer page, @PathVariable("rows") Integer rows);
}

 

在web模块中实现controller接口:

@Controller
@RequestMapping("/base")
public class BaseControllerImpl implements BaseController {
    private static final Logger logger = LogManager.getLogger(BaseControllerImpl.class);

    @RequestMapping(value = "/reqParam", method = RequestMethod.POST)
    @ResponseBody
    public Object reqParam(@SpringQueryMap ParamVo paramVo) {
        logger.info("reqParam param:{}", JsonUtils.toJson(paramVo));
        return MxHttpRespUtility.successResp();
    }

    @RequestMapping(value = "/reqBody", method = RequestMethod.POST)
    @ResponseBody
    public Object reqBody(@RequestBody ParamVo paramVo) {
        logger.info("reqBody param:{}", JsonUtils.toJson(paramVo));
        return MxHttpRespUtility.successResp();
    }

    @RequestMapping(value = "/reqPath/{batteryCode}/{page}/{rows}", method = RequestMethod.POST)
    @ResponseBody
    public Object reqPath(@PathVariable("batteryCode") String batteryCode, @PathVariable("page") Integer page, @PathVariable("rows") Integer rows) {
        logger.info("reqPath param: batteryCode={}, page={}, rows={}", batteryCode, page, rows);
        return MxHttpRespUtility.successResp();
    }
}

注:
@SpringQueryMap为Feign中注解,用于解析QeuryString 形式的参数,
即controller中方法参数为Object reqParam(ParamVo paramVo)形式(参数没有被@RequestParam, @RequestBody等所修饰)时,
需在controller接口定义的相应方法参数上添加@SpringQueryMap,如:Object reqParam(@SpringQueryMap ParamVo paramVo);

 

在其他服务中,依赖dev-web中api模块后,即可通过继承该controller接口的方式进行FeignClient调用:

//启动类添加注解(FeignClient自动配置)
@EnableFeignClients


//声明FeignClient,继承被调用服务的controller接口
//指定url后,即实际请求的路径即为url/requestPath(仅指定name则走SpringCloud的服务名称的负载均衡调用)
@FeignClient(name="base", url = "http://localhost:8089/dev-springboot-template")
public interface BaseControllerFeign extends BaseController {
}

//自动注入FeignClient代理
@Resource
private BaseControllerFeign baseControllerFeign;
...
//FeignClient服务调用
Object result = baseControllerFeign.reqBody(paramVo);

在k8s(或Istio)中,我们可以通过指定url=serviceName.namespace的方式来进行k8s间服务的调用,即无需通过name指定服务名称,在K8s集群中可直接通过serviceName.namespace进行服务间的调用,由K8s(或Istio)集群环境负责服务的路由与负载均衡;

示例类图如下:

总结

通过FeignClient的调用方式,可以将Controller抽象出接口定义,服务端来实现Controller的具体实现逻辑,而客户端依赖Controller接口定义并以此进行服务调用(由OpenFeign客户端对Controller中SpringMVC的注解进行解析并转换为相应的Http调用),此种方式中Controller接口定义为客户端和服务端二者之间连接的桥梁,通过Maven多module的形式使得Controller接口定义得以在多个应用间进行传递复用,而在K8s(Istio)中可以通过指定url=serviceName.namespace/contextPath来进行服务间的请求调用,由K8s(或Istio)集群环境负责服务的路由与负载均衡;

问题:FeignClient接口定义并不是完全符合SpringMVC规范,可能会对原Controller接口进行调整方能完全适配;例如@SpringQueryMap注解的使用,以及@PathVariable需要对应到每个参数上,例如原Controller实现定义如下:

@RequestMapping(value = "/reqPath/{batteryCode}/{page}/{rows}", method = RequestMethod.POST)
@ResponseBody
public Object reqPath(ParamVo paramVo) {
    logger.info("reqPath param:{}", JsonUtils.toJson(paramVo));
    return MxHttpRespUtility.successResp();
}

但是FeignClient需要修改如下形式才可正确调用:

@RequestMapping(value = "/reqPath/{batteryCode}/{page}/{rows}", method = RequestMethod.POST)
@ResponseBody
Object reqPath(@PathVariable("batteryCode") String batteryCode, @PathVariable("page") Integer page, @PathVariable("rows") Integer rows);

以上只是简单的了解,若想真正像使用Feign进行K8s间服务调用,还需对spring-cloud-starter-openfeign源码进行分析与重构,删除不必要的依赖...

 


http://www.niftyadmin.cn/n/1152214.html

相关文章

【2016ACM/ICPC亚洲区大连站C】HDU - 5973 Game of Taking Stones 威佐夫博弈

题意 给你两个石堆的石头数量&#xff0c;两个人轮流拿&#xff0c;两人轮流从任意一堆取至少一个或者从两堆取同样多的物品。问你先手获胜还是后手胜。 http://acm.hdu.edu.cn/showproblem.php?pid5973 分析 威佐夫博弈情景 代码 转载于:https://www.cnblogs.com/helloWR/p/1…

dede文章增加HTML自定义字段字符被过滤问题

为什么80%的码农都做不了架构师&#xff1f;>>> 在dedecms后台频道模型增加自定义字段&#xff0c;一般HTML文字编辑器能解决用户编辑问题&#xff0c;当然还包括纯单行或多行文本编辑。但发现dedecms会自动过滤掉某些敏感的字符&#xff0c;比如style样式&#xf…

springboot jackson日期序列化配置

springboot2.0后会将date自动给转成UTC字符串 配置springboot2.x将date转换为时间戳&#xff1a; spring.jackson.serialization.write-dates-as-timestampstrue 配置springboot2.x将date格式化后再返回&#xff1a; spring.jackson.date-formatyyyy-MM-dd HH:mm:ss spring.jac…

第五次作业——李丽欣

习题1&#xff1a;读入文件pmi_days.csv&#xff0c;完成以下操作&#xff1a;1.统计质量等级对应的天数&#xff0c;例如&#xff1a;优&#xff1a;5天良&#xff1a;3天中度污染&#xff1a;2天2.找出PMI2.5的最大值和最小值&#xff0c;分别指出是哪一天。 import csv impo…

SE-0005,一个你可能想知道的 Swift 改进提案

作者&#xff1a;Erica Sadun&#xff0c;原文链接&#xff0c;原文日期&#xff1a;2016-02-02译者&#xff1a;bestswifter&#xff1b;校对&#xff1a;numbbbbb&#xff1b;定稿&#xff1a;Cee截止 2016 年 2 月 5 日&#xff0c;SE-0005 提案正在接受公开的审核。它提议在…

EMQ开启共享订阅

MQTT协议开启共享订阅 ## Whether the Server supports MQTT Shared Subscriptions. ## ## Value: boolean mqtt.shared_subscription true 对应的Listener的zone开启mqtt EMQ X 消息服务器默认开启的 TCP 服务端口包括: 1883MQTT TCP 协议端口8883MQTT/TCP SSL 端口8083MQT…

不要懒惰,坚持每周总结一篇博客

好久没更新博客了&#xff0c;从今天2019年5月24号开始&#xff0c;每周总结一篇 转载于:https://www.cnblogs.com/uglyliu/p/10920757.html

windows下使用pycharm的一些注意事项

首先安装python 3 进入https://www.python.org/downloads/&#xff0c;选择对应的版本及对应的OS 注意选中环境变量配置 安装pycharm开发工具 通过Settings->Project->Project Interceptor处点击按钮导入依赖包 导入包过程中如果提示如下&#xff1a; error: Microsoft…