【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(上)

本文涉及的产品
云解析DNS,个人版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【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的两个问题


  1. 原生Feign只能一次解析一个接口,生成对应的请求代理对象,如果一个包里有多个调用接口就要多次解析非常麻烦。
  2. Feign生成的调用代理只是一个普通对象,该如何注册到Spring中,以便于我们可以使用@Autowired随时注入。



解决方案:


  1. 针对多次解析的问题,可以通过指定扫描包路径,然后对包中的类依次解析。
  2. 实现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);
    }
}
复制代码



注意事项:


  1. 如果接口返回的是一个复杂的嵌套对象,那么一定要明确的指定泛型,因为Feign在解析复杂对象的时候,需要通过反射获取接口返回对象内部的泛型类型才能正确使用Jackson解析。如果不明确的指明类型,Jackson会将json对象转换成一个LinkedHashMap类型。


  1. 如果你使用的是的Spring,又需要通过http调用别人的接口,都可以使用这个工具来简化调用与解析的操作。




相关文章
|
1月前
|
Dubbo Cloud Native 网络协议
【Dubbo3技术专题】「服务架构体系」第一章之Dubbo3新特性要点之RPC协议分析介绍
【Dubbo3技术专题】「服务架构体系」第一章之Dubbo3新特性要点之RPC协议分析介绍
51 1
|
1月前
|
设计模式 负载均衡 网络协议
【分布式技术专题】「分布式技术架构」实践见真知,手把手教你如何实现一个属于自己的RPC框架(架构技术引导篇)
【分布式技术专题】「分布式技术架构」实践见真知,手把手教你如何实现一个属于自己的RPC框架(架构技术引导篇)
88 0
|
3天前
|
XML 编解码 JSON
技术经验解读:什么是RPC?
技术经验解读:什么是RPC?
|
1月前
|
Java fastjson 数据安全/隐私保护
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
【Dubbo3技术专题】「云原生微服务开发实战」 一同探索和分析研究RPC服务的底层原理和实现
101 0
|
自然语言处理 Kubernetes Dubbo
【Alibaba微服务技术系列】「Dubbo3.0技术专题」第一章之Dubbo3新特性要点之RPC协议分析介绍
【Alibaba微服务技术系列】「Dubbo3.0技术专题」第一章之Dubbo3新特性要点之RPC协议分析介绍
179 0
|
存储 安全 API
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(高级用法)
【Fegin技术专题】「原生态」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(高级用法)
130 0
|
1月前
|
负载均衡 Dubbo Java
Dubbo 3.x:探索阿里巴巴的开源RPC框架新技术
随着微服务架构的兴起,远程过程调用(RPC)框架成为了关键组件。Dubbo,作为阿里巴巴的开源RPC框架,已经演进到了3.x版本,带来了许多新特性和技术改进。本文将探讨Dubbo 3.x中的一些最新技术,包括服务注册与发现、负载均衡、服务治理等,并通过代码示例展示其使用方式。
138 9
|
1月前
|
Dubbo Java 应用服务中间件
Rpc编程系列文章第三篇:Hessian RPC一个老的RPC框架
Rpc编程系列文章第三篇:Hessian RPC一个老的RPC框架
|
4天前
|
存储 缓存 Linux
【实战指南】嵌入式RPC框架设计实践:六大核心类构建高效RPC框架
在先前的文章基础上,本文讨论如何通过分层封装提升一个针对嵌入式Linux的RPC框架的易用性。设计包括自动服务注册、高性能通信、泛型序列化和简洁API。框架分为6个关键类:BindingHub、SharedRingBuffer、Parcel、Binder、IBinder和BindInterface。BindingHub负责服务注册,SharedRingBuffer实现高效数据传输,Parcel处理序列化,而Binder和IBinder分别用于服务端和客户端交互。BindInterface提供简单的初始化接口,简化应用集成。测试案例展示了客户端和服务端的交互,验证了RPC功能的有效性。
|
9天前
|
分布式计算 资源调度 网络协议
分布式系统详解--框架(Hadoop--RPC协议)
分布式系统详解--框架(Hadoop--RPC协议)
13 0