深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)

Posted 洛神灬殇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出SpringCloud原理及实战「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(下)相关的知识,希望对你有一定的参考价值。

前提回顾

【深入浅出SpringCloud原理及实战】「Netflix系列之Fegin」打开Fegin之RPC技术的开端,你会使用原生态的Fegin吗?(中)

【深入浅出SpringCloud原理及实战】「Netflix系列之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=1
remote-client.ribbon.MaxAutoRetriesNextServer=1
remote-client.ribbon.OkToRetryOnAllOperations=true
remote-client.ribbon.ServerListRefreshInterval=2000
remote-client.ribbon.ConnectTimeout=3000
remote-client.ribbon.ReadTimeout=3000
remote-client.ribbon.listOfServers=127.0.0.1:8080,127.0.0.1:8085
remote-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的过程中即可指定负载策略的具体实现。

以上是关于深入浅出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全流程开发实践指南(入门篇)