文章目錄
- 前言
- 正文
- 一、項目結構
- 二、服務調用鏈路說明
- 三、Rpc調用鏈路說明
- 四、項目代碼
- 4.1 client 模塊中的feign接口
- 4.2 client 中的rest接口
- 4.3 client 中的啟動類
- 4.4 server中的rest接口
- 4.5 server中的配置文件
- 五、調試
- 附錄
- 附1:本系列文章鏈接
前言
本篇是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個模塊。
二、服務調用鏈路說明
- 使用IDEA/Postman/Apifox等工具進行觸發client服務的接口
- client內部通過feign調用server接口
- server執行業務邏輯
- server返回執行結果到client
- 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);
}
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 {@Resourceprivate 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);}
}
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);}}
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;}
}
4.5 server中的配置文件
spring.application.name=openserver
server.port=10080
五、調試
啟動server 和 client 服務。
在idea中觸發client 的服務:
POST http://localhost:8080/helloclient/postHello
Content-Type: application/json{"title": "托爾斯泰"
}
響應報文如下:
{"fromHost": "127.0.0.1","fromPort": 8080,"title": "托爾斯泰","localDateTime": "2023-11-21T14:07:18.537384"
}
server中的服務,打印出來的日志如下:
以上就是通過onepfeign 進行rpc 調用的完整示例了。
可以看到,我們只在client中定義了接口,并沒有實現。但是在調用時,沒有報錯,同時也調用到了server服務。而這,就是spring cloud 中的 openfeign 封裝了遠程調用,幫我們處理的部分,也是我們后續研究其原理的核心部分。
附錄
附1:本系列文章鏈接
SpringCloud原理-OpenFeign篇(一、Hello OpenFeign項目示例)
SpringCloud原理-OpenFeign篇(二、OpenFeign包掃描和FeignClient的注冊原理)
SpringCloud原理-OpenFeign篇(三、FeignClient的動態代理原理)