• SpringCloud原理-OpenFeign篇(一、Hello OpenFeign项目示例)


    前言

    本篇是SpringCloud原理系列的 OpenFeign 模块的第一篇。主要内容是搭建一个极简的Spring Cloud OpenFeign 调用链路。

    项目代码仓库地址:https://gitee.com/fengsoshuai/springcloud-openfeign-demo

    正文

    本次项目使用java 17,spring cloud 4.0.4,springboot 3.1.4。
    maven 环境编译,idea开发。

    一、项目结构

    本次项目分为3个模块。
    在这里插入图片描述
    在这里插入图片描述

    二、服务调用链路说明

    在这里插入图片描述

    1. 使用IDEA/Postman/Apifox等工具进行触发client服务的接口
    2. client内部通过feign调用server接口
    3. server执行业务逻辑
    4. server返回执行结果到client
    5. client返回调用结果到触发方

    三、Rpc调用链路说明

    两个服务之间,使用远程调用。
    基本都是需要URL,请求头,请求报文,请求方式(Get\Post 等)等基本信息的。
    下图简单说明rpc调用时的链路。
    在这里插入图片描述
    其中,调用方,相当于发起远程调用的一方,对比本项目的话,相当于使用postman等工具触发后,client模块的操作。

    只是特殊的一点在于,调用方中的红色虚线框内的部分,被openFeign 封装了,不再是我们手动去处理他们。而这也正是本系列研究的重点。

    中间部分,就是形如 RestTemplate,WebClient的功能,只是发出请求,接受响应。

    服务方,就是一个提供rest接口的普通应用。

    四、项目代码

    本文全部代码托管在gitte仓库中,地址已经在文章开头给出。
    这里只粘贴出比较重要的几个文件。

    4.1 client 模块中的feign接口

    package org.feng.feigns;
    
    import org.feng.common.dto.HelloRequest;
    import org.feng.common.dto.HelloResponse;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    
    /**
     * hello-feign客户端接口
     *
     * @version v1.0
     * @author: fengjinsong
     * @date: 2023年11月20日 21时25分
     */
    @FeignClient(name = "helloFeignClient", url = "http://localhost:10080")
    public interface HelloFeignClient {
    
        @PostMapping("/hello/post")
        HelloResponse postHello(@RequestBody HelloRequest helloRequest);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.2 client 中的rest接口

    package org.feng.controller;
    
    import jakarta.annotation.Resource;
    import jakarta.servlet.http.HttpServletRequest;
    import lombok.extern.slf4j.Slf4j;
    import org.feng.common.dto.HelloRequest;
    import org.feng.common.dto.HelloResponse;
    import org.feng.feigns.HelloFeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import java.time.LocalDateTime;
    import java.util.Objects;
    
    /**
     * HelloFeignClientController
     *
     * @version v1.0
     * @author: fengjinsong
     * @date: 2023年11月20日 21时45分
     */
    @Slf4j
    @RestController
    @RequestMapping("/helloclient")
    public class HelloFeignClientController {
    
        @Resource
        private HelloFeignClient helloFeignClient;
    
        @PostMapping("/postHello")
        public HelloResponse postHello(@RequestBody HelloRequest helloRequest) {
            if(Objects.isNull(helloRequest.getLocalDateTime())){
                helloRequest.setLocalDateTime(LocalDateTime.now());
            }
    
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            String localAddr = request.getLocalAddr();
            int serverPort = request.getServerPort();
            helloRequest.setHost(localAddr);
            helloRequest.setPort(serverPort);
    
            log.info("helloRequest  {}", helloRequest);
            return helloFeignClient.postHello(helloRequest);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    4.3 client 中的启动类

    指定扫描包为 org.feng.feigns

    package org.feng;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @EnableFeignClients(basePackages = "org.feng.feigns")
    @SpringBootApplication
    public class ClientApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ClientApplication.class, args);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.4 server中的rest接口

    package org.feng.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.feng.common.dto.HelloRequest;
    import org.feng.common.dto.HelloResponse;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * openfeign 控制器
     *
     * @author feng
     */
    @Slf4j
    @RequestMapping("/hello")
    @RestController
    public class HelloOpenFeignController {
    
        @PostMapping("/post")
        public HelloResponse postHello(@RequestBody HelloRequest helloRequest) {
            log.info("request:{}", helloRequest);
    
            HelloResponse response = new HelloResponse();
            response.setTitle(helloRequest.getTitle());
            response.setLocalDateTime(helloRequest.getLocalDateTime());
            response.setFromHost(helloRequest.getHost());
            response.setFromPort(helloRequest.getPort());
    
            log.info("response: {}", response);
            return response;
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    4.5 server中的配置文件

    spring.application.name=openserver
    server.port=10080
    
    • 1
    • 2

    五、调试

    启动server 和 client 服务。
    在idea中触发client 的服务:

    POST http://localhost:8080/helloclient/postHello
    Content-Type: application/json
    
    {
      "title": "托尔斯泰"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    响应报文如下:

    {
      "fromHost": "127.0.0.1",
      "fromPort": 8080,
      "title": "托尔斯泰",
      "localDateTime": "2023-11-21T14:07:18.537384"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    server中的服务,打印出来的日志如下:
    在这里插入图片描述
    以上就是通过onepfeign 进行rpc 调用的完整示例了。

    可以看到,我们只在client中定义了接口,并没有实现。但是在调用时,没有报错,同时也调用到了server服务。而这,就是spring cloud 中的 openfeign 封装了远程调用,帮我们处理的部分,也是我们后续研究其原理的核心部分。

  • 相关阅读:
    C和指针 第13章 高级指针话题 13.3 函数指针
    抽象轻松测试接口API
    猿创征文 |【算法面试入门必刷】动态规划-线性dp(四)
    ROS2 从头开始:第3部分 — 创建自定义消息(.msg 和 .srv)和 Turtlebot3 服务节点的实践指南
    光伏、储能双层优化配置接入配电网研究(附带Matlab代码)
    分享一些领英添加客户的话术
    【网络安全】SSL Pinning及其绕过
    C# I/O流: FileStream
    #案例:演示网页截图!
    插入排序算法
  • 原文地址:https://blog.csdn.net/FBB360JAVA/article/details/134528304