spring环境中Feign + Ribbon的使用

作者: SamHxm | 来源:发表于2017-01-07 23:51 被阅读10815次

    之前在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;
    
    @Configuration
    public 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;
    
    @RestController
    public 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;
    
    @RestController
    public 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();
        }
    }
    

    相关文章

      网友评论

      • Java小白菜:请问,
        这里的:
        RemoteService service = Feign.builder().client(client).encoder(new JacksonEncoder())
        .decoder(new JacksonDecoder()).target(RemoteService.class, "http://sample-client/gradle-web&quot;);
        terget()方法第二个参数除了将listServers写到配置文件中,还有什么好的方法吗?
        Java小白菜:@SamHxm 好吧,按说按原生那种配置,然后通过服务名读取服务列表那样应该是可以的,但是不知道为什么总是报错 服务名 没有可用client
        SamHxm:我也没找到更好的方式.
        Java小白菜:为什么按照官网如下配置就错了呢?
        receive.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
        # refresh every minute
        receive.ribbon.ServerListRefreshInterval=60000
        # movieservice is the virtual address that the target server(s) uses to register with Eureka server
        receive.ribbon.DeploymentContextBasedVipAddresses=receive.mydomain.net

      本文标题:spring环境中Feign + Ribbon的使用

      本文链接:https://www.haomeiwen.com/subject/axcqbttx.html