• SpringCloud OpenFeign(服务调用)


    1.定义

    Feign是一个声明式的Web服务客户端,是面向接口编程的。也就是说使用Feign,只需要创建一个接口并使用注解方式配置它,就可以完成对微服务提供方的接口绑定。

    OpenFeign对feign进行进一步的封装,添加了springmvc的一些功能,更加强大。

    在使用RestTemplate时,每次调用服务都需要指定服务的具体路径,当在多个地方同时使用时要写多次,显得代码冗余也难以维护,而openfeign就可以避免这种操作。

    2.项目实战

    源码:https://github.com/zhongyushi-git/cloud-open-feign-demo.git

    2.1基础环境搭建

    这里使用consul作为服务注册中心。

    1)创建一个maven工程名为cloud-open-feign-demo,删除src目录

    2)在pom中导入依赖,对SpringBoot和SpringCloud版本进行锁定

     <properties>
            <spring.boot.version>2.2.2.RELEASE</spring.boot.version>
            <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
            <lombok.version>1.16.18</lombok.version>
        </properties>
    
        <!--  依赖管理,父工程锁定版本-->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring.boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring.cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
               <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>

    2.2搭建公共模块

    1)新建maven子模块(common-api),导入依赖

        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
        </dependencies>

    2)创建实体对象

    package com.zys.cloud.entity;
    
    import lombok.Data;
    
    @Data
    public class User {
    
        private String name;
    
        private String username;
    
        private Integer age;
    }

    2.3搭建服务提供者

    1)新建maven子模块(cloud-provider8001),导入依赖

         <dependencies>
            <!--web-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>

    2)新建启动类ProviderMain8001并添加注解

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class ProviderMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(ProviderMain8001.class, args);
        }
    }

    3)配置application.yml

    server:
      port: 8001
    
    spring:
      application:
        name: cloud-consul-provider
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}

    4)新建controller接口

    package com.zys.cloud.controller;
    
    import com.zys.cloud.entity.User;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    public class UserController {
    
        @Value("${server.port}")
        private String port;
    
        @GetMapping("/user/get")
        public String get() {
            return "我是服务提供者,端口:" + port;
        }
    
        @GetMapping("/user/getParam")
        public String getParam(@RequestParam("name") String name) {
            return "我是服务提供者,参数:" + name + ",端口:" + port;
        }
    
        @PostMapping("/user/postParam")
        public String postParam(@RequestParam("username") String username) {
            return "我是服务提供者,参数:" + username + ",端口:" + port;
        }
    
        @PostMapping("/user/add")
        public String addUser(@RequestBody User user) {
            return "我是服务提供者,参数:" + user.toString() + ",端口:" + port;
        }
    
    
    }

    这里的接口并没有在类上使用@RequestMapping注解,而是把接口路径都写在方法上面,那么在服务调用方进行映射时直接复制其方法名即可,不需要方法体。

    5)启动服务,可看到已注册到consul。

    6)根据服务提供者ProviderMain8001,再创建ProviderMain8002.

    2.4搭建服务消费者

    1)新建maven子模块(cloud-consumer80),导入依赖

        <dependencies>
            <!--web-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--feign-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
            <dependency>
                <groupId>com.zys.cloud</groupId>
                <artifactId>common-api</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>

    2)新建启动类ConsumerMain80并添加注解

    package com.zys.cloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    class ConsumerMain80 {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerMain80.class, args);
        }
    }

    要在启动类上启用Feign客户端。

    3)配置application.yml

    server:
      port: 80
    
    spring:
      application:
        name: cloud-consul-consumer
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}

    3)创建服务接口UserServiceClient,对于服务提供者接口。需要添加注解@FeiginClient,指定微服务的名称

    package com.zys.cloud.service;
    
    import com.zys.cloud.entity.User;
    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;
    
    //指定微服务名称
    @FeignClient(value = "cloud-consul-provider")
    public interface UserServiceClient {
    
        @GetMapping("/user/get")
        String get();
    
        @GetMapping("/user/getParam")
        String getParam(@RequestParam("name") String name);
    
        @PostMapping("/user/postParam")
        String postParam(@RequestParam("username") String username);
    
        @PostMapping("/user/add")
        String addUser(@RequestBody User user);
    }

    注意:

    •  必须使用注解@FeignClient指定服务提供方的服务名称
    •  在编写接口映射时,可直接复制服务提供方的方法名等信息
    •  get请求传递参数时,要使用@RequestParam注解,其value是参数的名字,需与服务提供者端保持一致
    •  post请求传递参数时,当参数是对象时使用@RequestBody,当参数不是对象时需使用@RequestParam注解

    4)创建controller接口,将UserServiceClient注入使用

    package com.zys.cloud.controller;
    
    import com.zys.cloud.entity.User;
    import com.zys.cloud.service.UserServiceClient;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    
    @RestController
    @RequestMapping("/consumer")
    public class TestController {
    
        @Resource
        private UserServiceClient userServiceClient;
    
        @GetMapping("/get")
        public String get() {
            return userServiceClient.get();
        }
    
        @GetMapping("/param")
        public String getParam(String name) {
            return userServiceClient.getParam(name);
        }
    
        @PostMapping("/post")
        public String postParam(String username) {
            return userServiceClient.postParam(username);
        }
    
        @PostMapping("/add")
        public String addUser(@RequestBody User user) {
            return userServiceClient.addUser(user);
        }
    }

    5)启动测试。先启动服务提供者集群,再启动服务消费者,对四个接口进行测试,服务均可正常调用。

    另外对其中一个接口进行多次刷新,会发现服务提供者集群是遵循轮询机制。原因是openfeignl默认已引入了Ribbon,可提供负载均衡策略。

    2.5超时控制

    为了体现服务快速响应的特点,Feign默认等待1秒,超过后报错。也就是说Feigin客户端只等待一秒,若服务端处理过程超过一秒,会导致客户端会出错,故需设置超时时间,避免出现这样的情况。

    1)情景重现

    为了看到这种效果,可在服务提供者的接口中设置线程阻塞,让其响应时间超过1S。这里以8001为例,修改UserController的get()方法:

        @GetMapping("/user/get")
        public String get() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "我是服务提供者,端口:" + port;
        }

    截图:

    重启后测试这个接口,消费者控制台报错:

    2)配置超时时间

    在客户端(服务消费者80)的yml进行配置即可。配置有两种方式:

    1)方式一:设置Ribbon的负载超时时间

    #设置ribbon的负载超时时间,单位都是ms
    ribbon:
      #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
      ReadTimeout: 5000
      #指的是建立连接后从服务器读取到可用资源所用的时间
      ConnectTimeout: 5000

    2)方式二:设置feign的超时时间

    feign:
      client:
        config:
          #指定全局
          default:
            #连接超时时间
            connectTimeout: 5000
            #服务等待时间
            readTimeout: 5000

    上述是配置全局的,也可对每个服务设置超时时间:

    使用上述任意一种配置,配置后重启客户端后测试这个接口,服务正常调用,没有错误。

    2.6日志打印

    Feign对日志的处理非常灵活,可为每个Feign客户端指定日志记录策略。每个客户端都会创建一个Logger,默认情况下Feign的日志是Debug级别的,故不会显示。

    2.6.1日志级别类型

    NONE:默认的,不显示任何日志
    BASIC:仅记录请求方法、URL、响应状态码及执行时间
    HEADERS:BASIC信息以及请求和响应的头信息	
    FULL:HEADERS信息以及请求和响应的正文和元数据
    

    2.6.2配置日志

    配置日志有两种方式,二选一即可。

    1)第一种方式:使用配置类+yml配置

    第一步:在客户端(服务消费者80)添加配置类LogConfig,指定日志的级别

    package com.zys.cloud.config;
    
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    //配置feign日志级别
    @Configuration
    public class LogConfig {
        @Bean
        Logger.Level feignLevel(){
            return Logger.Level.FULL;
        }
    }

    第二步:在yml配置指定调用的客户端的日志级别,必须是Debug级别

    logging:
      level:
        com.zys.cloud.service.UserServiceClient: debug

    2)第二种方式:全部使用yml配置

    配置如下:


    feign:
    client:
    config:
    #指定全局
    default:
    loggerLevel: full

    logging:
    level:
    com.zys.cloud.service.UserServiceClient: debug

    当然,上述使用default指定了全局的级别,也可以对每个服务指定级别,把default换成服务名即可:

    配置完成后重启客户端,调用四个接口的任意一个,在控制台可以看出多了很多的打印信息。

    2.7文件上传

    在使用openfeign进行文件上传时,需要特别注意,不能使用默认方式。

    //import org.springframework.http.MediaType;
    
    //文件上传
    @PostMapping(value = "/user/fileUpload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String fileUpload(@RequestPart("file")MultipartFile file);

    调用的关键代码如上。必须在请求中指定consumes的值是MediaType.MULTIPART_FORM_DATA_VALUE,另外传递的参数需要使用@RequestPart注解来修饰MultipartFile.

    就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !
  • 相关阅读:
    《CoderXiaoban》第八次团队作业:Alpha冲刺5
    《CoderXiaoban》第八次团队作业:Alpha冲刺4
    《CoderXiaoban》第八次团队作业:Alpha冲刺 3
    《CoderXiaoban》第八次团队作业:Alpha冲刺 2
    《CoderXiaoban》第八次团队作业:Alpha冲刺1
    毛毛虫组【Beta】Scrum Meeting 3
    毛毛虫组【Beta】Scrum Meeting 2
    毛毛虫组【Beta】Scrum Meeting 1
    《毛毛虫团队》第九次团队作业:BETA冲刺与团队项目验收
    《毛毛虫组》【Alpha】Scrum meeting 5
  • 原文地址:https://www.cnblogs.com/zys2019/p/12638166.html
Copyright © 2020-2023  润新知