深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)
Posted 洛神灬殇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)相关的知识,希望对你有一定的参考价值。
前提介绍
-
Feign是SpringCloud中服务消费端的调用框架,通常与ribbon,hystrix等组合使用。
-
由于遗留原因,某些项目中,整个系统并不是SpringCloud项目,甚至不是Spring项目,而使用者关注的重点仅仅是简化http调用代码的编写。
-
如果采用httpclient或者okhttp这样相对较重的框架,对初学者来说编码量与学习曲线都会是一个挑战,而使用spring中RestTemplate,又没有配置化的解决方案,由此想到是否可以脱离Spring cloud,独立使用Feign。
内容简介
Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket。Feign最初是为了降低统一绑定Denominator到HTTP API的复杂度,不区分是否支持Restful。Feign旨在通过最少的资源和代码来实现和HTTP API的连接。通过可定制的解码器和错误处理,可以编写任意的HTTP API。
maven依赖
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jackson</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
<version>2.18.1</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jackson</artifactId>
<version>8.18.0</version>
</dependency>
定义配置类
RemoteService service = Feign.builder()
.options(new Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3))
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(RemoteService.class, "http://127.0.0.1:8085");
- options方法指定连接超时时长及响应超时时长
- retryer方法指定重试策略
- target方法绑定接口与服务端地址。
- 返回类型为绑定的接口类型。
自定义接口
随机定义一个远程调用的服务接口,并且声明相关的接口参数和请求地址。
通过@RequestLine指定HTTP协议及URL地址
public class User
String userName;
public interface RemoteService
@Headers("Content-Type: application/json","Accept: application/json")
@RequestLine("POST /users/list")
User getOwner(User user);
@RequestLine("POST /users/list2")
@Headers(
"Content-Type: application/json",
"Accept: application/json",
"request-token: requestToken",
"UserId: userId",
"UserName: userName"
)
public User getOwner(@RequestBody User user,
@Param("requestToken") String requestToken,
@Param("userId") Long userId,
@Param("userName") String userName);
服务提供者
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value="users")
public class UserController
@RequestMapping(value="/list",method=RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT)
@ResponseBody
public User list(@RequestBody User user) throws InterruptedException
System.out.println(user.getUsername());
user.setId(100L);
user.setUsername(user.getUsername().toUpperCase());
return user;
调用
与调用本地方法相同的方式调用feign包装的接口,直接获取远程服务提供的返回值。
String result = service.getOwner(new User("scott"));
原生Feign的两个问题
-
原生Feign只能一次解析一个接口,生成对应的请求代理对象,如果一个包里有多个调用接口就要多次解析非常麻烦。
-
Feign生成的调用代理只是一个普通对象,该如何注册到Spring中,以便于我们可以使用@Autowired随时注入。
解决方案:
-
针对多次解析的问题,可以通过指定扫描包路径,然后对包中的类依次解析。
-
实现BeanFactoryPostProcessor接口,扩展Spring容器功能。
定义一个注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignApi
/**
* 调用的服务地址
* @return
*/
String serviceUrl();
生成Feign代理并注册到Spring实现类:
import feign.Feign;
import feign.Request;
import feign.Retryer;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class FeignClientRegister implements BeanFactoryPostProcessor
//扫描的接口路径
private String scanPath="com.xxx.api";
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
List<String> classes = scan(scanPath);
if(classes==null)
return ;
System.out.println(classes);
Feign.Builder builder = getFeignBuilder();
if(classes.size()>0)
for (String claz : classes)
Class<?> targetClass = null;
try
targetClass = Class.forName(claz);
String url=targetClass.getAnnotation(FeignApi.class).serviceUrl();
if(url.indexOf("http://")!=0)
url="http://"+url;
Object target = builder.target(targetClass, url);
beanFactory.registerSingleton(targetClass.getName(), target);
catch (Exception e)
throw new RuntimeException(e.getMessage());
public Feign.Builder getFeignBuilder()
Feign.Builder builder = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.options(new Request.Options(1000, 3500))
.retryer(new Retryer.Default(5000, 5000, 3));
return builder;
public List<String> scan(String path)
ScanResult result = new FastClasspathScanner(path).matchClassesWithAnnotation(FeignApi.class, (Class<?> aClass) ->
).scan();
if(result!=null)
return result.getNamesOfAllInterfaceClasses();
return null;
调用接口编写示例:
import com.xiaokong.core.base.Result;
import com.xiaokong.domain.DO.DeptRoom;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import com.xiaokong.register.FeignApi;
import java.util.List;
@FeignApi(serviceUrl = "http://localhost:8085")
public interface RoomApi
@Headers("Content-Type: application/json","Accept: application/json")
@RequestLine("GET /room/selectById?id=id")
Result<DeptRoom> selectById(@Param(value="id") String id);
@Headers("Content-Type: application/json","Accept: application/json")
@RequestLine("GET /room/test")
Result<List<DeptRoom>> selectList();
接口使用示例:
@Service
public class ServiceImpl
//将接口注入要使用的bean中直接调用即可
@Autowired
private RoomApi roomApi;
@Test
public void demo()
Result<DeptRoom> result = roomApi.selectById("1");
System.out.println(result);
注意事项:
-
如果接口返回的是一个复杂的嵌套对象,那么一定要明确的指定泛型,因为Feign在解析复杂对象的时候,需要通过反射获取接口返回对象内部的泛型类型才能正确使用Jackson解析。如果不明确的指明类型,Jackson会将json对象转换成一个LinkedHashMap类型。
-
如果你使用的是的Spring,又需要通过http调用别人的接口,都可以使用这个工具来简化调用与解析的操作。
以上是关于深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)的主要内容,如果未能解决你的问题,请参考以下文章
深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)
深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的超时机制的原理和实现分析
深入浅出SpringCloud原理及实战「Netflix系列之Hystrix」针对于限流熔断组件Hystrix的基本参数和实现原理介绍分析
深入浅出Dubbo3原理及实战「SpringCloud-Alibaba系列」基于Nacos作为注册中心进行发布SpringCloud-alibaba生态的RPC接口实战
深入浅出SpringCloud原理及实战「Netflix系列之Ribbon」针对于负载均衡组件Ribbon的基本参数和实现原理介绍分析
深入浅出SpringCloud原理及实战「SpringCloud-Gateway系列」微服务API网关服务的Gateway全流程开发实践指南(入门篇)