六、Feign服务调用

Feign服务调用

​ 前一节有说到,Feign使用了Ribbon,即具有了Ribbon的负载均衡的功能。这一节主要是基于上一节的程序上,使用Feign这个伪Http客户端来进行服务调用。

Feign是一个声明式web服务客户端,它让我们写web服务客户端更加简单。使用Feign只需要创建一个接口并标相应的注解。

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka to provide a load balanced http client when using Feign.

官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.0.M3/reference/html/

程序基于上一节,即Ribbon和RestTemplate实现服务负载均衡。

服务提供方

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@RestController
public class IndexController {

private static Map<Integer,User> users = new HashMap<>();

static {
users.put(1,new User(1,"张三","provider-1",18));
users.put(2,new User(2,"李四","provider-1",19));
users.put(3,new User(3,"王五","provider-1",20));
}

@GetMapping("/echo/{str}")
public String echo(@PathVariable("str") String str){
return "provider-1 echo : "+str;
}

@GetMapping("/user/{id}")
public User user(@PathVariable("id") Integer id){
System.out.println("provider 1 rec");
return users.get(id);
}

@GetMapping("/exist")
public boolean exist(User user){
System.out.println("provider 1 rec");
boolean isExist = false;
for (Map.Entry<Integer,User> userEntry : users.entrySet()){
if (userEntry.getValue().getUsername().equals(user.getUsername())
&&
userEntry.getValue().getPassword().equals(user.getPassword())){
isExist = true;
break;
}
}
return isExist;
}
}

服务消费方

依赖

使用Feign,我们需要先导入Feign的依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class NacosFeignServiceConsumerApplication {

@Bean
public IRule iRule(){
return new RoundRobinRule();//轮询策略
}

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}

public static void main(String[] args) {
SpringApplication.run(NacosFeignServiceConsumerApplication.class, args);
}
}

注意:这里我们使用@EnableFeignClients 注解,来启用Feign客户端功能。重点是我们如何来使用feign调用服务。如下:

Feign服务接口

1
2
3
4
5
6
7
8
9
@FeignClient(value = "feign-service-provider")
public interface FeignService {

@GetMapping("/echo/{str}")
String echo(@PathVariable("str") String str);

@GetMapping("/exist")
boolean exist(@SpringQueryMap User user);
}

我们可以发现,这里与服务提供方的controller方法很类似,首先,声明一个接口,使用@FeignClient 注解来标注此接口为Feign客户端接口,使用value 属性来标注是哪一个服务,value指定服务名。细心的可能已经发现这里出现了一个新的注解@StringQueryMap 注解,后面会介绍到。

Controller

使用Feign服务:首先将服务接口注入,其他就与普通的方法调用一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RestController
public class IndexController {

@Autowired
private FeignService feignService;

@Autowired
private RestTemplate restTemplate;

@GetMapping("/echo/{str}")
public HashMap echo(@PathVariable("str") String str) {
HashMap map = new HashMap();
String echo = feignService.echo(str);
System.out.println("message:" + echo);
map.put("message", echo);
return map;
}

//使用RestTemplate调用
@GetMapping("/user/{id}")
public User user(@PathVariable("id") Integer id) {
User user = restTemplate.getForObject("http://feign-service-provider/user/" + id, User.class);
System.out.println(user);
return user;
}

@GetMapping("/exist")
public boolean exist(User user) {
return feignService.exist(user);
}
}

效果图

@StringQueryMap

https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.0.M3/reference/html/#feign-querymap-support

The OpenFeign @QueryMap annotation provides support for POJOs to be used as GET parameter maps. Unfortunately, the default OpenFeign QueryMap annotation is incompatible with Spring because it lacks a value property.

Spring Cloud OpenFeign provides an equivalent @SpringQueryMap annotation, which is used to annotate a POJO or Map parameter as a query parameter map.

OpenFeign的 @QueryMap 注解提供了Get请求的POJOS的参数支持,遗憾的是,这个OpenFeign的默认QueryMap注解由于缺少value属性而不太适用。

1
2
3
4
5
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface QueryMap {
boolean encoded() default false;
}

SpringCloudOpenFeign提供了一个相同功能的注解:@SpringQueryMap ,它可以标注PoJo或Map用来作为请求参数。

1
2
3
4
5
6
7
8
9
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface SpringQueryMap {
@AliasFor("encoded")
boolean value() default false;

@AliasFor("value")
boolean encoded() default false;
}

我们再回来看看Feign这个接口:

1
2
3
4
5
6
7
8
9
@FeignClient(value = "feign-service-provider")
public interface FeignService {

@GetMapping("/echo/{str}")
String echo(@PathVariable("str") String str);

@GetMapping("/exist")
boolean exist(@SpringQueryMap User user);
}

当我们不使用@SpringQueryMap 注解是,会抛出如下异常:

正常测试结果:

Feign 日志

https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.0.M3/reference/html/#feign-logging

配置文件

1
2
3
logging:
level:
com.ooyhao.nacosfeignserviceconsumer.feignserver: debug

The Logger.Level object that you may configure per client, tells Feign how much to log. Choices are:

  • NONE, No logging (DEFAULT).
  • BASIC, Log only the request method and URL and the response status code and execution time.
  • HEADERS, Log the basic information along with request and response headers.
  • FULL, Log the headers, body, and metadata for both requests and responses.

Feign的日志级别有以上几种方式:

  1. NONE:没有日志,默认(性能高)。
  2. BASIC:记录请求方法和请求路径,响应状态码和执行时间。
  3. HEADERS:记录基本的请求和响应头信息。
  4. FULL:记录请求/响应头,体和元数据。
1
2
3
4
5
6
class FooConfig{
@Bean
Logger.Level level(){
return Logger.Level.FULL;
}
}

效果图

下面是调用效果:

Feign继承

https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.0.M3/reference/html/#spring-cloud-feign-inheritance

上面是从SpringCloud OpenFeign官方文档截图的,说:这显示是不明智的在Feign服务端和客户端共享一个接口,这样会提高耦合度,所以这里就做演示了。

#

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×