Spring Cloud調(diào)用Ribbon的步驟
Ribbon集成在spring-cloud-starter-netflix-eureka-client中,可以參考eureka的使用。在此基礎(chǔ)上簡單修改一下,就可以完成服務(wù)調(diào)用及負(fù)載均衡
1. RestTemplate 官網(wǎng) 通過RestTemplate,可以實現(xiàn)HttpClient的功能,只需要給它提供一個url及返回類型,即可實現(xiàn)遠程方法調(diào)用。 1.1 加入到IOC容器首先,將其加入到IOC容器中。@LoadBalanced表示開啟負(fù)載均衡。
@Configurationpublic class ApplicationContextConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }}1.2 RestTemplate 遠程調(diào)用
@Slf4j@RestController@RequestMapping('/order')public class OrderController { @Autowired RestTemplate restTemplate; // 在ioc容器中獲取 @Value('${payment.url}') String paymentUrl; // 遠程調(diào)用的URL,保存在配置文件中,解耦 @GetMapping('/payment/get/{id}') public CommonResult<Payment> getPaymentById(@PathVariable('id') Long id) { CommonResult<Payment> result = restTemplate.getForObject(paymentUrl + '/payment/get/' + id, CommonResult.class); // get方法調(diào)用,并且返回封裝成 CommonResult 類型 log.info('Order 查詢 Payment,id:' + id); return result; }}
也可以使用getForEntity()方法,獲取整個響應(yīng),自己在響應(yīng)中獲取想要的內(nèi)容。
@GetMapping('/payment/getEntity/{id}') public CommonResult<Payment> getPaymentEntityById(@PathVariable('id') Long id) { ResponseEntity<CommonResult> entity = restTemplate.getForEntity(paymentUrl + '/payment/get/' + id, CommonResult.class); log.info('獲取到的信息是:' + entity.toString()); log.info('獲取到的StatusCode是:' + entity.getStatusCode()); log.info('獲取到的StatusCodeValue是:' + entity.getStatusCodeValue()); log.info('獲取到的Headers是:' + entity.getHeaders()); if (entity.getStatusCode().is2xxSuccessful()) { log.info('查詢成功:' + id); return entity.getBody(); } else { log.info('查詢失敗:' + id); return new CommonResult<>(CommonResult.FAIlURE, '查詢失敗'); } }
如果使用post方法,就將get改成post就好了。
1.3 配置文件url,可以寫具體的地址,表示直接調(diào)用該地址;也可以寫在eureka的服務(wù)名,首先在eureka中獲取該服務(wù)的所有地址,再通過LB選擇一個。
payment: url: 'http://CLOUD-PAYMENT-SERVICE'2. LoadBalancer
上面通過@LoadBalanced開啟了負(fù)載均衡。默認(rèn)使用輪詢算法,也可以修改成其他算法。
Class 算法 com.netflix.loadbalancer.RoundRobinRule 輪詢,默認(rèn)算法 com.netflix.loadbalancer.RandomRule 隨機算法,通過產(chǎn)生隨機數(shù)選擇服務(wù)器 com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略獲取服務(wù),如果獲取服務(wù)失敗則在指定時間內(nèi)會進行重試,獲取可用的服務(wù) WeightedResponseTimeRule 對RoundRobinRule的擴展,響應(yīng)速度越快的實例選擇權(quán)重越大,越容易被選擇 BestAvailableRule 會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務(wù),然后選擇一個并發(fā)量最小的服務(wù) AvailabilityFilteringRule 先過濾掉故障實例,再選擇并發(fā)較小的實例 ZoneAvoidanceRule 默認(rèn)規(guī)則,復(fù)合判斷server所在區(qū)域的性能和server的可用性選擇服務(wù)器
2.1 修改負(fù)載均衡算法如果想讓該算法只針對某個服務(wù),則不能將其放在ComponentScan夠得到的地方,否則會修改所有服務(wù)的負(fù)載均衡算法。因此,最好在外面再新建一個package,用來放這個LB
@Configurationpublic class MyRule { @Bean public IRule rule() { return new RandomRule(); }}
在主啟動類上,標(biāo)識一下服務(wù)與算法直接的映射關(guān)系
@SpringBootApplication@EnableEurekaClient@RibbonClient(name = 'CLOUD-PAYMENT-SERVICE', configuration = MyRule.class)public class OrderApplication80 { public static void main(String[] args) { SpringApplication.run(OrderApplication80.class, args); }}
如果嫌這種方法麻煩,也可以使用配置文件的方法
CLOUD-PAYMENT-SERVICE: # 服務(wù)名稱 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 算法選擇3. 負(fù)載均衡算法源碼
以默認(rèn)的RoundRobinRule作為閱讀的源碼,其他的源碼基本上很類似,只是修改的選擇服務(wù)器的代碼。
public interface IRule { Server choose(Object var1); // 選擇服務(wù)器,最重要的方法 void setLoadBalancer(ILoadBalancer var1); ILoadBalancer getLoadBalancer();}3.2 AbstractLoadBalancerRule
基本沒什么作用,只是將公共的部分提取了出來進行實現(xiàn)。
public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware { private ILoadBalancer lb; // ILoadBalancer接口,主要的功能就是獲取當(dāng)前服務(wù)器的狀態(tài)、數(shù)量等,為負(fù)載均衡算法提供計算的參數(shù) public AbstractLoadBalancerRule() { } public void setLoadBalancer(ILoadBalancer lb) { this.lb = lb; } public ILoadBalancer getLoadBalancer() { return this.lb; }}3.3 RoundRobinRule
簡單來說,就是通過一個計數(shù)器,實現(xiàn)了輪詢
public class RoundRobinRule extends AbstractLoadBalancerRule { private AtomicInteger nextServerCyclicCounter; // 原子類,用來保存一個計數(shù),記錄現(xiàn)在輪詢到哪了 private static final boolean AVAILABLE_ONLY_SERVERS = true; private static final boolean ALL_SERVERS = false; private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); public RoundRobinRule() { this.nextServerCyclicCounter = new AtomicInteger(0); // 初始化 } public RoundRobinRule(ILoadBalancer lb) { // 設(shè)置LoadBalancer this(); this.setLoadBalancer(lb); } public Server choose(ILoadBalancer lb, Object key) { // 最重要的方法,選擇服務(wù)器并返回 // 下面貼出來 } private int incrementAndGetModulo(int modulo) { // 對計數(shù)器進行修改,并返回一個選擇值,是輪詢算法的實現(xiàn) // 下面貼出來 } public Server choose(Object key) { // 接口的方法,在該類中調(diào)用了另一個方法實現(xiàn) return this.choose(this.getLoadBalancer(), key); } public void initWithNiwsConfig(IClientConfig clientConfig) {}}
簡單來說,該方法就是根據(jù)目前的狀態(tài),選擇一個服務(wù)器返回。
public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { // 如果沒有LoadBalancer,那就不白費功夫了 log.warn('no load balancer'); return null; } else { Server server = null; int count = 0; while(true) { if (server == null && count++ < 10) { // 嘗試十次,如果還找不到server就放棄了 List<Server> reachableServers = lb.getReachableServers(); // 通過LB獲取目前所有可獲取的服務(wù)器 List<Server> allServers = lb.getAllServers(); // 獲取實際上的所有服務(wù)器 int upCount = reachableServers.size(); // 獲取目前可獲得的服務(wù)器數(shù)量 int serverCount = allServers.size(); // 所有服務(wù)器的數(shù)量,這是取余的除數(shù) if (upCount != 0 && serverCount != 0) { // 如果目前有服務(wù)器且服務(wù)器可用 int nextServerIndex = this.incrementAndGetModulo(serverCount); // 最關(guān)鍵的選擇算法,將目前的的服務(wù)器數(shù)量放進去,返回一個選擇的號碼 server = (Server)allServers.get(nextServerIndex); // 根據(jù)下標(biāo)將服務(wù)器取出來 if (server == null) { // 如果取出來為空,表示目前不可用,則進入下一個循環(huán) Thread.yield(); } else { if (server.isAlive() && server.isReadyToServe()) { // 如果該服務(wù)器活著且可以被使用,則直接將其返回return server; } server = null; } continue; } log.warn('No up servers available from load balancer: ' + lb); return null;}if (count >= 10) { log.warn('No available alive servers after 10 tries from load balancer: ' + lb);}return server; } } }
簡單來說,就是將目前的計數(shù)器+1取余,獲取一個下標(biāo),并返回。為了避免高并發(fā)的危險,采用CAS的方法進行設(shè)置。
private int incrementAndGetModulo(int modulo) { int current; int next; do { current = this.nextServerCyclicCounter.get(); // 獲取當(dāng)前值 next = (current + 1) % modulo; // +1取余 } while(!this.nextServerCyclicCounter.compareAndSet(current, next)); // CAS,如果成功就返回,失敗就再來 return next; }
以上就是Spring Cloud調(diào)用Ribbon的步驟的詳細(xì)內(nèi)容,更多關(guān)于Spring Cloud調(diào)用Ribbon的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. CSS3實現(xiàn)動態(tài)翻牌效果 仿百度貼吧3D翻牌一次動畫特效2. ASP.NET MVC使用正則表達式驗證手機號碼3. JS實現(xiàn)購物車基本功能4. PHP開發(fā)注意安全問題總結(jié)5. 解決VUE 在IE下出現(xiàn)ReferenceError: Promise未定義的問題6. vue+springboot+element+vue-resource實現(xiàn)文件上傳教程7. Java App表達式語言最終版MVEL 2.0 發(fā)布8. 基于Python的自媒體小助手---登錄頁面的實現(xiàn)代碼9. vue自定義標(biāo)簽和單頁面多路由的實現(xiàn)代碼10. vue使用vue-quill-editor富文本編輯器且將圖片上傳到服務(wù)器的功能
