Ribbon
是一个客户端负载均衡器。
LB 负载均衡,平摊用户请求到多个服务上,以达到系统的高可用。
- 集中式: F5、Nginx,独立于消费方和提供方。
- 进程式:Ribbon,将负载均衡逻辑集成到消费方,从注册中心获取可用的服务,然后自己选择一个进行访问。
Spring Cloud Netflix默认情况下为 (BeanType beanName:ClassName)提供以下bean:
IClientConfig ribbonClientConfig:DefaultClientConfigImpl
IRule ribbonRule:ZoneAvoidanceRule
IPing ribbonPing:NoOpPing
ServerList<Server> ribbonServerList:ConfigurationBasedServerList
ServerListFilter<Server> ribbonServerListFilter:ZonePreferenceServerListFilter
ILoadBalancer ribbonLoadBalancer:ZoneAwareLoadBalancer
ServerListUpdater ribbonServerListUpdater:PollingServerListUpdater
可以自定义修改相关配置:
@Configuration
public class FooConfiguration {
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
@Bean
public IRule ribbonRule() {
return new RetryRule();
}
}
这里指定了 ping 规则和负载均衡规则。
- ZoneAvoidanceRule 默认规则,复合判断 server 所在区域的性能和可用性选择。
- RandomRule 随机
- RoundRobinRule 轮询
- WeightedResponseTimeRule 计算平均响应时间去设置权重。刚开始启动收集的数据少,就使用轮询规则。
接下来自定义一个负载均衡规则:
官网例子:
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}
注意:
FooConfiguration必须是@Configuration,但请注意,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan)。
public class TimesRule extends AbstractLoadBalancerRule {
private int times = 5;
private int total = 0; // 总共被调用的次数
private int currentIndex = 0; // 当前提供服务的机器号
public TimesRule() {
super();
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes only get more
* restrictive.
*/
return null;
}
if (total < times) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}
}
if (server == null) {
/*
* The only time this should happen is if the server list were somehow trimmed.
* This is a transient condition. Retry after yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
Ribbon + RestTemplate 就是其中一种服务调用的方式。
Feign
Feign是一个声明式的Web服务客户端。使用它就又可以回归面向接口开发,脱离面向 restTemplate 开发。
定义一个接口,加上注解,@FeignClient 绑定服务,@RequestMapping 声明调用的 api 接口,在配置中开启 @EnableFeignClients。
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
}
Feign 集成了 Ribbon,实现了负载均衡,集成了 Hystrix,实现了服务降级。
与 Ribbon + RestTemplate 不同的是,通过 Feign只需要定义服务绑定接口且以声明的方式控制层调用接口,优雅而简单的实现了服务调用。