写点什么

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

发布于: 2 小时前
【SpringCloud 技术专题】「原生态 Fegin」打开 Fegin 之 RPC 技术的开端,你会使用原生态的 Fegin 吗?(下)

前提回顾


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


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

内容简介

在项目开发中,除了考虑正常的调用之外,负载均衡和故障转移也是关注的重点,这也是 feign + ribbon 的优势所在,基于上面两篇文章的基础,接下来我们开展最后一篇原生态 fegin 结合 ribbon 服务进行服务远程调用且实现负载均衡机制,也帮助大家学习 ribbon 奠定基础。

maven 依赖

<dependencies>    <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>com.netflix.feign</groupId>        <artifactId>feign-ribbon</artifactId>        <version>8.18.0</version>    </dependency>  <dependency>    <groupId>com.netflix.archaius</groupId>    <artifactId>archaius-core</artifactId></dependency>
</dependencies>
复制代码


其中 feign-core 和 feign-ribbon 是必须的,如果需要在服务消费端和服务生产端之间进行对象交互,建议使用 feign-jackson

配置读取

import com.netflix.config.ConfigurationManager;import feign.Feign;import feign.jackson.JacksonDecoder;import feign.jackson.JacksonEncoder;import feign.ribbon.RibbonClient;public class AppRun {    public static void main(String[] args) throws Exception {        User param = new User();        param.setUsername("test");        RemoteService service = Feign.builder().client(RibbonClient.create())        .encoder(new JacksonEncoder())                .decoder(new JacksonDecoder())          .options(new Options(1000, 3500))                .retryer(new Retryer.Default(5000, 5000, 3))          .target(RemoteService.class, "http://remote-client/gradle-web");        /**         * 调用测试         */        for (int i = 1; i <= 10; i++) {            User result = service.getOwner(param);            System.out.println(result.getId() + "," + result.getUsername());        }    }}
复制代码


  • 声明了一个 User 类型的对象 param,该对象将作为参数被发送至服务生产端。

  • 重点在于通过 RibbonClient.create()使得 Feign 对象获得了 Ribbon 的特性。之后通过 encoder,decoder 设置编码器与解码器,并通过 target 方法将之前定义的接口 RemoteService 与一个 URL 地址 http://remote-client/gradle-web 进行了绑定。


现在来看 remote-client.properties 中的配置项,主要多是 RemoteClient 的配置机制


remote-client.ribbon.MaxAutoRetries=1remote-client.ribbon.MaxAutoRetriesNextServer=1remote-client.ribbon.OkToRetryOnAllOperations=trueremote-client.ribbon.ServerListRefreshInterval=2000remote-client.ribbon.ConnectTimeout=3000remote-client.ribbon.ReadTimeout=3000remote-client.ribbon.listOfServers=127.0.0.1:8080,127.0.0.1:8085remote-client.ribbon.EnablePrimeConnections=false
复制代码


所有的 key 都以 remote-client 开头,表明这些配置项作用于名为 remote-client 的服务。其实就是与之前绑定 RemoteService 接口的 URL 地址的 schema 相对应。


重点看 remote-client.ribbon.listOfServers 配置项,该配置项指定了服务生产端的真实地址。


之前与 RemoteService 接口绑定的 URL 地址是 : http://remote-client/gradle-web


在调用时会被替换为:


  • http://127.0.0.1:8080/gradle-web

  • http://127.0.0.1:8085/gradle-web


@RequestLine 指定的地址进行拼接,得到最终请求地址。本例中最终请求地址为:


  • http://127.0.0.1:8080/gradle-web/users/list

  • http://127.0.0.1:8085/gradle-web/users/list


由于使用的 ribbon,所以 feign 不再需要配置超时时长,重试策略。ribbon 提供了更为完善的策略实现。


本例中,服务生产端是一个简单的 springMvc,实现如下:


@RestController@RequestMapping(value="users")public class UserController {    @RequestMapping(value="/list",method={RequestMethod.GET,RequestMethod.POST,RequestMethod.PUT})    public User list(@RequestBody User user) throws InterruptedException{        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();        user.setId(new Long(request.getLocalPort()));        user.setUsername(user.getUsername().toUpperCase());        return user;    }}
复制代码


故障转移是通过 remote-client.properties 中的配置项进行配置。


  • 首先利用 archaius 项目的 com.netflix.config.ConfigurationManager 读取配置文件 remote-client.properties,该文件位于 src/main/resources 下。


负载均衡的策略又是如何设置呢?


import com.netflix.client.ClientFactory;import com.netflix.client.config.IClientConfig;import com.netflix.config.ConfigurationManager;import com.netflix.loadbalancer.ILoadBalancer;import com.netflix.loadbalancer.RandomRule;import com.netflix.loadbalancer.ZoneAwareLoadBalancer;import feign.Feign;import feign.jackson.JacksonDecoder;import feign.jackson.JacksonEncoder;import feign.ribbon.LBClient;import feign.ribbon.LBClientFactory;import feign.ribbon.RibbonClient;public class AppRun {    public static void main(String[] args) throws Exception {        ConfigurationManager.loadPropertiesFromResources("remote-client.properties");        User param = new User();        param.setUsername("test");        RibbonClient client = RibbonClient.builder().lbClientFactory(new LBClientFactory() {            @Override            public LBClient create(String clientName) {                IClientConfig config = ClientFactory.getNamedConfig(clientName);                ILoadBalancer lb = ClientFactory.getNamedLoadBalancer(clientName);                ZoneAwareLoadBalancer zb = (ZoneAwareLoadBalancer) lb;                zb.setRule(new RandomRule());                return LBClient.create(lb, config);            }        }).build();        RemoteService service = Feign.builder().client(client).encoder(new JacksonEncoder())                .decoder(new JacksonDecoder()).options(new Options(1000, 3500))                .retryer(new Retryer.Default(5000, 5000, 3)).target(RemoteService.class, "http://remote-client/gradle-web");        /**         * 调用测试         */        for (int i = 1; i <= 10; i++) {            User result = service.getOwner(param);            System.out.println(result.getId() + "," + result.getUsername());        }    }}
复制代码

其他负载均衡策略

 /**     * Ribbon负载均衡策略实现     * 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,     * 剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。     * @return     */
private IRule zoneAvoidanceRule() { return new ZoneAvoidanceRule(); }
/** * Ribbon负载均衡策略实现 * 随机选择一个server。 * @return */ private IRule randomRule() { return new RandomRule(); }
复制代码


不再使用 RibbonClient.create()来创建默认的 RibbonClient,而是通过 RibbonClient.builder()获得 feign.ribbon.Builder,进而设置 LBClientFactory 的实现来定制 LBClient,在创建 LBClient 的过程中即可指定负载策略的具体实现。

发布于: 2 小时前阅读数: 4
用户头像

🏆2021年InfoQ写作平台-签约作者 🏆 2020.03.25 加入

👑【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 我们始于迷惘,终于更高水平的迷惘

评论

发布
暂无评论
【SpringCloud 技术专题】「原生态 Fegin」打开 Fegin 之 RPC 技术的开端,你会使用原生态的 Fegin 吗?(下)