目錄
Feign 聲明式web服務客戶端
spring-cloud-openfeign 官網:https://spring.io/projects/spring-cloud-openfeign
spring cloud 官方 2.1.x 文檔:https://cloud.spring.io/spring-cloud-openfeign/2.1.x/single/spring-cloud-openfeign.html
feign Github 開源地址:https://github.com/OpenFeign/feign
1、feign 是一個聲明式 Web 服務客戶端/http 客戶端,它使編寫 Web 服務客戶端更加容易,要使用 feign,請創建一個接口並對其進行註釋。它具有可插拔的註解支持,包括外部註解和 JAX-RS 註解。Feign 還支持可插拔的編碼器和解碼器。
2、Spring Cloud 增加了對 Spring MVC 註解的支持,並支持使用 Spring Web 中默認使用的 HttpMessageConverters,Spring Cloud 集成了 Ribbon 和 Eureka,在使用 Feign 時提供一個負載均衡的 http 客戶端。
3、雖然直接使用 org.springframework.web.client.RestTemplate 也可以實現微服務之間的 http 調用,但是 feign 作爲一個獨立的庫,更具有優勢,它使得調用遠程微服務的 API 就如同調用自己本地的 API 一樣。
4、How to Include Feign?按着官網文檔介紹,使用 feign 很簡單,分爲如下幾步:
1)服務請求/調用/消費方在 pom.xml 文件中導入 feign 依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)服務請求/調用/消費方在啓動類上加上 @org.springframework.cloud.openfeign.EnableFeignClients 註解開啓 feignClient 客戶端:
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3)服務請求/調用/消費方創建一個接口,@FeignClient 表示此接口爲 feign 客戶端,"stores" 爲服務提供者的微服務名稱,可以從註冊中心看到,接口中的方法就是服務提供者 Cotroller 層的方法。
其中 @RequestMapping 請求方式必須與服務提供者提供的方式一致,value 是請求路徑,如果對方設置了應用上下文,則 value 中也要加上,方法名稱可以自定義,不過建議與服務提供者一致。
@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);
}
4)然後服務請求/調用/消費方可以在自己的 Controller 中調用上面接口中的方法,表面上好像調用自己的 API,實際上會通過微服務名稱和路徑調用遠程微服務接口。
feign 聲明式web客戶端使用
1、使用非常簡單,開發環境爲:Java JDK 1.8 + Spring Boot 2.1.3 + Spring Cloud Greenwich.SR1 + IDEA 2018。
2、準備三個微服務應用:eurekaserver_changsha 應用作 eureka 服務端,用於服務註冊;eureka-client-food 應用提供服務;feign-client-cat 應用作爲服務請求者,請求 eureka-client-food 提供的服務(接口)
3、操作流程:用戶從瀏覽器訪問 feign-client-cat 、feign-client-cat 應用內部調用 eureka-client-food 微服務,然後返回數據。
eurekaserver_changsha(註冊中心)
1、pom.xml 文件核心內容如下(詳細源碼地址:https://github.com/wangmaoxiong/feign_first):
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2、全局配置文件內容如下(詳細源碼地址:https://github.com/wangmaoxiong/feign_first):
server:
port: 9393
eureka:
server:
enable-self-preservation: false #關閉自我保護機制
eviction-interval-timer-in-ms: 60000 #驅逐計時器掃描失效服務間隔時間。(單位毫秒,默認 60*1000)
instance:
hostname: localhost
client:
register-with-eureka: false #禁用自己向自己註冊
fetch-registry: false #不同步其他的 Eureka Server節點的數據
service-url: #Eureka Client 與 Eureka Server 交互的地址
default-zone: http://${eureka.instance.hostname}:${server.port}/eureka/
3、啓動類上添加 @EnableEurekaServer:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @EnableEurekaServer:開啓 eureka 服務
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaserverChangshaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaserverChangshaApplication.class, args);
}
}
註冊中心提供服務註冊,內容不多。
eureka-client-food(服務提供者)
1、pom.xml 文件核心內容如下(詳細源碼地址:https://github.com/wangmaoxiong/feign_first):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
...
2、全局配置文件內容如下(詳細源碼地址:https://github.com/wangmaoxiong/feign_first):
server:
port: 9395 #服務器端口
servlet:
context-path: /food #應用訪問上下文
spring:
application:
name: eureka-client-food #微服務名稱
eureka:
client:
service-url:
defaultZone: http://localhost:9393/eureka/ #eureka 服務器地址
instance:
prefer-ip-address: true # IP 地址代替主機名註冊
instance-id: changSha-food # 微服務實例id名稱
3、服務提供者提供的服務就是 http 訪問的接口,所以創建一個 Controller 層,提供訪問接口,其中提供了不同參數的訪問方式,以達到基本滿足日常開發的需要:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import wmx.com.eurekaclient_food.pojo.Person;
import java.io.IOException;
import java.time.LocalDateTime;
/**
* 菜譜
*
* @author wangmaoxiong
*/
@RestController
public class Cuisine {
private static final Logger logger = LoggerFactory.getLogger(Cuisine.class);
/**
* 獲取湘菜菜譜數據。訪問地址:http://localhost:9395/food/getHunanCuisine?uuid=98389uou8309adko990
*
* @return
*/
@GetMapping("getHunanCuisine")
public String getHunanCuisine(String uuid) {
logger.info("獲取湖南菜譜,uuid = {}", uuid);
JsonNodeFactory nodeFactory = JsonNodeFactory.instance;//以 json 格式返回
ArrayNode arrayNode = nodeFactory.arrayNode()
.add("辣椒炒肉")
.add("剁椒魚頭")
.add("螞蟻上樹")
.add(StringUtils.trimToNull(uuid));
return arrayNode.toString();
}
/**
* 根據 id 刪除:http://localhost:9395/food/deleteDataById?id=980890
*
* @param id
* @return
*/
@GetMapping("deleteDataById")
public String deleteDataById(@RequestParam(value = "id") Integer id) {
logger.info("根據 id 刪除,id = {}", id);
JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
ObjectNode objectNode = nodeFactory.objectNode();//以 json 格式返回
objectNode.put("id", id);
objectNode.put("code", 200);
objectNode.put("message", "delete success");
return objectNode.toString();
}
/**
* 更新:http://localhost:9395/food/updateData/889uuo65eud99?data=name_zhangsan,age_33
*
* @param uid :使用路徑變量
* @param data :使用普通的請求參數
* @return
*/
@RequestMapping(value = "updateData/{uid}", method = RequestMethod.GET)
public String updateData(@PathVariable("uid") String uid, String data) {
logger.info("更新數據,uid = {}, data = {}", uid, data);
JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
ObjectNode objectNode = nodeFactory.objectNode(); //以 json 格式返回
objectNode.put("code", 200);
objectNode.put("message", "更新成功");
objectNode.put("uid", uid);
objectNode.put("data", data);
return objectNode.toString();
}
/**
* 保存數據:http://localhost:9395/food/saveData?type=saveing
*
* @param jsonData :使用請求正文(json 格式)傳入,數據在請求體中,如:{"id":9527,"name":"華安","order":100000},頁面必須傳入
* @param type :使用普通的 key-value 格式傳入,數據在請求透中
* @return
*/
@PostMapping("saveData")
public String saveData(@RequestBody String jsonData, @RequestParam String type) {
logger.info("保存數據,jsonData = {}, type = {}", jsonData, type);
JsonNodeFactory nodeFactory = JsonNodeFactory.instance;
ObjectNode objectNode = nodeFactory.objectNode();//以 json 格式返回
try {
objectNode.put("code", 200);
objectNode.put("message", "保存成功");
objectNode.put("type", type);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(jsonData);
objectNode.set("jsonDta", jsonNode);
} catch (IOException e) {
e.printStackTrace();
}
return objectNode.toString();
}
/**
* 測試 get 方式複雜對象調用:http://localhost:9395/food/updatePerson?pid=100&pname=張三&age=33
*
* @param person
* @return
*/
@GetMapping("updatePerson")
public Person updatePerson(Person person) {
logger.info("更新數據 person = {}", person);
person.setBirthday(LocalDateTime.now());
return person;
}
/**
* 測試 post 方式複雜對象調用:http://localhost:9395/food/updatePerson2?pid=100&pname=張三&age=33
* 1、對於複雜對象,不建議使用 @RequestBody 在請求體中傳遞數據,因爲 feignClient 客戶端調用時會很難處理
* 2、如果非得要使用 @RequestBody ,則建議使用 String 類型,而不是複雜對象
* 3、也可以如下所示,使用請求頭傳遞參數,這樣 feignClient 客戶端可以將複雜對象拆成屬性調用
*
* @param person
* @return
*/
@PostMapping("updatePerson2")
public Person updatePerson2(Person person) {
logger.info("更新數據(post) person = {}", person);
person.setBirthday(LocalDateTime.now());
return person;
}
}
feign-client-cat(服務消費者)
1、pom.xml 文件核心內容如下(詳細源碼地址:https://github.com/wangmaoxiong/feign_first):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
...
2、全局配置文件內容如下(詳細源碼地址:https://github.com/wangmaoxiong/feign_first):
server:
port: 9394 #服務器端口
servlet:
context-path: /cat #應用訪問上下文
spring:
application:
name: feign-client-cat #微服務名稱
eureka:
client:
service-url:
defaultZone: http://localhost:9393/eureka/ #eureka 服務器地址
instance:
prefer-ip-address: true # IP 地址代替主機名註冊
instance-id: feign-cat # 微服務實例id名稱
feign:
name:
food: eureka-client-food #服務提供方的服務名稱,自定義配置。
3、啓動類代碼如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @EnableFeignClients:開啓 feign 客戶端
* @EnableEurekaClient:開啓 eureka 客戶端,可以不寫,默認就是開啓的
*/
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
public class FeignClientCatApplication {
public static void main(String[] args) {
SpringApplication.run(FeignClientCatApplication.class, args);
}
}
4、提供 feign 客戶端接口如下:
1)@FeignClient 註解的 vaule 和 name 其實是一個屬性,互相使用了別名,完全等價。值爲服務提供方的服務名稱。
2)@FeignClient(name = "${feign.name.food}"):推薦方式,服務提供方的服務名稱從配置文件讀取。
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import wmx.com.feign_client_cat.pojo.Person;
import java.time.LocalDateTime;
/**
* 1、@FeignClient :聲明接口爲 feign 客戶端,value 值爲被請求的微服務名稱(註冊中心可以看到,配置文件中的spring.application.name屬性值),
* value 可以省略如 @FeignClient("eureka-client-food")。推薦方式:@FeignClient(name = "${feign.name.food}")
* 2、@FeignClient 接口中的方法名稱可以自定義,但建議保持與對方一致,請求方式必須一致,請求路徑記得帶上服務提供方上下文路徑(如果有的話)
* 有些細節需要注意,下面註釋中有說明
*/
@FeignClient(value = "eureka-client-food")
public interface FoodFeignClient {
/**
* 獲取湘菜菜譜數據
* 1、"food" 是被請求應用的上下文件路徑,一併寫在方法上
* 2、@GetMapping 也可以拆開寫成:@RequestMapping(value = "food/getHunanCuisine", method = RequestMethod.GET)
* 3、參數爲字符串時,如果沒加 @RequestParam("uuid") 請求參數註解,則請求時會拋異常如下:
* feign.FeignException: status 405/404 reading FoodFeignClient#getHunanCuisine(String)
* @return
* @GetMapping("getHunanCuisine")
*/
@GetMapping("food/getHunanCuisine")
public String getHunanCuisine(@RequestParam("uuid") String uuid);
/**
* 根據 id 刪除
* 1、@RequestMapping 也可以換成 @GetMapping("food/deleteDataById")
* 2、經過實測參數爲整形時,@RequestParam("id") 此時可加可不加,但都建議加上
* @param id
* @return
*/
@RequestMapping(value = "food/deleteDataById", method = RequestMethod.GET)
public String deleteDataById(@RequestParam("id") Integer id);
/**
* 更新
* 1、@PathVariable("uid") 註解可以不寫 value 屬性
* 2、再次提醒:對於 String 參數,服務提供者方法上有沒有加 @RequestParam,feignClient 客戶端都需要加上,否則調用失敗,拋異常:
* feign.FeignException: status 405 reading FoodFeignClient#updateData(String,String)
* @param uid :使用路徑變量
* @param data :使用普通的請求參數
* @return
*/
@GetMapping(value = "food/updateData/{uid}")
public String updateData(@PathVariable("uid") String uid, @RequestParam String data);
/**
* 保存數據,post 請求。再次提醒 food 是服務提供者應用上下文件路徑
* @return
*/
@PostMapping("food/saveData")
public String saveData(@RequestBody String jsonData, @RequestParam String type);
/**
* 測試複雜對象調用
* 1、對於 get 請求,參數爲複雜對象時,feignClient 中如果直接使用 public Person updatePerson(Person person); 會拋異常:
* feign.FeignException: status 404 reading FoodFeignClient#updatePerson(Person)
* 2、拋異常的原因是對於get方式複雜對象傳遞時,雖然已經指明瞭是 get 方式,但是 feign 還是會以 post 方式傳遞,導致調用失敗
* 3、解決方式1:可以和服務提供者協商,轉換成 String 的方式進行傳遞;解決方式2:將複雜對象(Pojo) 拆成簡單的屬性,如下所示
* @return
*/
@GetMapping("food/updatePerson")
public Person updatePerson(@RequestParam Integer pid,
@RequestParam String pname,
@RequestParam Integer age,
@RequestParam LocalDateTime birthday);
/**
* 測試 post 方式複雜對象調用
* 1、與 get 方式差不多,當服務提供者使用 updatePerson2(Person person) 時,feign 客戶端不能這麼直接傳遞
* 雖然 post 方式這樣直傳不會報錯,但是對方接收不到數據,所以仍然只能拆成簡單的屬性進行傳遞
* 2、如果對方要求的是 @RequestBody ,則此時這樣寫會直接拋異常,推薦解決方式是使用 String 類型傳遞
* @return
*/
@PostMapping("food/updatePerson2")
public Person updatePerson2(@RequestParam Integer pid,
@RequestParam String pname,
@RequestParam Integer age,
@RequestParam LocalDateTime birthday);
}
5、@FeignClient 客戶端接口中的方法請求方式要求與服務提供者一致,然而服務消費者自己控制層的調用方式是不受約束的,可以自己隨意設置,控制層調用代碼如下:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import wmx.com.feign_client_cat.feignClient.FoodFeignClient;
import wmx.com.feign_client_cat.pojo.Person;
import javax.annotation.Resource;
@RestController
public class CatController {
private static final Logger logger = LoggerFactory.getLogger(CatController.class);
//注入 feign 客戶端實例。有了 feign 之後,調用遠程微服務就如同調用自己本地的方法一樣簡單
//@FeignClient 客戶端接口中的方法請求方式要求與服務提供者一致,然而服務消費者自己控制層的調用方式是不受約束的
@Resource
private FoodFeignClient foodFeignClient;
/**
* 查詢湖南菜譜:http://localhost:9394/cat/getHunanCuisine?uuid=77884934euei000pp
* @param uuid
* @return
*/
@GetMapping("getHunanCuisine")
public String getHunanCuisine(String uuid) {
logger.info("查詢湖南菜譜,uuid = {}", uuid);
String result = this.foodFeignClient.getHunanCuisine(uuid);
return result;
}
/**
* 根據 id 刪除:http://localhost:9394/cat/deleteDataById?id=980890
* @param id
* @return
*/
@GetMapping("deleteDataById")
public String deleteDataById(@RequestParam(value = "id") Integer id) {
logger.info("根據 id 刪除,id = {}", id);
String result = this.foodFeignClient.deleteDataById(id);
return result;
}
/**
* 更新:http://localhost:9394/cat/updateData/889uuo65eud99?data=name_zhangsan,age_33
* @param uid :使用路徑變量
* @param data :使用普通的請求參數
* @return
*/
@RequestMapping(value = "updateData/{uid}", method = RequestMethod.GET)
public String updateData(@PathVariable("uid") String uid, String data) {
logger.info("更新數據,uid = {}, data = {}", uid, data);
String result = this.foodFeignClient.updateData(uid, data);
return result;
}
/**
* 保存數據:http://localhost:9394/cat/saveData?type=saveing
* @param jsonData :使用請求正文(json 格式)傳入,數據在請求體中,如:{"id":9527,"name":"華安","order":100011}
* @param type :使用普通的 key-value 格式傳入,數據在請求透中
* @return
*/
@PostMapping("saveData")
public String saveData(@RequestBody String jsonData, @RequestParam String type) {
logger.info("保存數據,jsonData = {}, type = {}", jsonData, type);
String result = this.foodFeignClient.saveData(jsonData, type);
return result;
}
/**
* 測試 get 方式複雜對象調用:http://localhost:9394/cat/updatePerson?pid=100&pname=張三&age=33
* @param person
* @return
*/
@GetMapping("updatePerson")
public Person updatePerson(Person person) {
logger.info("updatePerson(get) person = {}", person);
return this.foodFeignClient.updatePerson(person.getPid(),
person.getPname(),
person.getAge(),
person.getBirthday());
}
/**
* 測試 post 方式複雜對象調用:http://localhost:9394/cat/updatePerson2
* 1、@RequestBody:表示參數通過請求體傳遞,且爲 json 格式,所以前端必須設置請求類型:Content-Type: application/json
* @param person :{"pid":9334,"pname":"華雄","age":35}
* @return
*/
public Person updatePerson2(@RequestBody Person person) {
logger.info("updatePerson(post) person = {}", person);
return this.foodFeignClient.updatePerson2(person.getPid(),
person.getPname(),
person.getAge(),
person.getBirthday());
}
}
詳細源碼地址:https://github.com/wangmaoxiong/feign_first
瀏覽器訪問微服務調用測試
1、順序啓動 eureka 註冊中心、服務提供者、服務消費者,然後從瀏覽器請求 feign-client-cat,如果它能從 eureka-client-food(服務提供者)獲取數據並返回,則說明成功。
2、因爲有 post 請求,所以在 firefox 瀏覽器上安裝使用 https://addons.mozilla.org/zh-CN/firefox/addon/restclient/ 插件進行訪問測試:
Feign 請求與響應壓縮 與 日誌
Feign 請求與響應壓縮
1、Feign request/response compression:可以爲 feign 請求或響應 使用 gzip 壓縮,壓縮設置與爲 web 服務器的設置類似,允許選擇壓縮媒體類型和最小請求閾值長度。哪邊使用 feign 就配置在哪邊。
feign.compression.request.enabled=true #開啓 feign 請求壓縮,默認 false
feign.compression.response.enabled=true #開啓 feign 響應壓縮,默認 false
feign.compression.request.mime-types=text/xml,application/xml,application/json #設置 feign 請求壓縮類型
feign.compression.request.min-request-size=2048 #開啓 feign 請求壓縮閾值,超過此值才進行壓縮,默認 2048
Feign 日誌記錄
1、Feign logging:Feign 日誌默認是不開啓的,可以通過配置進行開啓,如下所示,logging.level 表示日誌級別,後面跟着 feign 客戶端接口的完整類名,或者它的包名,日誌記錄只響應 debug 級別,所以值只能是 debug。
logging:
level:
wmx.com.feign_client_cat.feignClient.FoodFeignClient: debug
2、上面的配置開啓之後表示 feign 可以記錄日誌了,但是具體怎麼記錄,還需要在配置類(@Configuration)中進行指定記錄級別:
NONE:無日誌記錄(默認)
BASIC:基本,只記錄請求方法和 url 以及響應狀態代碼和執行時間。
HEADERS: 頭,記錄基本信息以及請求和響應頭。
FULL: 完整,記錄請求和響應的頭、正文和元數據。
3、例下面將 logger.level 設置爲 full 級別:
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SysConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
4、當再次訪問 feign-client-cat 微服務,它內部調用 eureka-client-food 時,控制檯打印日誌記錄如下:
2019-11-04 17:35:36.768 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData] <--- HTTP/1.1 200 (268ms)
2019-11-04 17:35:36.768 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData] content-length: 89
2019-11-04 17:35:36.768 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData] content-type: text/plain;charset=UTF-8
2019-11-04 17:35:36.768 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData] date: Mon, 04 Nov 2019 09:35:36 GMT
2019-11-04 17:35:36.768 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData]
2019-11-04 17:35:36.770 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData] {"code":200,"message":"更新成功","uid":"889uuo65eud99","data":"name_zhangsan,age_33"}
2019-11-04 17:35:36.770 DEBUG 8912 --- [nio-9394-exec-1] w.c.f.feignClient.FoodFeignClient : [FoodFeignClient#updateData] <--- END HTTP (89-byte body)