Spring Cloud Feign(声明式服务调用)(2)

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37598011/article/details/82818387

继承特性

1.首先新建个工程,名为hello-service-api。因为要用到Spring MVC的注解所以要要加锁spring-boot-web的依赖,具体如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>hello-service-api</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>hello-service-api</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

2.因为在服务提供者和服务消费者中需要用到User这个类,所以将其提取出来放置到新建的工程中。

3.创建HelloService接口:

@RequestMapping("/refactor")
public interface HelloService {

    @RequestMapping("/hello")
    public String hello();

    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
    String hello(@RequestParam("name") String name);

    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
    Map<String,Object> hello(@RequestHeader("name") String name, @RequestHeader("author") String author, @RequestHeader("price") Integer price);

    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
    String hello(@RequestBody Map<String, Object> book);

    @RequestMapping(value = "/hello4", method = RequestMethod.POST)
    String hello(@RequestBody User user);
}

最后的目录结构:

然后需要对hello-service(服务提供者)和feign-consumer(服务调用)进行重构:

4.首先需要在各自的pom.xml文件中引入对hello-service-api的依赖:

        <dependency>
            <groupId>com.example</groupId>
            <artifactId>hello-service-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

5.重写服务提供者的控制器,改为:


@RestController
@RequestMapping("/refactor")
public class HelloController {

    private final Logger logger=Logger.getLogger(getClass());

    @RequestMapping("/hello")
    public String index() throws InterruptedException {
//        int sleepTime=new Random().nextInt(3000);
//        logger.info("sleep:"+sleepTime);
//        Thread.sleep(sleepTime);
        logger.info(new Date());
        return "Hello"+new Date()+"---"+new Random().nextInt();
    }


    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
    public String hello1(@RequestParam String name) {
        return "hello " + name + "!";
    }

    @RequestMapping(value = "/hello2", method = RequestMethod.GET)
    public Map<String,Object> hello2(@RequestHeader String name, @RequestHeader String author, @RequestHeader Integer price) throws UnsupportedEncodingException {
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("name",URLDecoder.decode(name));
        map.put("author",URLDecoder.decode(author));
        return map;
    }


    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
    public String hello3(@RequestBody Map<String,Object> book) {
        return "书名为:" + book.get("name") + ";作者为:" + book.get("author");
    }

    @RequestMapping(value = "/hello4", method = RequestMethod.POST)
    public String hello4(@RequestBody User user) {
        return "用户名:" + user.getName() + ";年龄为:" + user.getAge();
    }

}

需要注意的是在原先的控制器上新增了@RequestMapping("/refactor"),要跟hello-service-api上的RequestMapping相同,这里也可以用实现(implements)的方式实现。

6.创建HelloServiceImpl实现HelloService接口并添加@FeignClient注解绑定服务。

@FeignClient("hello-service")//用于绑定名为hello-service服务
public interface HelloServiceImpl extends HelloService {
}

7.修改其控制器如:

@RestController
public class ConsumerController {

    //    @Autowired
//    HelloService helloService;
    @Autowired
    HelloServiceImpl helloService;

    @GetMapping("/feign-consumer")
    public String helloConsumer() {
        return helloService.hello();
    }

    @RequestMapping("/hello1")
    public String hello1() {
        return helloService.hello("张三");
    }

    @RequestMapping(value = "/hello2")
    public Map<String, Object> hello2() throws UnsupportedEncodingException {
        Map<String, Object> book = helloService.hello(URLEncoder.encode("三国演义", "UTF-8"), URLEncoder.encode("罗贯中", "UTF-8"), 33);
        System.out.println(book);
        return book;
    }

    @RequestMapping("/hello3")
    public String hello3() {
        Map<String, Object> book = new HashMap<String, Object>();
        book.put("name", "三国演义");
        book.put("author", "罗贯中");
        return helloService.hello(book);
    }

    @RequestMapping("/hello4")
    public String hello4() {
        User user = new User("刘德华", 99);
        return helloService.hello(user);
    }

}

OK这就是Feign的继承特性

Ribbon配置

由于Spring Cloud Feign的客户端负载均衡是通过Spring Cloud Ribbon实现,所以我们可以直接通过配置Ribbon客户端方式来自定义各个服务客户端调用的参数。

全局配置

全局配置的方式十分容易,如同Ribbon的配置一样可以直接使用ribbon.<key>=<value>的方式来设置ribbon的各项默认参数。如修改客户端调用超时时间:

ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000

指定服务设置

在使用Spring Cloud Feign的时候,针对各个服务客户端进行个性化配置的方式与使用Spring Cloud Ribbon时的配置方式是一样的,都采用<client>.ribbon.key=value的格式进行设置。

在定义Feign客户端时,我们使用了@FeignClient注解。在初始化过程中,Spring Cloud Feign会根据该注解的name属性或value属性指定的服务名,自动创建一个同名的Ribbon客户端。在上面的代码中使用的@FeignClient("hello-service")这时的application.properties的配置如:

# 设置针对hello-service服务的连接超时时间
hello-service.ribbon.ConnectTimeout=600
# 设置针对hello-service服务的读取超时时间
hello-service.ribbon.ReadTimeout=6000
# 设置针对hello-service服务所有操作请求都进行重试
hello-service.ribbon.OkToRetryOnAllOperations=true
# 设置针对hello-service服务切换实例的重试次数
hello-service.ribbon.MaxAutoRetriesNextServer=2
# 设置针对hello-service服务的当前实例的重试次数
hello-service.ribbon.MaxAutoRetries=1

Hystrix配置

在Spring Cloud Feign中除了引入了客户端负载均衡的Spring Cloud Ribbon之外,还引入了Hystrix。默认情况下Spring Cloud Feign会为所有Feign客户端方法都封装到Hystrix命令中进行服务保护。

全局配置

对于Hystrix的全局配置同Spring Cloud Ribbon的全局配置一样,直接使用它的默认配置前缀hystrix.command.default进行设置,如设置全局超时时间:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000

另外对Hystrix进行配置之前,我们需要确认feign.hystrix.enabled参数没有被设置为false,否则该参数设置会关闭Feign客户端的Hystrix支持。feign.hystrix.enabled=false可以关闭Hystrix功能,hystrix.command.default.execution.timeout.enabled=false关闭熔断功能。

禁用Hystrix

除了feign.hystrix.enabled=false可以关闭Hystrix功能,如果想针对某个服务客户端关闭Hystrix支持可以使用@Scope("prototype")注解为指定的客户端配置Feign.Builder实例。

@Configuration
public class DisableHystrixConfiguration{


    @Bean
    @Scope("prototype")
    public Builder feignBuilder(){
        return Feign.builder();
    }
}

在HelloService中的@FeignClient注解中,通过configuration参数引入上面实现的配置。

@FeignClient(name="hello-service",configuration=DisableHystrixConfiguration.class)

指定的命令配置

指定命令配置方法也跟传统的Hystrix 命令的参数配置相似,采用hystrix.command.<commandkey>作为前缀,而默认情况下会采用Feign客户端中的方法名作为标识,比如, 针对hello接口的熔断超时时间的配置可以通过其方法名作为来进行配置, 具体如下:

hystrix.command.hello.execution.isolation.thread.timeoutinMilliseconds=5OOO 

在使用指定命令配置的时候, 需要注意, 由于方法名很有可能重复, 这个时候相同方法名的Hystrix配置会共用,所以在进行方法定义与配置的时候需要做好一定的规划。当然,也可以重写Feign.Builder的实现,并在应用主类中创建它的实例来覆盖自动化配置的HystrixFeign.Builder实现

服务降级配置

由于Spring Cloud Feign在定义服务客户端的时候与Spring Cloud Ribbon有很大差别,HystrixCommand定义被封装了起来, 我们无法像之前介绍Spring Cloud Hystrix时, 通过@HystrixCommand注解的fallback参数那样来指定具体的服务降级处理方法。但是, Spring Cloud Feign提供了另外一种简单的定义方式,如下: 

1.继承HelloService类,实现服务降级逻辑:

@Component
@RequestMapping("fallback/refactor")//必须要加,不然会报bean创建异常
public class HelloServiceFallbackImpl implements HelloService {
    @Override
    public String hello() {
        return "error";
    }

    @Override
    public String hello(@RequestParam("name") String name) {
        return "error1";
    }

    @Override
    public Map<String, Object> hello(@RequestHeader("name") String name, @RequestHeader("author") String author, @RequestHeader("price") Integer price) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("error", "未知");
        return map;
    }

    @Override
    public String hello(@RequestBody Map<String, Object> book) {
        return "error3";
    }

    @Override
    public String hello(@RequestBody User user) {
        return "error4";
    }
}

2.通过FeignClient注解的fallback属性来指定对应的服务降级实现类 

@RequestMapping("/refactor")
@FeignClient(name = "hello-service",fallback = HelloServiceFallbackImpl.class)//用于绑定名为hello-service服务 ,fallback = HelloServiceFallbackImpl.class
public interface HelloService {

    @RequestMapping("/hello")
    public String hello();

    @RequestMapping(value = "/hello1", method = RequestMethod.GET)
    String hello(@RequestParam("name") String name);

    @RequestMapping(value = "/hello2", method = RequestMethod.GET,produces = {"application/json;charset-UTF-8"})
    Map<String,Object> hello(@RequestHeader("name") String name, @RequestHeader("author") String author, @RequestHeader("price") Integer price);

    @RequestMapping(value = "/hello3", method = RequestMethod.POST)
    String hello(@RequestBody Map<String,Object> book);

    @RequestMapping(value = "/hello4", method = RequestMethod.POST)
    String hello(@RequestBody User user);
}

3.修改application.properties开启服务降级功能

spring.application.name=feign-consumer
server.port=9001

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

feign.hystrix.enabled=true

并且修改Hello-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

日志配置

Spring Cloud Feign在构建被@FeignClient注解修饰的客户端服务时,会为每一个客户端都创建一个feign.Logger实例。开启方式如下:

logging.level.com.example.feign.consumer.service.HelloService=DEBUG

但是由于Feign客户端默认的Logger.Level对象定义为NONE级别,该级别不会记录任何Feign调用过程中的信息。所以要在应用主类中加入Logger.Level的Bean创建:

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class FeignConsumerApplication {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
    
    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApplication.class, args);
    }
}

Logger级别主要有4类:

NONE:不记录任何信息

BASIC:仅记录请求方法,URL以及响应吗和执行时间。

HEADERS:除了记录BASIC级别的信息外,还会记录请求和响应的头信息。

FULL:记录所有请求与响应的明细,包括头信息,请求体,元数据等。

参考:《Spring Cloud 微服务实战》

猜你喜欢

转载自blog.csdn.net/qq_37598011/article/details/82818387