前言:在上一篇文章中我們介紹了關于負載輪詢算法的原理以及看了源代碼,而本章節內容則是著重于我們自己手寫一個負載輪詢算法
1、分別編寫provider-payment8001、provider-payment8002這兩個子項目的PaymentController類,增加一個/payment/lb接口
provider-payment8001
package com.ken.springcloud.controller;import com.ken.springcloud.entities.CommonResult;
import com.ken.springcloud.entities.Payment;
import com.ken.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;@RestController
@Slf4j
public class PaymentController {@Resourceprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@Resourceprivate DiscoveryClient discoveryClient;@PostMapping("/payment/insert")public CommonResult insert(@RequestBody Payment payment) {int result = paymentService.insert(payment);log.info("插入結果{}",result);if(result > 0) {return new CommonResult(200,"插入數據庫成功,提供服務的端口號為" + serverPort,result);}else {return new CommonResult(500,"插入數據庫失敗",result);}}@GetMapping("/payment/get/{id}")public CommonResult insert(@PathVariable("id") Long id) {Payment payment = paymentService.getPaymentById(id);log.info("查詢結果{}",payment);if(payment != null) {return new CommonResult(200,"查詢成功,提供服務的端口號為" + serverPort,payment);}else {return new CommonResult(500,"沒有對應的數據,查詢失敗,查詢id" + id,payment);}}@GetMapping("/payment/discovery")public Object discovery() {//獲取eureka內的服務List<String> services = discoveryClient.getServices();for (String service : services) {log.info("***service:" + service);}//獲取服務名為CLOUD-PAYMENT-SERVICE下的實例List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");for (ServiceInstance instance : instances) {log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());}return this.discoveryClient;}@GetMapping("/payment/lb")public String getPaymentLB() {//返回當前服務的端口號return serverPort;}}
provider-payment8002?
package com.ken.springcloud.controller;import com.ken.springcloud.entities.CommonResult;
import com.ken.springcloud.entities.Payment;
import com.ken.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;@RestController
@Slf4j
public class PaymentController {@Resourceprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@PostMapping("/payment/insert")public CommonResult insert(@RequestBody Payment payment) {int result = paymentService.insert(payment);log.info("插入結果{}",result);if(result > 0) {return new CommonResult(200,"插入數據庫成功,提供服務的端口號為" + serverPort,result);}else {return new CommonResult(500,"插入數據庫失敗",result);}}@GetMapping("/payment/get/{id}")public CommonResult insert(@PathVariable("id") Long id) {Payment payment = paymentService.getPaymentById(id);log.info("查詢結果{}",payment);if(payment != null) {return new CommonResult(200,"查詢成功,提供服務的端口號為" + serverPort,payment);}else {return new CommonResult(500,"沒有對應的數據,查詢失敗,查詢id" + id,payment);}}@GetMapping("/payment/lb")public String getPaymentLB() {//返回當前服務的端口號return serverPort;}}
2、在com.ken.springcloud包下新建名為lb的包,用于存儲負載均衡相關的代碼?
效果圖:
3、在lb包下新建一個名為LoadBalancer的接口類
效果圖:
4、編寫LoadBalancer接口類
package com.ken.springcloud.lb;import org.springframework.cloud.client.ServiceInstance;import java.util.List;public interface LoadBalancer {ServiceInstance instances(List<ServiceInstance> sserviceINstances);}
5、在lb包下新建一個名為MyBL的類
效果圖:
6、編寫MyLB類,使其實現LoadBalancer接口類
package com.ken.springcloud.lb;import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;@Component
public class MyLB implements LoadBalancer{private AtomicInteger atomicInteger = new AtomicInteger(0);/*** 獲取當前是第幾次的請求* @return*/public final int getAndIncrement() {//atomicInteger的期望值int current;int next;do {current = this.atomicInteger.get();next = current >= Integer.MAX_VALUE ? 0 : current + 1;}while (!this.atomicInteger.compareAndSet(current,next));//比較atomicInteger對象當前的值和期望值current,如果atomicInteger對象當前的值和期望值current相同,則把atomicInteger對象當前的值修改為next,并返回trueSystem.out.println("next:" + next);return next;}@Overridepublic ServiceInstance instances(List<ServiceInstance> serviceInstances) {//通過請求數模以提供服務的服務數獲取需要提供服務的服務實例下標int index = getAndIncrement() % serviceInstances.size();//返回提供服務的服務實例return serviceInstances.get(index);}
}
7、編寫consumer-order80這個子項目的OrderController類,增加一個/payment/lb接口
package com.ken.springcloud.controller;import com.ken.springcloud.entities.CommonResult;
import com.ken.springcloud.entities.Payment;
import com.ken.springcloud.lb.LoadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import javax.annotation.Resource;
import java.net.URI;
import java.util.List;@RestController
@Slf4j
public class OrderController {//public static final String PAYMENT_URL = "http://localhost:8001";public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";@Resourceprivate RestTemplate restTemplate;@Resourceprivate LoadBalancer loadBalancer;@Resourceprivate DiscoveryClient discoveryClient;@PostMapping("/consumer/insert")public CommonResult<Payment> insert(Payment payment) {log.info("payment:{}",payment);return restTemplate.postForObject(PAYMENT_URL + "/payment/insert",payment,CommonResult.class);}/*** 返回對象為響應體中數據轉化成的對象,基本上可以理解為Json* @param id* @return*/@GetMapping("/consumer/get/{id}")public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id,CommonResult.class);}/*** 返回對象為ResponseEntity對象,包含了響應中的一些重要信息,比如響應頭、響應狀態碼、響應體等* @param id* @return*/@GetMapping("/consumer/getForEntity/{id}")public CommonResult<Payment> getPayment2(@PathVariable("id") Long id) {ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id,CommonResult.class);if(entity.getStatusCode().is2xxSuccessful()) {return entity.getBody();}else {return new CommonResult<>(500,"異常");}}@GetMapping("/consumer/payment/lb")public String getPaymentLB() {//獲取服務名為CLOUD-PAYMENT-SERVICE下的實例List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");if (instances == null || instances.size() <= 0) {return null;}//獲取提供接口服務的服務實例ServiceInstance serviceInstance = loadBalancer.instances(instances);//獲取接口地址URI uri = serviceInstance.getUri();//請求服務接口return restTemplate.getForObject(uri + "/payment/lb",String.class);}}
8、分別啟動eureka-server7001、eureka-server7002、provider-payment8001、provider-payment8002、consumer-order80,然后在瀏覽器地址欄輸入
http://localhost/consumer/payment/lb?并回車調用接口,可以看到接口調用成功,并會按照預想的那樣輪詢地分別訪問provider-payment8001和provider-payment8002服務
效果圖: