1. 程式人生 > 其它 >10.rabbion負載均衡和服務呼叫

10.rabbion負載均衡和服務呼叫

spring cloud ribbon是基於NetflixRibbon實現的一套客戶端(注意是客戶端)負載均衡工具

LB負載均衡(Load Balance)是什麼
    簡單的說就是將使用者的請求平攤到多個伺服器上,從而達到系統的HA(高可用)
    常見的負載均衡有軟體Nginx,LXS,硬體F5等
    
Ribbon本地負載均衡客戶端 VS Nginx服務端負載均衡區別
    1.Nginx是伺服器負載均衡,客戶所有請求都會交給Nginx,然後由Nginx實現轉發請求。即負載均衡是由服務端實現的。
    2.Ribbon是本地負載均衡,在呼叫微服務介面時候,會在註冊中心上獲取註冊服務列表後快取到JVM本地,從而在本地實現RPC遠端服務呼叫技術!
    
集中式LB:
    即在服務的消費方換和提供方之間使用獨立的LB設施(可以是硬體,如F5,也可以是軟體,如Nginx),由該設施負責將訪問請求通過某種策略轉發到
    服務的提供方!

程序內LB:
    將LB邏輯整合到消費方,消費方從服務註冊中心獲知到有哪些地址可用,然後自己再從這些地址中選擇出一個合適的伺服器!
    Ribbon就屬於程序內LB,它只是一個類庫,集成於消費方程序,消費方通過它獲取到服務提供方的地址!
Ribbon其實就是一個軟負載均衡的客戶端元件,它可以和其他所需請求的客戶端結合使用,和eureka結合只是其中的一個例項!
Ribbon在工作時分成兩步:
    1.先選擇EurekaServer,它優先選擇在同一個區域內負載較少的server
    2.再根據使用者指定的策略,在從server渠道的服務註冊列表中選擇一個地址
其中的ribbon提供了多種策略;比如輪詢、隨機和根據響應時間加權等等!

總結:重要
    ribbon:就是負載均衡+RestTemplate呼叫

RestTemplate的常用方法解析:

1.返回Object物件 2.getForEntity()返回的是ResponseEntity物件
Rabbion的核心元件IRule介面:
    IRule:根據特定演算法從服務列表中選取一個要訪問的服務(有以下7種演算法)
        1.com.netflix.loadbalancer.RoundRobinRule:輪詢
        2.com.netflix.loadbalancer.RandomRule:隨機
        3.com.netflix.loadbalancer.RetryRule:先安裝RoundRobinRule的策略獲取服務,如果獲取服務失敗,則在指定時間內進行重試,獲取可用服務
        4.WeightedResponseTimeRule:對RoundRobinRule的擴充套件,響應速度越快的示例選擇權重越大,月容易被選中
        5.BestAvailableRule:會先過濾掉多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
        6.AvailabilityFilteringRule:會先過濾掉故障例項,再選擇併發較小的例項
        7.ZoneAvoidanceRule:預設規則,複合判斷Server所在的區域的效能和server的可用性選擇伺服器


重點1:ribbon的負載均衡策略更換(ribbon的預設負載演算法是輪詢)
    pom檔案的解釋:
        因為在客戶端引入了:
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        spring-cloud-starter-netflix-eureka-client裡包含有
            <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
              <version>2.2.1.RELEASE</version>
              <scope>compile</scope>
            </dependency>
    如圖所示:無需再單獨引入ribbon的包
1.注意點,ribbon選擇自己的負載方法時,需要建立自己的配置類,該配置類不能在springboot的包掃描範圍內!
1.在自定義配置類中程式碼如下:
    @Configuration
    public class MyRule {
        @Bean
        public RandomRule getRandomRule()
            //重點1:隨機策略
            return new RandomRule();
        }
    }
    
2.在springboot啟動類上加上該註解:@RibbonClient  1.value:表示呼叫的服務在eureka的註冊名稱,configuration:是指自定義的配置類位置
    @SpringBootApplication
    @EnableEurekaClient
    @RibbonClient(value = "CLOUD-PROVIDER",configuration = MyRule.class)
    public class OrderMain80 {
        public static void main(String[] args) {
                SpringApplication.run(OrderMain80.class, args);
            }
    }
 

如何編寫自己的負載均衡

原理解析:
    1.主要用到的是:List<ServiceInstance> instances =discoveryClient.getInstances(serverName)根據服務註冊中心中的註冊服務名稱獲取服務例項
    2.根據下表獲取到具體的ServiceInstance服務例項,String uri = instances.get(index).getUri().toString(),獲取遠端呼叫的uri即(http://192.168.137)
    3.需要考慮的是代表訪問次數的數字執行緒安全問題,因為會有多個客戶端呼叫更改該數字:這裡涉及到了CAS和自旋解鎖等問題,等後面學習了JUC會明白
    
具體的程式碼設計如下:設計自己的輪詢演算法
    1.負載均衡的介面
        //傳入遠端服務的名稱,獲取到具體服務例項的URI
        public interface MyLB {
            String getUri(String serverName);
        }
    2.介面實現如下:這就是簡單的輪詢演算法,可以在此基礎上加上自己的邏輯等等,
        但重點是:
            1.discoveryClient獲取服務相關資訊
            2.AtomicInteger:CAS和自旋解鎖等JUC知識
        @Component
        @Slf4j
        public class MyLbImp implements MyLB{
            //重點1:使用discoveryClient獲取服務相關資訊
            @Resource
            private DiscoveryClient discoveryClient;
            //重點2:使用AtomicInteger執行緒安全的去自增數字
            private AtomicInteger atomicInteger;
        
            public MyLbImp() {
                this.atomicInteger = new AtomicInteger(0);
            }
        
            @Override
            public String getUri(String serverName) {
                List<ServiceInstance> instances = discoveryClient.getInstances(serverName);
                int size = instances.size();
                int current;
                int next;
                do {
                    current = atomicInteger.get();
                    next = current >= Integer.MAX_VALUE ? 0 : current + 1;
                } while (!atomicInteger.compareAndSet(current, next));
                log.info("current:" + current + " next:" + next);
                int index = next % size;
                String uri = instances.get(index).getUri().toString();
                log.info("訪問URI:" + uri);
                return uri;
            }
        }
    3.控制層呼叫:
        @Resource
        private MyLB myLB;
        public CommResult queryUserById(@PathVariable("id") int id){
            log.info("客戶端根據id查詢使用者,使用者id:"+id);
            //重點1:根據服務名稱獲取對應的輪詢訪問地址!
            String uri=myLB.getUri("PRODUCER-USER-API") + "/producer/queryUserById/" + id;
            log.info("訪問路徑:"+uri);
            CommResult result = restTemplate.getForObject(uri, CommResult.class);
            return  result;
        }