之前介绍了非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