前面使用了Ribbon作客戶端負載均衡,使用Hystrix作容錯保護,這二者被做爲基礎工具類框架被普遍地應用在各個微服務的實現中。SpringCloudFeign是將二者作了更高層次的封裝以簡化開發。它基於Netfix Feign實現,整合了SpringCloudRibbon和SpringCloudHystrix,除了提供這二者的強大功能外,還提供了一種聲明是的Web服務客戶端定義的方式。SpringCloudFeign在NetFixFeign的基礎上擴展了對SpringMVC註解的支持,在其實現下,咱們只需建立一個接口並用註解的方式來配置它,便可完成對服務提供方的接口綁定。簡化了SpringCloudRibbon自行封裝服務調用客戶端的開發量。
接着以前的代碼:SpringCloud容錯保護Hystrix(二)
代碼地址:https://gitee.com/wqh3520
1.建立一個SpringBoot工程,這裏命名爲feign-consumer,而後在pom文件中添加依賴:java
<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的支持功能git
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } }
3.接口定義:咱們這裏調用USER-SERVICE服務,在該服務中建立一個查詢全部用戶的接口,而後在feign-consumer中定義。
USER-SERVICEweb
@RestController public class UserFeignController { @Autowired private UserRepository userRepository; @GetMapping("/feign/user/list") public List<User> findAllUser(){ return userRepository.findAll(); } }
feign-consumerspring
@FeignClient(value = "USER-SERVICE") public interface UserService { @GetMapping("/feign/user/list") List<User> findAll(); }
使用@FeignClient
註解指定服務名來綁定服務,若是不指定服務名,啓動項目將會報錯。而後建立一個接口與調用普通的service同樣調用UserService。json
@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
接口,能夠正常返回數據。api
在實際開發中,像上面那種不帶參數的接口可能少之又少。Feign提供了多種參數綁定的方式。
在服務提供的UserFeignController
中添加如下三個接口:app
/** * 根據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中提供了繼承特性來幫助咱們解決這些複製操做。ide
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
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); }
<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註解。
<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很是簡單,直接在application.properties中配置便可,如:
# 設置鏈接超時時間 ribbon.ConnectTimeout=500 # 設置讀取超時時間 ribbon.ReadTimeout=5000 # 對全部操做請求都進行重試 ribbon.OkToRetryOnAllOperations=true # 切換實例的重試次數 ribbon.MaxAutoRetriesNextServer=2 # 對當前實例的重試次數 ribbon.MaxAutoRetries=1
一樣也能夠指定服務配置,直接在application.properties中採用<client>.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的配置一樣能夠在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;
@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對其進行封裝,並提供了一種簡單的定義方式:
@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(value = "USER-SERVICE",fallback = UserServiceFallback.class)
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類:
做爲SpringCloud學習筆記,有不少地方很差。望指出!!!