03.`feign`→服务间的通信

2020-09-09  本文已影响0人  风安峻_
1. 依赖
  1. spring-cloud-starter-openfeign依赖

    <dependencies>
        <!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
    
  2. 说明

    1. 消费者添加spring-cloud-starter-openfeign的依赖即可,提供者不需要

    2. spring-cloud-starter-openfeign的父依赖

      <properties>
          <spring-cloud.version>Hoxton.SR7</spring-cloud.version>
      </properties>
      
      <dependencyManagement>
          <dependencies>
              <!-- spring-cloud 父依赖 -->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-dependencies</artifactId>
                  <version>${spring-cloud.version}</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
      
    3. 提供者注册到注册中心,消费者要从注册中心拉取提供者信息

      <properties>
           <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
      </properties>
      
      <dependencies>
          <!-- nacos 注册中心 -->
          <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
          </dependency>
      </dependencies>
      
      <dependencyManagement>
          <!-- spring-cloud-alibaba 父依赖 -->
          <dependency>
              <groupId>com.alibaba.cloud</groupId>
              <artifactId>spring-cloud-alibaba-dependencies</artifactId>
              <version>${spring-cloud-alibaba.version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencyManagement>    
      
2. 配置
  1. feign-provider提供者

    1. application.yml主配置文件

      server:
        port: 8081
        servlet:
          # 提供者一般不设置前缀,这是为了演示
          context-path: /api/v1
      spring:
        application:
          # 注册到注册中心的服务名
          name: feign-provider
        cloud:
          nacos:
            discovery:
              # nacos注册中心的地址
              server-addr: 120.25.207.44:8848
              cluster-name: ${spring.application.name}
      
    2. 启动类

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
      
      // 开启 nacos 注册中心扫描,发现该服务
      @EnableDiscoveryClient
      @SpringBootApplication
      public class FeignProviderApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(FeignProviderApplication.class, args);
          }
      
      }
      
  2. feign-consumer消费者

    1. application.yml主配置文件

      server:
        port: 8082
      spring:
        application:
          # 注册到注册中心的服务名,不注册到注册中心可以不写
          name: feign-consumer
        cloud:
          nacos:
            discovery:
              # nacos 注册中心的地址
              server-addr: 120.25.207.44:8848
              # 不将自己注册到注册中心
              register-enabled: false
      feign:
        okhttp:
          # feign 启用 okhttp,性能最好
          enabled: true
      
    2. 启动类

      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
      import org.springframework.cloud.openfeign.EnableFeignClients;
      
      // 启动 feign 客户端
      @EnableFeignClients
      // 开启 nacos 注册中心扫描,发现该服务
      @EnableDiscoveryClient
      @SpringBootApplication
      public class FeignConsumerApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(FeignConsumerApplication.class, args);
          }
      
      }
      
      • @EnableFeignClients启动feign客户端
      • @EnableDiscoveryClient开启注册中心扫描,发现该服务
3. feign组件的使用
  1. feign-consumer消费者的远程调用类中用@FeignClient注解标记

    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Service;
    
    @Service
    @FeignClient(value = "feign-provider", path = "/api/v1/provider")
    public interface ProviderService {
    
    }
    
    1. value = "feign-provider"的值feign-provider提供者注册到注册中心的服务名

      提供者的服务名
    2. path = "/api/v1/provider"是提供者的ProviderController的访问路径,/api/v1/是提供者在application.yml主配置文件配置的context-path

  2. 消费者远程调用类中,一个参数必须使用@RequestParam@PathVariable修饰,feign底层一个参数时会自动放进请求体中默认使用post请求(不用@RequestParam@PathVariable有可能取到的值为null),Get请求直接报错,提供者的controller可以不用@RequestParam修饰

    1. @RequestParam栗子,GetMapping或者PostMapping都可以

      1. 提供者controller

        import org.springframework.web.bind.annotation.*;
        
        @RestController
        @RequestMapping("/provider")
        public class ProviderController {
           @GetMapping("/testParam")
            public String testParam(String userId) {
                return "ProviderController_testParam_" + userId;
            }
        
            @PostMapping("/testPostMappingParam")
            public String testPostMappingParam(String userId) {
                return "ProviderController_testPostMappingParam_" + userId;
            }
        }
        
      2. 消费者

        1. 远程调用类

          import org.springframework.cloud.openfeign.FeignClient;
          import org.springframework.stereotype.Service;
          import org.springframework.web.bind.annotation.*;
          
          @Service
          @FeignClient(value = "feign-provider", path = "/api/v1/provider")
          public interface ProviderService {
              /**
               * 对应 public String testParam(String userId){}
               *
               * @param userId 用户id
               * @return 字符串
               */
              @GetMapping("/testParam")
              String testParam(@RequestParam String userId);
          
              /**
               * 对应 public String testPostMappingParam(String userId){}
               *
               * @param userId 用户id
               * @return 字符串
               */
              @PostMapping("/testPostMappingParam")
              String testPostMappingParam(@RequestParam String userId);
          }
          
        2. controller

          import com.sheng.cloud.feign.service.ProviderService;
          import org.springframework.web.bind.annotation.*;
          
          import javax.annotation.Resource;
          
          @RestController
          @RequestMapping("/consumer")
          public class ConsumerController {
              @Resource
              private ProviderService providerService;
           
              @GetMapping("/testParam")
              public String testParam(String userId) {
                  return providerService.testParam(userId);
              }
          
              @PostMapping("/testPostMappingParam")
              public String testPostMappingParam(String userId) {
                  return providerService.testPostMappingParam(userId);
              }
          }
          
    2. @PathVariable栗子,GetMapping或者PostMapping都可以

      1. 提供者controller

        import org.springframework.web.bind.annotation.*;
        
        @RestController
        @RequestMapping("/provider")
        public class ProviderController {
            @GetMapping("/testPathVariable/{userId}")
            public String testPathVariable(@PathVariable String userId) {
                return "ProviderController_testPathVariable_" + userId;
            }
        }
        
      2. 消费者

        1. 远程调用类

          import org.springframework.cloud.openfeign.FeignClient;
          import org.springframework.stereotype.Service;
          import org.springframework.web.bind.annotation.*;
          
          @Service
          @FeignClient(value = "feign-provider", path = "/api/v1/provider")
          public interface ProviderService {
              /**
               * 对应 public String testPathVariable(@PathVariable String userId){}
               *
               * @param userId 用户id
               * @return 字符串
               */
              @GetMapping("/testPathVariable/{userId}")
              String testPathVariable(@PathVariable String userId);
          }
          
        2. controller

          import com.sheng.cloud.feign.service.ProviderService;
          import org.springframework.web.bind.annotation.*;
          
          import javax.annotation.Resource;
          
          @RestController
          @RequestMapping("/consumer")
          public class ConsumerController {
              @Resource
              private ProviderService providerService;
           
                  @GetMapping("/testPathVariable/{userId}")
                  public String testPathVariable(@PathVariable String userId) {
                      return providerService.testPathVariable(userId);
                  }
          }
          
  3. 消费者远程调用类中,两个参数或以上必须使用@RequestParam@PathVariable修饰,不修饰不能启动,会直接报错。提供者的controller可以不用@RequestParam修饰

    1. @RequestParam栗子,GetMapping或者PostMapping都可以
      1. 提供者controller

        import org.springframework.web.bind.annotation.*;
        
        @RestController
        @RequestMapping("/provider")
        public class ProviderController {
            @PostMapping("/login")
            public String login(String username, String password) {
                return "ProviderController_login_username: " + username + "_password: " + password;
            }
        }
        
      2. 消费者

        1. 远程调用类

          import org.springframework.cloud.openfeign.FeignClient;
          import org.springframework.stereotype.Service;
          import org.springframework.web.bind.annotation.*;
          
          @Service
          @FeignClient(value = "feign-provider", path = "/api/v1/provider")
          public interface ProviderService {
              /**
               * 对应 public String login(String username, String password){}
               *
               * @param username 用户名
               * @param password 密码
               * @return 字符串
               */
              @PostMapping("/login")
              String login(@RequestParam String username, @RequestParam String password);
          }
          
        2. controller

          import com.sheng.cloud.feign.service.ProviderService;
          import org.springframework.web.bind.annotation.*;
          
          import javax.annotation.Resource;
          
          @RestController
          @RequestMapping("/consumer")
          public class ConsumerController {
              @Resource
              private ProviderService providerService;
           
              @PostMapping("/login")
              public String login(String username, String password) {
                  return providerService.login(username, password);
              }   
          }
          
    2. @PathVariable栗子,GetMapping或者PostMapping都可以
      1. 提供者controller

        import org.springframework.web.bind.annotation.*;
        
        @RestController
        @RequestMapping("/provider")
        public class ProviderController {
            @GetMapping("/loginByPathVariable/{username}/{password}")
            public String loginByPathVariable(@PathVariable String username, @PathVariable String password) {
                return "ProviderController_login_username: " + username + "password: " + password;
            }
        }
        
      2. 消费者

        1. 远程调用类

          import org.springframework.cloud.openfeign.FeignClient;
          import org.springframework.stereotype.Service;
          import org.springframework.web.bind.annotation.*;
          
          @Service
          @FeignClient(value = "feign-provider", path = "/api/v1/provider")
          public interface ProviderService {
              /**
               * 对应 public String loginByPathVariable(@PathVariable String username, @PathVariable String password){}
               *
               * @param username 用户名
               * @param password 密码
               * @return 字符串
               */
              @GetMapping("/loginByPathVariable/{username}/{password}")
              String loginByPathVariable(@PathVariable String username, @PathVariable String password);
          }
          
        2. controller

          import com.sheng.cloud.feign.service.ProviderService;
          import org.springframework.web.bind.annotation.*;
          
          import javax.annotation.Resource;
          
          @RestController
          @RequestMapping("/consumer")
          public class ConsumerController {
              @Resource
              private ProviderService providerService;
           
              @GetMapping("/loginByPathVariable/{username}/{password}")
              public String loginByPathVariable(@PathVariable String username, @PathVariable String password) {
                  return providerService.loginByPathVariable(username, password);
              }   
          }
          
  4. 如果消费者远程调用类的形参是JavaBean对象,那么提供者的controller对应的方法的形参必须用@RequestBody标记,否则取到的值为null,因为feign底层默认将对象序列化成json数据进行传递。

    1. 结果

      1. 不用@RequestBody修饰

        不用@RequestBody修饰
      2. @RequestBody修饰

        @RequestBody修饰
    2. 栗子

      1. 提供者controller

        import com.sheng.cloud.feign.dto.UserDto;
        import org.springframework.web.bind.annotation.*;
        
        @RestController
        @RequestMapping("/provider")
        public class ProviderController {
            @PostMapping("/getUser")
            public UserDto getUser(@RequestBody UserDto userDto) {
                return userDto;
            }
        }
        
      2. 消费者

        1. 远程调用类

          import com.sheng.cloud.feign.dto.UserDto;
          import org.springframework.cloud.openfeign.FeignClient;
          import org.springframework.stereotype.Service;
          import org.springframework.web.bind.annotation.*;
          
          @Service
          @FeignClient(value = "feign-provider", path = "/api/v1/provider")
          public interface ProviderService {
              /**
               * 对应 public UserDto getUser(@RequestBody UserDto userDto){}
               *
               * @param userDto 对象
               * @return UserDto
               */
              @PostMapping("/getUser")
              UserDto getUser(UserDto userDto);
          }
          
        2. controller

          import com.sheng.cloud.feign.dto.UserDto;
          import com.sheng.cloud.feign.service.ProviderService;
          import org.springframework.web.bind.annotation.*;
          
          import javax.annotation.Resource;
          
          @RestController
          @RequestMapping("/consumer")
          public class ConsumerController {
              @Resource
              private ProviderService providerService;
           
              @PostMapping("/getUser")
              public UserDto getUser(@RequestBody UserDto userDto) {
                  return providerService.getUser(userDto);
              }   
          }
          
  5. feign-demo 源码

上一篇 下一篇

猜你喜欢

热点阅读