SpringCloud(四)——Feign

#全局超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=4000
#hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=2000
#hystrix.command.<commandKey>作为前缀,默认是采用Feign的客户端的方法名字作为标识
hystrix.command.saveStudent.execution.isolation.thread.timeoutInMilliseconds=6000
hystrix.command.queryContents.circuitBreaker.sleepWindowInMilliseconds=20000

hystrix.command.errorMessage.execution.isolation.thread.timeoutInMilliseconds=100000000000

#ribbon.restclient.enabled=true
ribbon.client.name=micro-order
#点对点直连测试配置
# 关闭ribbon访问注册中心Eureka Server发现服务,但是服务依旧会注册。
#true使用eureka false不使用
ribbon.eureka.enabled=true
spring.cloud.loadbalancer.retry.enabled=true
#指定调用的节点
#micro-order.ribbon.listOfServers=localhost:8001
#单位ms ,请求连接超时时间
ribbon.ConnectTimeout=1000
#单位ms ,请求处理的超时时间
ribbon.ReadTimeout=3000

在前面我们调用其他服务的接口时采用的是RestTemplate的方式,其是Spring提供的访问Rest服务的客户端,操作起来也非常的方便。

常用的http调用方式还有HttpClient、OkHttp、HttpURLConnetion等。

这里要介绍的是声明式的REST客户端:Feign组件。

Feign是SpringCloud的一个非常重要的组件,SpringCloud对其进行了封装,且可以与Eureka、Hystrix、Ribbon整合一起使用。通过Feign我们可以非常方便的通过RestAPI的形式调用其他服务的接口。

下面看下如何使用:

一、引入依赖

目前已更名为openfeign:

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

二、启动类开启注解

@EnableFeignClients(clients = {StudentService.class})

这里可以指明接口的类,或是使用basePackages指明要扫描的接口所在包:

@EnableFeignClients(basePackages = "com.wml.feign")

三、使用

我们在order-service服务中定义一些要调用的RestAPI:

@Slf4j
@RestController
public class StudentController{
    @RequestMapping("/feign/student/findByName/{name}")
    public String findByName(@PathVariable("name") String name) {
        return name;
    }

    @RequestMapping("/feign/student/getAllStudent")
    @Override
    public String getAllStudent() {
        List<Student>students=new ArrayList<>();
        Student student;
        for (int i = 0; i < 5; i++) {

            student=new Student();
            student.setId(i);
            student.setName("哈哈"+i);
            students.add(student);
        }
        return JSON.toJSONString(students);
    }

    @RequestMapping("/feign/student/getStudentById")
    @Override
    public String queryStudentById(@RequestParam("id") Integer id) {
        return "id:"+id;
    }

    @RequestMapping("/feign/student/saveStudent")
    @Override
    public String saveStudent(@RequestBody Student student) {
        return JSON.toJSONString(student);
    }
}

方法很简单,但涉及传实体、占位符传参、传统的?传参几种方式。

接着在另一个web服务中声明Feign客户端:

@FeignClient(name = "ORDER-SERVICE",path = "/feign"
        ,fallbackFactory = StudentServiceFallbackFactory.class)
public interface StudentService {

    @GetMapping("/student/getAllStudent")
    String getAllStudent();

    @RequestMapping("/student/findByName/{name}")
    public String findByName(@PathVariable("name") String name);
    @PostMapping("/student/saveStudent")
    String saveStudent(@RequestBody Student student);

    @GetMapping("/student/getStudentById")
    String getStudentById(@RequestParam("id") Integer id);

}

这里作几点说明:

  1. @FeignClient注解表明当前接口是一个Feign客户端接口
  2. name:name属性为要调用的服务名称
  3. path:为一个公共前缀,因为我们要调用的客户端接口都有一个feign前缀,所以可以在这里标明一下,下面的路径中就可以省略掉该部分
  4. fallbackFactory:像前面使用Hystrix一样,Feign的每个接口都相当于添加了一个HystrixCommand注解,fallbackFactory为降级的处理类,该类实现了FallbackFactory<StudentService>接口,可以获取具体异常信息。
  5. 当前Feign客户端直接将要调用的服务的接口定义复制过来即可。

除了使用fallbackFactory,也可以使用fallback = StudentServiceFallback.class的方式,该Fallback类实现了当前接口,但不推荐该方式,因为该方式无法获取具体的异常信息。

Feign降级处理

以下是fallbackFactory的处理类,Feign接口出现异常时就会进行如下的对应方法的回调中:

@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 findByName(String name) {
                log.info("exception=====findByName==========" + 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客户端进行调用了:

@Slf4j
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @RequestMapping("/getAllStudent")
    public String getAllStudent() {
        return studentService.getAllStudent();
    }
    @RequestMapping("/findByName/{name}")
    public String findByName(@PathVariable("name") String name) {
        return studentService.findByName(name);
    }

    @RequestMapping("/getStudentById")
    public String getStudentById(Integer id) {
        return studentService.getStudentById(id);
    }
    @PostMapping("/saveStudent")
    public String saveStudent(@RequestBody Student student) {
        return studentService.saveStudent(student);
    }
}

在这里插入图片描述

在这里插入图片描述

其他几个接口也是可以正常调用的,就不一一展示了。

四、其他配置

4.0 开启熔断器

#feign开启熔断器
feign.hystrix.enabled=true

4.1 压缩

在feign中,我们可以开启GZIP压缩来节约网络资源:

#开启feign的压缩功能
feign.compression.request.enabled=true
feign.compression.response.enabled=true

配置压缩类型和最小压缩值:

feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048

但是压缩配置只有在非使用okHttp时才生效,因为okHttp本身使用GZIP压缩减少传输数据量。

4.2使用OKHTTP

Feign默认使用的HC是JDK 原生的URLConnection

如果要使用okHttp,只要引入其依赖:

<dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>

开启okhttp并关闭httpClient:

feign.httpclient.enabled=false
feign.okhttp.enabled=true

添加配置:

@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
    @Bean
    public okhttp3.OkHttpClient okHttpClient(){
        return new okhttp3.OkHttpClient.Builder()
                 //设置连接超时
                .connectTimeout(60, TimeUnit.SECONDS)
                //设置读超时
                .readTimeout(60, TimeUnit.SECONDS)
                //设置写超时
                .writeTimeout(60,TimeUnit.SECONDS)
                //是否自动重连
                .retryOnConnectionFailure(true)
                .connectionPool(new ConnectionPool())
                //构建OkHttpClient对象
                .build();
    }

}

4.3 使用Apache HttpClient

依赖:

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>

        <dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>8.17.0</version>
        </dependency>

配置:

feign.httpclient.enabled=true

4.4 自定义异常过滤器

该过滤器可以将Feign的异常信息封装成系统的通用异常对象

@Configuration
public class FeignMessageFilter {

    @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;
        }
    }
}

超时配置

#全局超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=4000
#hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=2000
#hystrix.command.<commandKey>作为前缀,默认是采用Feign的客户端的方法名字作为标识
hystrix.command.saveStudent.execution.isolation.thread.timeoutInMilliseconds=6000
hystrix.command.queryContents.circuitBreaker.sleepWindowInMilliseconds=20000
hystrix.command.errorMessage.execution.isolation.thread.timeoutInMilliseconds=100000000000
ribbon.eureka.enabled=true
#单位ms ,请求连接超时时间
ribbon.ConnectTimeout=1000
#单位ms ,请求处理的超时时间
ribbon.ReadTimeout=3000

这里配置了ribbon处理请求的超时为3000,hystrix超时为5000。

如果 hystrix 超时时间大于 ribbon 超时时间,那么就以 hystrix 超时时间为准。

在实际开发中,使用Fiegn跨服务调用可能会出现请求头丢失的情况,这种情况的解决可参考周立老师的方案:

https://www.jianshu.com/p/f30892335057

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值