Skip to content

第5章 REST客户端Feign

本章要点

  • REST 客户端
  • Feign 框架的使用
  • 在 Spring Cloud 中使用 Feign

5.1 REST客户端

在SpringCloud集群中,各个角色的通信基于REST服务,因此在调用服务时,就不可避免地需要使用REST服务的请求客户端。前面的章节中使用了Spring自带的RestTemplate,RestTemplate使用HttpClient发送请求。本章中,我们将介绍另一个REST客户端:Feign。

Feign框架已经被集成到Spring Cloud的Netflix项目中,使用该框架可以在Spring Cloud的集群中更加简单地调用REST服务。

5.1.1 使用CXF调用REST服务

CXF是目前一个较为流行的Web Service框架,是Apache的一个开源项目。使用CXF可以发布和调用使用各种协议的服务,包括SOAP协议、XML/HTTP等。当前CXF已经对REST 风格的Web Service提供支持,可以发布或调用REST风格的Web Service。由于CXF可以与Spring进行整合使用并且配置简单,因此得到许多开发者的青睐,而笔者以往所在公司的大部分项目,均使用CXF来发布和调用WebService。本章所使用的CXF版本为3.1.10,在Maven中加入以下依赖:

xml
		<!-- CXF -->
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-core</artifactId>
			<version>3.1.10</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-rs-client</artifactId>
			<version>3.1.10</version>
		</dependency>

编写代码请求/person/ {personld}服务,请见代码清单 5-1 。

文件:Demo4Book/SCN/05/5.1/rest-client/src/main/java/org/crazyit/cloud/CxfClient.java

java
package org.crazyit.cloud;

import java.io.InputStream;
import javax.ws.rs.core.Response;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.jaxrs.client.WebClient;

public class CxfClient {
	public static void main(String[] args) throws Exception {
		// 创建WebClient
		WebClient client = WebClient.create("http://localhost:8080/person/1");
		// 获取响应
		Response response = client.get();
		// 获取响应内容
		InputStream ent = (InputStream) response.getEntity();
		String content = IOUtils.readStringFromStream(ent);
		// 输出字符串
		System.out.println(content);
	}
}

客户端中使用了 WebClient 类发送请求,获取响应后读取输入流,获取服务返回的 JSON 字符串。运行代码清单 5-1 ,可看到返回的信息。

5.1.2 使用Restlet调用REST服务

Restlet 是一个轻量级的 REST 框架,使用它可以发布和调用 REST 风格的 Web Service 。本小节中的例子所使用的版本为 2.3.10, Maven 依赖如下:

xml
		<!-- Restlet -->
		<dependency>
			<groupId>org.restlet.jee</groupId>
			<artifactId>org.restlet</artifactId>
			<version>2.3.10</version>
		</dependency>
		<dependency>
			<groupId>org.restlet.jee</groupId>
			<artifactId>org.restlet.ext.jackson</artifactId>
			<version>2.3.10</version>
		</dependency>

客户端实现请见代码清单 5-2 。

java
package org.crazyit.cloud;

import java.util.HashMap;
import java.util.Map;
import org.restlet.data.MediaType;
import org.restlet.ext.jackson.JacksonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.ClientResource;


public class RestletClient {
	public static void main(String[] args) throws Exception {
		ClientResource client = new ClientResource("http://localhost:8080/person/1");
		// 调用get方法,服务器发布的是GET
		Representation response = client.get(MediaType.APPLICATION_JSON);
		// 创建JacksonRepresentation实例,将响应转换为Map
		JacksonRepresentation jr = new JacksonRepresentation(response,HashMap.class);
		// 获取转换后的Map对象
		Map result = (HashMap) jr.getObject();
		// 输出结果
		System.out.println(result.get("id") + "-" + result.get("name") + "-"
				+ result.get("age") + "-" + result.get("message"));
	}
}

代码清单 5-2 中使用的 Restiet 的 API 较为简单, 在此不赘述 。 但需要注意的是,在Maven 中使用 Restlet , 要额外配置仓库地址, 笔者成书时 , 在 Apache 官方仓库中并没有Restlet 的包。在项目的 pom. xml 文件 中增加以下配置 :

xml
	<repositories>
		<repository>
			<id>maven-restlet</id>
			<name>Restlet repository</name>
			<url>http://maven.restlet.org</url>
		</repository>
	</repositories>

5.1.3 Feign框架介绍

Feign 是 GitHub 上的一个开源项目,目的是简化 Web Service 客户端的开发 。 在使用Feign 时,可以使用注解来修饰接口,被注解修饰的接口具有访问 Web Service 的能力 。

这些注解中既包括 Feign 自带的注解,也支持使用第三方的注解 。 除此之外, Feign 还支持插件式的编码器和解码器,使用者可以通过该特性对请求和响应进行不同 的封装与解析 。

Spring Cloud 将 Feign 集成到 Netflix 项目中,当与 Eureka 、 Ribbon 集成时, Feign 就具有负载均衡的功能。 Feign 本身在使用上的简便性,加上与 Spring Cloud 的高度整合,使用该框架在 Spring Cloud 中调用集群服务,将会大大降低开发的工作量。

5.1.4 第一个Feign程序

先使用Feign编写一个HelloWorld的客户端,访问服务端的/hello服务,得到返回的字符串。当前SpringCloud所依赖的Feign版本为9.5.0,本章案例中的Feign也使用该版本。建立名称为feign-client的Maven项目,加入以下依赖:

xml
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-core</artifactId>
			<version>9.5.0</version>
		</dependency>
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-gson</artifactId>
			<version>9.5.0</version>
		</dependency>

新建接口 HelloClient,请见代码清单 5 -3 。

java
package org.crazyit.cloud;

import feign.RequestLine;

public interface HelloClient {
	  @RequestLine("GET /hello")
	  String sayHello();
}

HelloClient 表示一个服务接口,在接口的 sayHello 方法中使用了@RequestLine 注解,表示使用 GET 方法向/hello 发送请求。接下来编写客户端的运行类,请见代码清单 5-4 。

java
package org.crazyit.cloud;

import feign.Feign;
import feign.gson.GsonDecoder;

public class HelloMain {
	public static void main(String[] args) {
		// 调用Hello接口
		HelloClient hello = Feign.builder().target(HelloClient.class, "http://localhost:8080/");
		System.out.println(hello.getClass().getName());
		System.out.println(hello.sayHello());
	}
}

在运行类中,使用Feign创建HelloClient接口的实例,最后调用接口定义的方法。运行代码清单5-4,可以看到返回的“HelloWorld”字符串,可见接口已经被调用。熟悉AOP的朋友大概已经猜到,Feign实际上会帮我们动态生成代理类。Feign使用的是JDK的动态代理,生成的代理类会将请求的信息封装,交给feign.Client接口发送请求,而该接口的默认实现类最终会使用java.net.HttpURLConnection来发送HTTP请求。

5.1.5 请求参数与返回对象

本案例中有两个服务,另外一个地址为/person/{personId),需要传入参数并且返回JSON字符串。编写第二个Feign客户端,调用该服务。新建PersonClient服务类,定义调用接口并添加注解,请见代码清单5-5。

java
package org.crazyit.cloud;

import lombok.Data;
import lombok.NoArgsConstructor;
import feign.Param;
import feign.RequestLine;

public interface PersonClient {
	@RequestLine("GET /person/{personId}")
	Person findById(@Param("personId") Integer personId);
	
	@Data // 为所有属性加上setter和getter等方法
	class Person {
		Integer id;
		String name;
		Integer age;
		String message;
	}
}

定义的接口名称为 findById,参数为personId。需要注意的是,由于会返回Person实例,我们在接口中定义了一个Person的类;为了减少代码量,使用了Lombok项目,使用了该项目的@Data注解。要使用Lombok,需要添加以下Maven依赖:

xml
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.16.18</version>
		</dependency>

准备好提供服务的客户端类后,再编写运行类 。 运行类基本上与前面的 Hello World 类似,请见代码清单 5-6 。

java
package org.crazyit.cloud;

import org.crazyit.cloud.PersonClient.Person;
import feign.Feign;
import feign.gson.GsonDecoder;

public class PersonMain {
	public static void main(String[] args) {
		PersonClient personService = Feign.builder()
				.decoder(new GsonDecoder())
				.target(PersonClient.class, "http://localhost:8080/");
		Person person = personService.findById(2);
		System.out.println(person.id);
		System.out.println(person.name);
		System.out.println(person.age);
		System.out.println(person.message);
	}
}

在调用 Person 服务的运行类中添加了解码器的配置, GsonDecoder 会将返回的 JSON 字符串转换为接口方法返回的对象,关于解码器的内容,将在后面章节中讲述。运行代码清单 5-6 ,可以看到最终的输出。

本节使用了 CXF 、 Restlet、 Feign 来编写 REST 客户端,在编写客户端的过程中, 可以看到 Feign 的代码更加“面向对象”, 至于是否更加简沽,则见仁见智 。 下面的章节, 将深入了解 Feign 的各项功能 。

5.2 使用Feign

5.2.1 编码器

5.2.2 解码器

5.2.3 XML的编码与解码

5.2.4 自定义编码器与解码器

5.2.5 自定义Feign客户端

5.2.6 使用第三方注解

5.2.7 Feign解析第三方注解

5.2.8 请求拦截器

5.2.9 接口日志

5.3 在Spring Cloud中使用Feign

5.3.1 Spring Cloud整合Feign

5.3.2 Feign负载均衡

5.3.3 默认配置

5.3.4 自定义配置

5.3.5 可选配置

5.3.6 压缩配置

5.4 本章小结