SpringCloud宣告式服務呼叫Feign
1.建立一個SpringBoot工程,這裡命名為feign-consumer,然後在pom檔案中新增依賴:
<dependencies> ..... <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
2.在主類上使用@EnableFeignClients註解開啟SpringCloudFeign的支援功能
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } }
3.介面定義:我們這裡呼叫USER-SERVICE服務,在該服務中建立一個查詢所有使用者的介面,然後在feign-consumer中定義。
USER-SERVICE
@RestController public class UserFeignController { @Autowired private UserRepository userRepository; @GetMapping("/feign/user/list") public List<User> findAllUser(){ return userRepository.findAll(); } }
feign-consumer
@FeignClient(value = "USER-SERVICE")
public interface UserService {
@GetMapping("/feign/user/list")
List<User> findAll();
}
使用@FeignClient註解指定服務名來繫結服務,如果不指定服務名,啟動專案將會報錯。然後建立一個介面與呼叫普通的service一樣呼叫UserService。
@RestController
public class FeignConsumerController {
@Autowired
private UserService userService;
@GetMapping(value = "/feign/find")
public List<User> findAllUser(){
return userService.findAll();
}
}
最後修改配置檔案
spring:
application:
name: feign-consumer
server:
port: 50000
eureka:
client:
service-url:
defaultZone: http://localhost:8888/eureka/
這裡使用的User物件與前面ARTICLE-SERVICE的User物件一樣。依次啟動服務註冊中心、服務提供方、服務消費方。然後呼叫/feign/find介面,可以正常返回資料。
引數繫結
在實際開發中,像上面那種不帶引數的介面可能少之又少。Feign提供了多種引數繫結的方式。
在服務提供的UserFeignController中新增以下三個介面:
/**
* 根據id查詢使用者,將引數包含在Request引數
*/
@GetMapping("/feign/userById")
public User finUserById(@RequestParam Long id){
logger.info(">>>>>>>>>>>id:{}<<<<<<<<<<<<<",id);
return userRepository.findOne(id);
}
/**
* 帶有Header資訊的請求,需要注意的是,使用請求頭傳遞引數,如果引數是中文會出現亂碼
* 所以需要使用 URLEncoder.encode(name,"UTF-8") 先編碼
* 後解碼 URLDecoder.decode(name,"UTF-8");
*/
@GetMapping("/feign/header/user")
public User findUserHeader(@RequestHeader String name,@RequestHeader Long id,@RequestHeader Integer age) throws UnsupportedEncodingException {
User user = new User();
user.setId(id);
user.setUsername( URLDecoder.decode(name,"UTF-8"));
user.setAge(age);
logger.info(">>>>>>>>>>>findUserHeader{}<<<<<<<<<<<<<",user);
return user;
}
/***
* 帶有RequestBody以及請求相應體是一個物件的請求
*/
@PostMapping("/feign/insert")
public User insertUser(@RequestBody User user){
userRepository.save(user);
return userRepository.findOne(user.getId());
}
直接將上面新增的介面複製到消費方的Service介面中,刪除方法體。需要注意的是:在SpringMVC中@RequestParam和@RequestHeader註解,如果我們不指定value,則預設採用引數的名字作為其value,但是在Feign中,這個value必須明確指定,否則會報錯。
/**
* 根據id查詢使用者,將引數包含在Request引數
*/
@GetMapping("/feign/userById")
User finUserById(@RequestParam("id") Long id);
/**
* 帶有Header資訊的請求
*/
@GetMapping("/feign/header/user")
User findUserHeader(@RequestHeader("name") String name, @RequestHeader("id") Long id,@RequestHeader("age") Integer age);
/**
* 帶有RequestBody以及請求相應體是一個物件的請求
*/
@PostMapping("/feign/insert")
User insertUser(@RequestBody User user);
測試介面:
@GetMapping("/testFeign")
public void testFeign() throws UnsupportedEncodingException {
User user = userService.finUserById(2L);
logger.info(">>>>>>>>>>>>Request引數:{}>>>>>>>>>>>>>",user);
User user2 = userService.findUserHeader(URLEncoder.encode("嗚嗚嗚嗚","UTF-8"), 3L,1000);
logger.info(">>>>>>>>>>>>Header:{}>>>>>>>>>>>>>",user2);
User save_user = new User(5L,"嘻嘻嘻",56);
User users = userService.insertUser(save_user);
logger.info(">>>>>>>>>>>>RequestBody:{}>>>>>>>>>>>>>",users);
}
繼承特性
在上面的例子中,在服務消費方宣告介面時都是將服務提供方的Controller複製過來。這麼做會出現很多重複程式碼。在SpringCloudFeign中提供了繼承特性來幫助我們解決這些複製操作。
1. 建立建一個基礎的Maven工程,命名service-api,以複用DTO與介面定義。這裡需要用到SpringMVC的註解,所以需要引入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
將上面的User物件複製到api中,並建立UserService
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private Long id;
private String username;
private int age;
}
@RequestMapping("/rafactor")
public interface UserService {
@GetMapping("/feign/user/list")
List<User> findAll();
/**
* 根據id查詢使用者,將引數包含在Request引數
*/
@GetMapping("/feign/userById")
User finUserById(@RequestParam("id") Long id);
/**
* 帶有Header資訊的請求
*/
@GetMapping("/feign/header/user")
User findUserHeader(@RequestHeader("name") String name, @RequestHeader("id") Long id, @RequestHeader("age") Integer age);
/**
* 帶有RequestBody以及請求相應體是一個物件的請求
*/
@PostMapping("/feign/insert")
User insertUser(@RequestBody User user);
}
重構USER-SERVICE,在pom檔案中新增service-api;並建立UserRafactorController類實現service-api的UserService類;
<dependency>
<groupId>com.wqh</groupId>
<artifactId>sevice-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
@RestController
public class UserRafactorController implements UserService{
private final Logger logger = LoggerFactory.getLogger(UserRafactorController.class);
@Autowired
private UserRepository userRepository;
@Override
public List<User> findAll() {
return null;
}
@Override
public User finUserById(Long id) {
logger.info(">>>>>>>>>>>Rafactor id:{}<<<<<<<<<<<<<",id);
com.wqh.user.entity.User one = userRepository.findOne(id);
User user = new User(one.getId(),one.getUsername(),one.getAge());
return user;
}
@Override
public User findUserHeader(@RequestHeader("name")String name, @RequestHeader("id")Long id,@RequestHeader("age") Integer age) {
User user = new User();
user.setId(id);
try {
user.setUsername( URLDecoder.decode(name,"UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
user.setAge(age);
logger.info(">>>>>>>>>>>Rafactor findUserHeader{}<<<<<<<<<<<<<",user);
return user;
}
@Override
public User insertUser(@RequestBody User user) {
logger.info(">>>>>>>>>>>Rafactor RequestBody{}<<<<<<<<<<<<<",user);
return user;
}
}
該類不需要使用@RequestMapping註解來定義請求對映,引數註解需要新增,並且在類上新增@RestController註解。
4. 重構feign-consumer,新增service-api的依賴
<dependency>
<groupId>com.wqh</groupId>
<artifactId>sevice-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
建立UserRafactorService介面繼承UserService介面
@FeignClient(value = "USER-SERVICE")
public interface UserRafactorService extends UserService {
}
測試介面
@GetMapping("/testRafactorService")
public void testRafactorService() throws UnsupportedEncodingException {
com.wqh.api.dto.User user = userRafactorService.finUserById(2L);
logger.info(">>>>>>>>>>>>Rafactor Request引數:{}>>>>>>>>>>>>>",user);
com.wqh.api.dto.User user2 = userRafactorService.findUserHeader(URLEncoder.encode("嗚嗚嗚嗚","UTF-8"), 3L,1000);
logger.info(">>>>>>>>>>>>Rafactor Header:{}>>>>>>>>>>>>>",user2);
com.wqh.api.dto.User save_user = new com.wqh.api.dto.User(5L,"嘻嘻嘻",56);
com.wqh.api.dto.User users = userRafactorService.insertUser(save_user);
logger.info(">>>>>>>>>>>>Rafactor RequestBody:{}>>>>>>>>>>>>>",users);
}
注意:這裡對於物件之間的處理是存在問題,就不詳細的修改了,主要是為了Feign的繼承特性。
Feign配置詳解
Ribbon配置
在Feign中配置Ribbon非常簡單,直接在application.properties中配置即可,如:
# 設定連線超時時間
ribbon.ConnectTimeout=500
# 設定讀取超時時間
ribbon.ReadTimeout=5000
# 對所有操作請求都進行重試
ribbon.OkToRetryOnAllOperations=true
# 切換例項的重試次數
ribbon.MaxAutoRetriesNextServer=2
# 對當前例項的重試次數
ribbon.MaxAutoRetries=1
同樣也可以指定服務配置,直接在application.properties中採用.ribbon.key=value的格式進行配置,如下:
# 設定針對user-service服務的連線超時時間
user-service.ribbon.ConnectTimeout=600
# 設定針對user-service服務的讀取超時時間
user-service.ribbon.ReadTimeout=6000
# 設定針對user-service服務所有操作請求都進行重試
user-service.ribbon.OkToRetryOnAllOperations=true
# 設定針對user-service服務切換例項的重試次數
user-service.ribbon.MaxAutoRetriesNextServer=2
# 設定針對user-service服務的當前例項的重試次數
user-service.ribbon.MaxAutoRetries=1
在SpringCloudFeign中是預設開啟重試機制,從上面的配置資訊也可以看出,我們可以設定重試的次數。對於重試機制的測試,可以讓服務提供方的方法延遲隨機毫秒數來測試。
Hystrix配置
對於Hystrix的配置同樣可以在application.properties中配置,全域性配置直接使用預設字首hystrix.command.default,如
# 設定熔斷超時時間
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
# 關閉Hystrix功能(不要和上面的配置一起使用)
feign.hystrix.enabled=false
# 關閉熔斷功能
hystrix.command.default.execution.timeout.enabled=false
也可以直接對指定的介面進行配置,採用hystrix.command.default.<commandKey>作為字首,比如如/findAllUser:
# 設定熔斷超時時間
hystrix.command.findAllUser.execution.isolation.thread.timeoutInMilliseconds=10000
# 關閉熔斷功能
hystrix.command.findAllUser.execution.timeout.enabled=false
對於重複的介面名會共用這一條Hystrix配置;
禁用Hystrix
上面的配置資訊中,可以通過配置檔案全域性禁用Hystrix也可以指定介面禁用。我們也可以註解屬性的方式禁用Hystrix;
- 構建一個關閉Hystrix的配置類
@Configuration
public class DisableHystrixConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder(){
return Feign.builder();
}
}
在@FeignClient註解中,通過configuration引數引入上面實現的配置
@FeignClient(value = "USER-SERVICE",configuration = DisableHystrixConfiguration.class)
public interface UserRafactorService extends UserService {
}
服務降級配置
在Hystrix中我們可以直接通過@HystrixCommand註解的fallback引數進行配置降級處理方法,然而Feign對其進行封裝,並提供了一種簡單的定義方式:
1. 在之前的feign-consumer服務中建立一個UserServiceFallback類,該類實現UserService介面。這裡對於哪個類介面的降級就實現哪個介面,
@Component
public class UserServiceFallback implements UserService {
@Override
public List<User> findAll() {
return null;
}
@Override
public User finUserById(Long id) {
return new User(-1L,"error",0);
}
@Override
public User findUserHeader(String name, Long id, Integer age) {
return new User(-1L,"error",0);
}
@Override
public User insertUser(User user) {
return new User(-1L,"error",0);
}
}
然後再@FeignClient註解中指定服務降級處理類即可:
@FeignClient(value = "USER-SERVICE",fallback = UserServiceFallback.class)
在配置檔案中開啟Hystrix:
feign:
hystrix:
enabled: true
然後在USER-SERVICE服務中將某個介面設定延遲測試:
請求壓縮
Spring Cloud Feign支援對請求和響應進行GZIP壓縮,以提高通訊效率,配置方式如下:
# 配置請求GZIP壓縮
feign.compression.request.enabled=true
# 配置響應GZIP壓縮
feign.compression.response.enabled=true
# 配置壓縮支援的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
# 配置壓縮資料大小的下限
feign.compression.request.min-request-size=2048
日誌配置
SpringCloudFeign為每一個FeignClient都提供了一個feign.Logger例項。可以根據logging.level.<FeignClient>引數配置格式來開啟Feign客戶端的DEBUG日誌,其中<FeignClient>為Feign客戶端定義介面的完整路徑。如:
logging:
level:
com.wqh.feign.service.UserService: debug
然後再主類中直接加入Looger.Level的Bean
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
這裡也可以通過配置,然後在具體的Feign客戶端來指定配置類實現日誌。
日誌級別有下面4類:
- NONE:不記錄任何資訊;
- BASIC:僅記錄請求方法、URL以及響應狀態碼和執行時間;
- HEADERS:除了記錄BASIC級別的資訊外,還記錄請求和響應的頭資訊;
- FULL:記錄所有請求與響應的明細,包括頭資訊、請求體、元資料等。
原文:https://blog.csdn.net/wqh8522/article/details/79075907
要更多幹貨、技術猛料的孩子,快點拿起手機掃碼關注我,我在這裡等你哦~