SpringCloud系列之Feign

作者 胡萝虎 日期 2018-05-14
SpringCloud系列之Feign

SpringCloud系列之Ribbon一文中我们介绍了使用RestTemplate配合Ribbon实现服务的调用和客户端负载均衡。但是使用RestTemplate调用服务有个弊端,就是参数传递不方便,无论是GET还是POST,都需要对参数进行组装,一旦参数比较多,实际开发时将非常麻烦,而且容易出错。所以,本文将介绍另一种Spring Cloud的服务调用方式——Feign。

Feign

Feign也是Netflix开发的,它是一种声明式、模版化的HTTP客户端,相对于RestTemplate更加便捷、优雅。在Spring Cloud中,我们可以使用Feign非常方便的创建服务调用的模版代码,配合相关的注解,完成配置。

实战

修改provider-user服务

增加测试接口

增加新增用户设置用户name接口,其他都不需要修改

package com.huluohu.cloud.user.presentation.web;

import com.huluohu.cloud.user.presentation.mode.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.Random;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
private Random random = new Random();

/**
* 查询用户
* @param id
* @return
*/
@GetMapping("/{id}")
public UserDTO findById(@PathVariable Integer id) {
return UserDTO.builder()
.id(id)
.username("root_" + id)
.name("huluohu_" + id)
.age(random.nextInt(30) % (30 - 15 + 1) + 15)
.balance(random.nextLong())
.build();
}

/**
* 新增用户
* @param user
* @return
*/
@PostMapping()
public Integer add(UserDTO user) {
return random.nextInt();
}


/**
* 设置用户name
* @param id
* @param name
* @return
*/
@PutMapping("/{id}")
public Boolean setName(@PathVariable Integer id, @RequestParam("name") String name){
log.info("id={}", id);
log.info("name={}", name);
return Boolean.TRUE;
}
}

创建Feign测试工程

consuer-movie-feign

复制consuer-movie module,修改名称为consuer-movie-feign

新增依赖

在pom.xml下增加Feign的依赖

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

修改启动类

package com.huluohu.cloud.movie;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.huluohu.cloud.movie.application.client") //启用feign
public class MovieApplication {
public static void main(String[] args) {
SpringApplication.run(MovieApplication.class,args);
}
}

增加服务调用代码

package com.huluohu.cloud.movie.application.client;

import com.huluohu.cloud.movie.application.model.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;

@FeignClient(serviceId = "provider-user")//设置服务提供者名称
public interface UserServiceClient {

/**
* 根据ID查询User
*
* @param id
* @return
*/
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
User findById(@PathVariable("id") Integer id);

/**
* 新增User
*
* @param user
* @return
*/
@RequestMapping(value = "/user", method = RequestMethod.POST)
Integer addUser(@RequestBody User user);

/**
* 修改name
* @param id
* @param name
* @return
*/
@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
Boolean setName(@PathVariable("id") Integer id, @RequestParam("name") String name);
}

增加测试代码

package com.huluohu.cloud.movie.presentation.web;

import com.huluohu.cloud.movie.application.client.UserServiceClient;
import com.huluohu.cloud.movie.application.model.User;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private UserServiceClient userServiceClient;

@GetMapping("/user/{id}")
public Object testFindUser(@PathVariable Integer id) {
final User user = userServiceClient.findById(id);
return ResponseEntity.ok(user);
}

@PostMapping("/user")
public Object testAddUser(User user) {
return ResponseEntity.ok(userServiceClient.addUser(user));
}

@PutMapping("/user/{id}")
public Object testSetName(@PathVariable Integer id, @RequestParam("name") String name) {
return ResponseEntity.ok(userServiceClient.setName(id, name));
}
}

配置

配置文件和consumer-movie中的一样。

到此,准备工作都已经完成了,下面开始测试。

测试

  1. 启动Eureka Server(local profile)
  2. 启动Config Server(不需要区分profile)
  3. 启动provider-user(local profile)
  4. 启动provider-user(local2 profile,模拟多节点)
  5. 启动consumer-movie-feign(local profile)
  • 启动应用列表

  • 服务注册情况

  • 测试结果(使用Postman测试)

    • 查询用户

    • 新增用户

    • 设置用户名称

以上三个测试,分别测试三种参数传递的情况:

  1. 查询用户,测试通过GET方式,使用@PathVariable注解从请求URL中获取参数
  2. 新增用户,测试通过POST方式,使用@RequestBody注解传递多个参数的情况
  3. 设置用户name,测试通过PUT方式,使用@RequestParam传递单个参数的情况

进阶

在Spring Cloud中,支持对Feign进行自定义配置,包括编码器、解码器、契约、拦截器等进行自定义:

  • Decoder
  • Encoder
  • Logger
  • Contract
  • Feign.Builder
  • Client

还有一些Bean在Spring Cloud中没有提供默认的实现,但是如果在应用上下文中存在这些实现,也会被Feign应用:

  • Logger.Level
  • Retryer
  • ErrorDecoder
  • Request.Options
  • Collection
  • SetterFactory

如果需要自定义以上Bean,可以创建一个Configuration类,然后设置到FeignClient即可:

配置类

@Configuration
public class UserServiceConfiguration {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}

@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
}

使用配置

@FeignClient(name = "provider-user", configuration = UserServiceConfiguration.class)
public interface UserServiceClient {
//..
}

你可能会喜欢

“扫一扫接着看”