Ribbon
是一個(gè)客戶端負(fù)載均衡器。
LB 負(fù)載均衡,平攤用戶請(qǐng)求到多個(gè)服務(wù)上,以達(dá)到系統(tǒng)的高可用。
- 集中式: F5、Nginx,獨(dú)立于消費(fèi)方和提供方。
- 進(jìn)程式:Ribbon,將負(fù)載均衡邏輯集成到消費(fèi)方,從注冊(cè)中心獲取可用的服務(wù),然后自己選擇一個(gè)進(jìn)行訪問(wèn)。
Spring Cloud Netflix默認(rèn)情況下為 (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
可以自定義修改相關(guān)配置:
@Configuration
public class FooConfiguration {
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
@Bean
public IRule ribbonRule() {
return new RetryRule();
}
}
這里指定了 ping 規(guī)則和負(fù)載均衡規(guī)則。
- ZoneAvoidanceRule 默認(rèn)規(guī)則,復(fù)合判斷 server 所在區(qū)域的性能和可用性選擇。
- RandomRule 隨機(jī)
- RoundRobinRule 輪詢
- WeightedResponseTimeRule 計(jì)算平均響應(yīng)時(shí)間去設(shè)置權(quán)重。剛開(kāi)始啟動(dòng)收集的數(shù)據(jù)少,就使用輪詢規(guī)則。
接下來(lái)自定義一個(gè)負(fù)載均衡規(guī)則:
官網(wǎng)例子:
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}
注意:
FooConfiguration必須是@Configuration,但請(qǐng)注意,它不在主應(yīng)用程序上下文的@ComponentScan中,否則將由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),則需要采取措施避免包含(例如將其放在一個(gè)單獨(dú)的,不重疊的包中,或者指定要在@ComponentScan)。
public class TimesRule extends AbstractLoadBalancerRule {
private int times = 5;
private int total = 0; // 總共被調(diào)用的次數(shù)
private int currentIndex = 0; // 當(dāng)前提供服務(wù)的機(jī)器號(hào)
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 就是其中一種服務(wù)調(diào)用的方式。
Feign
Feign是一個(gè)聲明式的Web服務(wù)客戶端。使用它就又可以回歸面向接口開(kāi)發(fā),脫離面向 restTemplate 開(kāi)發(fā)。
定義一個(gè)接口,加上注解,@FeignClient 綁定服務(wù),@RequestMapping 聲明調(diào)用的 api 接口,在配置中開(kāi)啟 @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,實(shí)現(xiàn)了負(fù)載均衡,集成了 Hystrix,實(shí)現(xiàn)了服務(wù)降級(jí)。
與 Ribbon + RestTemplate 不同的是,通過(guò) Feign只需要定義服務(wù)綁定接口且以聲明的方式控制層調(diào)用接口,優(yōu)雅而簡(jiǎn)單的實(shí)現(xiàn)了服務(wù)調(diào)用。