本系列博文为学习资料,是根据老师讲义,加上自己理解描述整理而成。如有不当之处希望不吝指正,持续更新改正。
Feign是对服务端和客户端通用接口的封装,让代码可以复用做到统一管理。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
import com.xiangxue.jack.service.feign.StudentService;
import com.xiangxue.jack.service.feign.TeacherServiceFeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication(scanBasePackages = {"com.xiangxue.jack"})
//注册到eureka
@EnableEurekaClient
//开启断路器功能
@EnableCircuitBreaker
//开启feign支持,clients指定哪个类开启feign
@EnableFeignClients(clients = {StudentService.class,TeacherServiceFeign.class})
//开启重试功能
//@EnableRetry
public class MicroWebApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
// HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
// factory.setConnectionRequestTimeout(2000);
// factory.setReadTimeout(4000);
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MicroWebApplication.class,args);
}
}
import com.xiangxue.jack.bean.Student;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
/*
* fallback = StudentServiceFallback.class
* 不能获取具体异常
*
* */
@FeignClient(name = "MICRO-ORDER",path = "/feign"
/*fallback = StudentServiceFallback.class,*/
,fallbackFactory = StudentServiceFallbackFactory.class)
public interface StudentService {
@GetMapping("/student/getAllStudent")
String getAllStudent();
@PostMapping("/student/saveStudent")
String saveStudent(@RequestBody Student student);
@GetMapping("/student/getStudentById")
String getStudentById(@RequestParam("id") Integer id);
@GetMapping("/student/errorMessage")
String errorMessage(@RequestParam("id") Integer id);
@GetMapping("/student/queryStudentTimeout")
String queryStudentTimeout(@RequestParam("millis") int millis);
}
import com.xiangxue.jack.bean.Student;
import com.xiangxue.jack.service.feign.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
public class StudentController implements StudentService {
@Autowired
private StudentService studentService;
@RequestMapping("/feign/student/getAllStudent")
@Override
public String getAllStudent() {
return studentService.getAllStudent();
}
@RequestMapping("/feign/student/getStudentById")
@Override
public String queryStudentById(@RequestParam("id") Integer id) {
return studentService.queryStudentById(id);
}
@RequestMapping("/feign/student/saveStudent")
@Override
public String saveStudent(@RequestBody Student student) {
return studentService.saveStudent(student);
}
@RequestMapping("/feign/student/errorMessage")
@Override
public String errorMessage(@RequestParam("id") Integer id) {
return studentService.errorMessage(id);
}
@RequestMapping("/feign/student/queryStudentTimeout")
@Override
public String queryStudentTimeout(@RequestParam("millis") int millis) {
log.info("provider--->" + millis);
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "provider--->" + millis;
}
}
服务端接口必须定义跟feign客户端相同的url。
其他参数
import com.xiangxue.jack.bean.Student;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class StudentServiceFallbackFactory implements FallbackFactory<StudentService> {
@Override
public StudentService create(Throwable throwable) {
if(throwable == null) {
return null;
}
final String msg = throwable.getMessage();
log.info("exception:" + msg);
return new StudentService() {
@Override
public String getAllStudent() {
log.info("exception=====getAllStudent==========" + msg);
return msg;
}
@Override
public String saveStudent(Student student) {
log.info("exception=====saveStudent==========" + msg);
return msg;
}
@Override
public String getStudentById(Integer id) {
log.info("exception=====getStudentById==========" + msg);
return msg;
}
@Override
public String errorMessage(Integer id) {
log.info("exception=====errorMessage==========" + msg);
return msg;
}
@Override
public String queryStudentTimeout(int millis) {
log.info("exception=====queryStudentTimeout==========" + msg);
return msg;
}
};
}
}
这里在调用对应feign客户端方法出现异常了,就会回调到create方法中,最终会回调到对应的客户端方法中。
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration
public class FeignErrMessageFilter {
@Bean
public ErrorDecoder errorDecoder() {
return new FeignErrorDecoder();
}
/*
* 当调用服务时,如果服务返回的状态码不是200,就会进入到Feign的ErrorDecoder中
* {"timestamp":"2020-02-17T14:01:18.080+0000","status":500,"error":"Internal Server Error","message":"/ by zero","path":"/feign/student/errorMessage"}
* 只有这种方式才能获取所有的被feign包装过的异常信息
*
* 这里如果创建的Exception是HystrixBadRequestException
* 则不会走熔断逻辑,不记入熔断统计
* */
class FeignErrorDecoder implements ErrorDecoder {
private Logger logger = LoggerFactory.getLogger(FeignErrorDecoder.class);
@Override
public Exception decode(String s, Response response) {
RuntimeException runtimeException = null;
try {
String retMsg = Util.toString(response.body().asReader());
logger.info(retMsg);
runtimeException = new RuntimeException(retMsg);
} catch (IOException e) {
e.printStackTrace();
}
return runtimeException;
}
}
}
过滤器把异常返回后,feign前面定义的降级方法就会调到create方法。
注:Feign 我不建议大家使用,流程简单并发不高的方法可以用一用。
这个讲义自己没有吃透,练习代码就暂时不上传,后续补上。