手记

spring环境中Feign + Ribbon的使用

之前介绍了非spring cloud环境中feign+ribbon的使用。简化http客户端的实现。

有朋友私信我,说demo都是在main方法中进行feign对象的组装,而实际的java项目大部分都会用到spring IOC容器进行对象管理,能不能提供在spring环境中使用的示例。

于是有了这篇文章。

spring环境有很多类型,大致可分为

  • 传统的,基于xml或Java config的spring应用。

  • spring boot应用。

  • spring cloud应用。

在spring cloud中,feign,ribbon等netflix产品已经与spring进行了集成,在程序员DD的系列文章中已有非常详尽的说明。
在spring boot中使用@Bean对远程调用接口定义即可。例如:
import java.io.IOException;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;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.IRule;import com.netflix.loadbalancer.RandomRule;import com.netflix.loadbalancer.ZoneAvoidanceRule;import com.netflix.loadbalancer.ZoneAwareLoadBalancer;import com.sam.demo.service.RemoteService;import feign.Feign;import feign.jackson.JacksonDecoder;import feign.jackson.JacksonEncoder;import feign.ribbon.LBClient;import feign.ribbon.LBClientFactory;import feign.ribbon.RibbonClient;@Configurationpublic class Config {    public Config() {        try {
            ConfigurationManager.loadPropertiesFromResources("sample-client.properties");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }    @Bean
    public RemoteService remoteService() {

        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(zoneAvoidanceRule());                return LBClient.create(lb, config);
            }
        }).build();

        RemoteService service = Feign.builder().client(client).encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder()).target(RemoteService.class, "http://sample-client/gradle-web");        return service;
    }    /**
     * 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();
    }

}

RemoteService就是自定义的远程调用接口。

import com.sam.demo.entity.User;import feign.Headers;import feign.RequestLine;public interface RemoteService {    
    @Headers({"Content-Type: application/json","Accept: application/json"})    @RequestLine("POST /users/list")    public User getOwner(User user);
}

这里顺便用到了Ribbon的另一种负载均衡策略实现ZoneAvoidanceRule,并定义了RandomRule负载均衡策略实现,如果有多个远程调用接口,可以根据业务情况,选择不同的负载均衡策略实现。

需要注意的是,在组装远程接口的实现之前,需要读取配置文件,这里将读取动作简单的放到了构造方法中。实际项目中应与其他读取配置文件的代码封装在一起。

在需要使用远程接口的地方,注入该接口即可,例如:

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import com.sam.demo.entity.User;import com.sam.demo.service.RemoteService;@RestControllerpublic class RemoteController {    
    @Autowired
    private RemoteService remoteService;    
    @RequestMapping(value="/remote",method={RequestMethod.GET})    public String list() {
        
        User user = new User();
        user.setUsername("scott");
        user = remoteService.getOwner(user);        
        return user.getUsername();
    }
}

将RemoteService当作普通的bean注入即可,使用上也完全与本地调用方式相同。

传统spring应用中,处理思路与spring boot相同,仅仅的操作方式发生改变。
<bean id="remoteService" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass">
        <value>com.sam.demo.config.Config</value>
    </property>
    <property name="targetMethod">
        <value>remoteService</value>
    </property></bean>

同样是将方法返回值定义为bean。下面是com.sam.demo.config.Config的实现,与spring boot的方式几乎一致。只不过为了方便,改为静态方法。

import java.io.IOException;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.IRule;import com.netflix.loadbalancer.RandomRule;import com.netflix.loadbalancer.ZoneAvoidanceRule;import com.netflix.loadbalancer.ZoneAwareLoadBalancer;import com.sam.demo.service.RemoteService;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 Config {    
    static {        try {
            ConfigurationManager.loadPropertiesFromResources("sample-client.properties");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }    public static RemoteService remoteService() {
        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(zoneAvoidanceRule());                return LBClient.create(lb, config);
            }
        }).build();

        RemoteService service = Feign.builder().client(client).encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder()).target(RemoteService.class, "http://sample-client/gradle-web");        return service;
    }    /**
     * Ribbon负载均衡策略实现
     * 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,
     * 剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。
     * @return
     */
    private static IRule zoneAvoidanceRule() {        return new ZoneAvoidanceRule();
    }    
    /**
     * Ribbon负载均衡策略实现
     * 随机选择一个server。
     * @return
     */
    private static IRule randomRule() {        return new RandomRule();
    }
}

在需要调用远程接口的类中,注入该接口即可。

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;import com.sam.demo.entity.User;import com.sam.demo.service.RemoteService;@RestControllerpublic class RemoteController {    
    @Autowired
    private RemoteService remoteService;    
    @RequestMapping(value="/remote",method={RequestMethod.GET})    public String list() {
        
        User user = new User();
        user.setUsername("scott");
        user = remoteService.getOwner(user);        
        return user.getUsername();
    }
}



作者:SamHxm
链接:https://www.jianshu.com/p/7bb96e2dc944


0人推荐
随时随地看视频
慕课网APP