内容简介
在项目开发中,除了考虑正常的调用之外,负载均衡和故障转移也是关注的重点,这也是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
在调用时会被替换为:
@RequestLine指定的地址进行拼接,得到最终请求地址。本例中最终请求地址为:
由于使用的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的过程中即可指定负载策略的具体实现。