- 思考: 使用RestTemplate+ribbon已經可以完成服務間的調用,為什么還要使用feign?
String restTemplateForObject = restTemplate.getForObject("http://服務名/url?參數" + name, String.class);
存在問題:
- 1.每次調用服務都需要寫這些代碼,存在大量的代碼冗余
- 2.服務地址如果修改,維護成本增高
- 3.使用時不夠靈活
說明
- https://cloud.spring.io/spring-cloud-openfeign/reference/html/
- Feign是一個聲明式的偽Http客戶端,它使得寫Http客戶端變得更簡單。使用Feign,只需要創建一個接口並注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign 注解和JAX-RS注解。Feign支持可插拔的編碼器和解碼器。Feign默認集成了Ribbon,默認實現了負載均衡的效果並且springcloud為feign添加了springmvc注解的支持。
1.openFeign 服務調用
還是在上一個項目的基礎之上,在users項目中
1.服務調用方法引入依賴OpenFeign依賴
<!--Open Feign依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.入口類加入注解開啟OpenFeign支持
@SpringBootApplication
@EnableFeignClients //開啟openfeign支持
public class Users9999Application {
public static void main(String[] args) {
SpringApplication.run(Users9999Application.class, args);
}
}
3.創建一個客戶端調用接口
// 此時的product項目中的方法
@RestController
@Slf4j
public class ProductController {
@Value("${server.port}")
private int port;
@GetMapping("/product/findAll")
public Map<String, Object> findAll(){
log.info("商品服務調用成功,當前的服務端口:[{}]",port);
HashMap<String, Object> map = new HashMap<>();
map.put("msg","服務調用成功,服務提供的端口為:"+port);
map.put("status",true);
return map;
}
}
//--------------------------------------------------------------------
package com.md.clients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author md
* @Desc 調用商品服務的組件
* @date 2020/12/9 15:30
*/
// 指定當前的接口是openfeign組件,value是調用的服務名
@FeignClient("products")
public interface ProductClient {
@GetMapping("/product/findAll")
String findAll();
}
4.使用feignClient客戶端對象調用服務
@RestController
@Slf4j
public class UserController {
//注入客戶端對象
@Autowired
private ProductClient productClient;
@GetMapping("/user/findAllFeignClient")
public String findAllFeignClient(){
log.info("通過使用OpenFeign組件調用商品服務...");
String msg = productClient.findAll();
return msg;
}
}
5.訪問並測試服務
2.調用服務並傳參
- 服務和服務之間通信,不僅僅是調用,往往在調用過程中還伴隨着參數傳遞,接下來重點來看看OpenFeign在調用服務時如何傳遞參數
3.GET方式調用服務傳遞參數
- 在商品服務中加入需要傳遞參數的服務方法來進行測試
- 在用戶服務中進行調用商品服務中需要傳遞參數的服務方法進行測試
1.商品服務中添加如下方法
@GetMapping("/product/findOne")
public Map<String,Object> findOne(String productId){
log.info("商品服務查詢商品信息調用成功,當前服務端口:[{}]",port);
log.info("當前接收商品信息的id:[{}]",productId);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服務查詢商品信息調用成功,當前服務端口: "+port);
map.put("status",true);
map.put("productId",productId);
return map;
}
2.用戶服務中在product客戶端中聲明方法
//
@FeignClient("products")
public interface ProductClient {
@GetMapping("/product/findOne")
Map<String, Object> findOne(@RequestParam("productId") String productId);
}
注意:使用openfeign的get方式傳遞參數,參數變量必須通過@RequestParam注解進行修飾
3.用戶服務中調用並傳遞參數
//
//注入客戶端對象
@RestController
@Slf4j
public class UserController {
@Autowired
private ProductClient productClient;
@GetMapping("/user/findOne")
public Map<String, Object> findOne(String productId){
log.info("用來測試Openfiegn的GET方式參數傳遞");
Map<String, Object> msg = productClient.findOne(productId);
log.info("調用返回信息:[{}]",msg);
return msg;
}
}
4.測試訪問
4.post方式調用服務傳遞參數
- 在商品服務中加入需要傳遞參數的服務方法來進行測試
- 在用戶服務中進行調用商品服務中需要傳遞參數的服務方法進行測試
1.商品服務加入post方式請求並接受name
@PostMapping("/product/save")
public Map<String,Object> save(String name){
log.info("商品服務保存商品調用成功,當前服務端口:[{}]",port);
log.info("當前接收商品名稱:[{}]",name);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品查詢服務完成當前服務端口: "+port);
map.put("status",true);
map.put("name",name);
return map;
}
2.用戶服務中在product客戶端中聲明方法
//value屬性用來指定:調用服務名稱
@FeignClient("products")
public interface ProductClient {
@PostMapping("/product/save")
String save(@RequestParam("name") String name);
}
3.用戶服務中調用並傳遞參數
@Autowired
private ProductClient productClient;
@PostMapping("/user/save")
public Map<String, Object> save(String productName){
log.info("接收到的商品信息名稱:[{}]",productName);
Map<String, Object> map = productClient.save(productName);
log.info("調用成功返回結果: "+map);
return map;
}
4.測試訪問
5.傳遞對象類型參數
- 商品服務定義對象
- 商品服務定義對象接收方法
- 用戶服務調用商品服務定義對象參數方法進行參數傳遞
//1.商品服務定義對象
@Data
public class Product {
private Integer id;
private String name;
private Date bir;
}
//2.商品服務定義接收對象的方法
@PostMapping("/product/saveProduct")
public Map<String,Object> saveProduct(@RequestBody Product product){
log.info("商品服務保存商品信息調用成功,當前服務端口:[{}]",port);
log.info("當前接收商品名稱:[{}]",product);
Map<String, Object> map = new HashMap<String,Object>();
map.put("msg","商品服務查詢商品信息調用成功,當前服務端口: "+port);
map.put("status",true);
map.put("product",product);
return map;
}
//3.將商品對象復制到用戶服務中
// 先階段先這樣用着
//4.用戶服務中在product客戶端中聲明方法
@FeignClient("products")
public interface ProductClient {
@PostMapping("/product/saveProduct")
String saveProduct(@RequestBody Product product);
}
//注意:服務提供方和調用方一定要加入@RequestBody注解
注意:服務提供方和調用方一定要加入@RequestBody注解
// 5.在用戶服務中調用保存商品信息服務
//注入客戶端對象
@Autowired
private ProductClient productClient;
@PostMapping("/user/saveProduct")
public Map<String, Object> saveProduct(Product product){
log.info("接收到的商品信息:[{}]",product);
Map<String, Object> map = productClient.saveProduct(product);
log.info("調用成功返回結果: "+map);
return map;
}
測試
5.OpenFeign超時設置
1.超時說明
- 默認情況下,openFiegn在進行服務調用時,要求服務提供方處理業務邏輯時間必須在1S內返回,如果超過1S沒有返回則OpenFeign會直接報錯,不會等待服務執行,但是往往在處理復雜業務邏輯是可能會超過1S,因此需要修改OpenFeign的默認服務調用超時時間。
2.模擬超時
- 服務提供方加入線程等待阻塞
3.進行客戶端調用
4.修改OpenFeign默認超時時間
# 這里的PRODUCTS使用的是大寫的方法
feign.client.config.PRODUCTS.connectTimeout=5000 #配置指定服務連接超時
feign.client.config.PRODUCTS.readTimeout=5000 #配置指定服務等待超時
#feign.client.config.default.connectTimeout=5000 #配置所有服務連接超時
#feign.client.config.default.readTimeout=5000 #配置所有服務等待超時
6.OpenFeign調用詳細日志展示
0.說明
- 往往在服務調用時我們需要詳細展示feign的日志,默認feign在調用是並不是最詳細日志輸出,因此在調試程序時應該開啟feign的詳細日志展示。feign對日志的處理非常靈活可為每個feign客戶端指定日志記錄策略,每個客戶端都會創建一個logger默認情況下logger的名稱是feign的全限定名需要注意的是,feign日志的打印只會DEBUG級別做出響應。
- 我們可以為feign客戶端配置各自的logger.level對象,告訴feign記錄那些日志logger.lever有以下的幾種值
NONE 不記錄任何日志
BASIC 僅僅記錄請求方法,url,響應狀態代碼及執行時間
HEADERS 記錄Basic級別的基礎上,記錄請求和響應的header
FULL 記錄請求和響應的header,body和元數據
1.開啟日志展示
# 這里的PRODUCTS使用的是大寫的方法
feign.client.config.PRODUCTS.loggerLevel=full #開啟指定服務日志展示
#feign.client.config.default.loggerLevel=full #全局開啟服務日志展示
logging.level.com.baizhi.feignclients=debug #指定feign調用客戶端對象所在包,必須是debug級別