The previous blog post built an Eureka+Ribbon+Hystrix framework. Although it can basically meet calls between services, the code looks really ugly. The client has to write a restTemplate every time. In order to make the calls more beautiful and readable, we are now learning to use Feign.
Feign includes Ribbon and Hystrix, which is gradually experiencing its significance in actual combat. The so-called inclusion is not the physical inclusion of Feign's jar packages including Ribbon and Hystrix's jar packages, but the logical inclusion of Feign's functions including the functions of the other two. In short: Feign can do things about Ribbon and Hystrix, but to use the annotations brought by Ribbon and Hystrix, the corresponding jar package must be introduced.
Case 1:
Eureka Registration Center: https://github.com/yejingtao/forblog/tree/master/demo-eureka-register
Service provider: https://github.com/yejingtao/forblog/tree/master/demo-feign-freeservice
Service caller: https://github.com/yejingtao/forblog/tree/master/demo-feign-freeconsumer
The service provider is a simple EurekaClient+web application, providing the following methods
@RestController @RequestMapping("/feign-service") public class HelloServiceContorller { private Logger logger = LoggerFactory.getLogger(this.getClass()); private void sleep(String methodName) { int sleepMinTime = new Random().nextInt(3000); logger.info("helloService "+methodName+" sleepMinTime: "+sleepMinTime); try { Thread.sleep(sleepMinTime); } catch (InterruptedException e) { e.printStackTrace(); } } @RequestMapping(value="/serviceGet",method=RequestMethod.GET) public String helloService(@RequestParam String name) { sleep("get"); return "HelloServiceImpl name :"+name; } @RequestMapping(value="/serviceHead", method=RequestMethod.HEAD) public String helloService(@RequestHeader String name, @RequestHeader String password) { sleep("header"); return "helloServiceHead name :"+name +" password:"+password; } @RequestMapping(value="/servicePost", method=RequestMethod.POST) public String helloService(@RequestBody UserDemo userDemo) { sleep("post"); return userDemo.toString(); } } The following annotations that need to be paid attention to cannot be omitted.
@RequestParam: Annotation which indicates that method parameter should be bound to a web request parameter
@RequestBody: Annotation indicating a methodparameter should be bound to the body of the web request.
@RequestHeader: Annotation which indicates that method parameter should be bound to a web request header.
If the above annotations are missing, although the service will not report an error after it is run, it cannot obtain the entry parameters.
Service caller project:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
Feign is only reliant here, not Ribbon and Hystrix.
application.yml:
server: port: 9051 spring: application: name: demo-feign-freeconsumer eureka: client: serviceUrl: defaultZone: http://peer1:1111/eureka/,http://peer2:1112/eureka/ feign: hystrix: enabled: true #Ribbon Timeout Setting#ribbon: # ConnectTimeout: 500 # ReadTimeout: 3000
The configuration of hystrix has tricked me for a long time. The Spring Cloud I used is Dalston version SR1, which is newer than the versions of other materials on the Internet. Because in the new version, Feign's support for Hystrix is turned off by default, so you have to manually turn on feign.hystrix.enabled=true through configuration, so that the service downgrade and other functions will be effective.
Application Starter Program
@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class DemoFeignApplication { public static void main(String[] args) { SpringApplication.run(DemoFeignApplication.class, args); } } Note that there is another pitfall here. I am using @SpringBootApplication+@EnableEurekaClient instead of @SpringCloudApplication, because the latter contains @EnableCircuitBreaker, and @EnableCircuitBreaker is the content in the Hystrix package. My pom does not introduce Hystrix. So Spring Cloud still has shortcomings in this regard. You will not report an error by directly compiling with @SpringCloudApplication, but it cannot be started. Of course, the protagonist here is still the annotation @EnableFeignClients.
Core client code
@FeignClient(name="demo-feign-freeservice",fallback=DemoFeignFallback.class) public interface DemoFeignService{ @RequestMapping(value="/feign-service/serviceGet",method=RequestMethod.GET) String helloService(@RequestParam("name") String name); @RequestMapping(value="/feign-service/serviceHead", method=RequestMethod.HEAD) String helloService(@RequestHeader("name") String name, @RequestHeader("password") String password); @RequestMapping(value="/feign-service/servicePost", method=RequestMethod.POST) String helloService(@RequestBody UserDemo userDemo); } The @FeignClient annotation defines that the interface is a Feign client, name specifies the service name registered on Eureka, and fallback is the interface implementation class after the service is downgraded.
@RequestMapping specifies the relative url and http request methods of the request, which corresponds to the server one by one. @RequestParam,
@RequestBody and @RequestHeader annotations have more value attributes than servers. They cannot be omitted here. They need to explicitly inform the Feign client how to correspond to the parameters.
Downgrade service code:
@Component public class DemoFeignFallback implements DemoFeignService{ @Override public String helloService(String name) { return "get error"; } @Override public String helloService(String name,String password) { return "head error"; } @Override public String helloService(UserDemo userDemo) { return "post error"; } } I found that I deliberately removed the @RequestParam, @RequestBody, and @RequestHeader annotations in the entry parameters here, because the essential significance of these annotations is that Feign uses to pass parameters to http when making microservice calls, but the service downgrade will not make http requests at all, so it can be omitted here.
Controller code:
@RestController public class DemoFeignController { @Autowired private DemoFeignService demoFeignService; @RequestMapping(value="/test", method=RequestMethod.GET) public String demoServiceTest() { StringBuffer sb = new StringBuffer(); sb.append(demoFeignService.helloService("yuanyuan")); sb.append("/n"); sb.append(demoFeignService.helloService("yjt","xixihaha")); sb.append("/n"); sb.append(demoFeignService.helloService(new UserDemo("yejingtao","123456"))); return sb.toString(); } }Let's see the effect:
Our service did not time out, all three methods were normal, but the head request did not get the return value. This is determined by the characteristics of the head method http request. The head does not return the body body of the response, and is generally used for connectivity testing.
Let's look at another set:
The head and post request methods have been processed for more than 2000ms, and the service is downgraded, and the implementation is replaced by the fallback processing class.
In case one, we always have a feeling that there is duplicate code between the service provider and the service caller. Can it be optimized? Please see Case 2.
Case 2:
Eureka Registration Center: https://github.com/yejingtao/forblog/tree/master/demo-eureka-register
Interface API: https://github.com/yejingtao/forblog/tree/master/demo-feign-serviceapi
Service provider: https://github.com/yejingtao/forblog/tree/master/demo-feign-serviceimpl
Service caller: https://github.com/yejingtao/forblog/tree/master/demo-feign-apicconsumer
The biggest change in Case 2 is to write service capabilities separately into an API project, and both the caller and the provider pom rely on this API.
API:
public interface HelloService { @RequestMapping(value="/feign-service/serviceGet",method=RequestMethod.GET) String helloService(@RequestParam("name") String name); @RequestMapping(value="/feign-service/serviceHead", method=RequestMethod.HEAD) String helloService(@RequestHeader("name") String name, @RequestHeader("password") String password); @RequestMapping(value="/feign-service/servicePost", method=RequestMethod.POST) String helloService(@RequestBody UserDemo userDemo); } Service Provider:
@RestController public class HelloServiceContorller implements HelloService{ private Logger logger = LoggerFactory.getLogger(this.getClass()); private void sleep(String methodName) { int sleepMinTime = new Random().nextInt(3000); logger.info("helloService "+methodName+" sleepMinTime: "+sleepMinTime); try { Thread.sleep(sleepMinTime); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String helloService(@RequestParam("name") String name) { sleep("get"); return "HelloServiceImpl name :"+name; } @Override public String helloService(@RequestHeader("name") String name, @RequestHeader("password") String password) { sleep("header"); return "helloServiceHead name :"+name +" password:"+password; } @Override public String helloService(@RequestBody UserDemo userDemo) { sleep("post"); return userDemo.toString(); } } Service caller:
@FeignClient(name="demo-feign-serviceimpl", fallback=FeignServiceFallback.class) public interface FeignService extends HelloService{ }Other codes remain basically unchanged, and the effect is the same.
The two styles have their own advantages and disadvantages: freestyle is more free, and the new method added to the server will not affect the client code. The disadvantage is that service capabilities are not synchronized and changes in service capabilities will cause abnormalities; the API format server client service capabilities are synchronized, but the changes in the interface require modification of the code on both sides, and you must consider it clearly when building them.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.