SpringCloudAlibaba 微服務講解(三)Nacos Discovery-服務治理

ityml發表於2022-03-25

3.1 服務治理

先來思考一個問題,通過上一章的操作,我們已經實現微服務之間的呼叫,但是我們把服務提供者的網路地址(ip,埠)等硬編碼到了程式碼中,這種做法存在許多問題:

  • 一旦服務提供者地址變化,就需要手工修改程式碼
  • 一旦是多個服務提供者,無法實現負載均衡功能
  • 一旦服務變得越來越多,人工維護呼叫關係困難

那麼應該怎麼解決問題呢,這時候和就需要通過註冊中心動態的實現服務治理

什麼是服務治理:

服務治理是為微服務架構中最核心的基本模組,用於實現各個微服務的自動化註冊與發現

  • 服務註冊:在服務治理框架中,都會構建一個註冊中心,每個服務單元向註冊中心登記自己提供服務的詳細資訊,並在註冊中心形成一張服務的清單,服務註冊中心需要以心跳的方式去監測清單中的服務是否可用,如果不可用,需要在服務清單中剔除不可用的服務
  • 服務發現:服務呼叫方向服務註冊中心諮詢服務,並獲取所有服務的例項清單,實現對具體服務例項的訪問

通過上面的呼叫圖會發現,除了微服務,還有一個元件是服務註冊中心,它是微服務架構中非常重要的一個元件,在微服務架構裡主要起到協調者的一個作用,註冊中心一般包含如下功能:

  1. 服務發現

    • 服務註冊:儲存服務提供者和服務呼叫者的資訊
    • 服務訂閱:服務呼叫者訂閱服務提供者的資訊,註冊中心向訂閱者推送提供者的資訊
  2. 服務配置

    • 配置訂閱:服務提供者和服務呼叫者訂閱微服務相關的配置
    • 配置下發:主動將配置推送給服務提供者和服務呼叫者
  3. 服務健康監測

    • 監測服務提供者的健康情況,如果發現異常,執行服務剔除

常見的註冊中心

  • Zookeeper

    Zookeeper十一個分散式協調服務框架,是Apache Hadoop 的一個子專案,它主要是用來解決分散式應用中經常遇到的一些資料管理問題,如:統一命名服務、狀態同步服務、叢集管理、分散式應用配置項的管理等

  • Eureka

    Erueka是SpringCloud Netflix的重要元件,主要作用就是做服務註冊與發現。但是現在已經閉源

  • Consul

    Consul是基於GO語言開發的開源工具,主要面向分散式,服務話的系統提供服務註冊、服務發現和配置管理的功能。Consul的功能都很實用,其中包括:服務註冊與發現、健康檢查、Key/Value儲存、多資料中心和分散式一致性保證等特徵。Consul本身只是一個二進位制的可執行檔案,所以安裝和部署都非常簡單,只需要從官網下載後,執行對應的啟動指令碼即可

  • Nacos

    Nacos是一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺。它是SpringCloud Alibaba 元件之一,負責服務註冊發現和服務配置,可以這樣認為 Naocs=Eureka+Config

3.2 Nacos簡介

Nacos致力於幫助您發現、配置和管理微服務。Nacos提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務後設資料及流量管理

從上面的介紹就可以看出,nacos的作用就是就是一個註冊中心,用來管理註冊上來的各個微服務

3.3 Nacos實戰入門

接下來,我們就在現有的環境中加入nacos,並將我們的兩個服務註冊上去。

3.3.1 搭建nacos環境

第一步:安裝nacos

下載地址:https://github.com/alibaba/nacos/releases
下載zip格式的安裝包,然後進行解壓縮操作

第二步:啟動nacos

# 切換目錄
cd nacos/bin
# 命令啟動
startup.cmd  -m standalone

第三步:訪問nacos

瀏覽器輸入:http://localhost:8848/nacos,即可訪問服務,預設賬號密碼是nacos/nacos

3.3.2 將商品微服務註冊到nacos

接下來開始修改shop-product模組的程式碼,將其註冊到nacos服務上

  1. 在pom.xml中新增nacos依賴

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.9.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>demo</name>
        <description>demo</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web-services</artifactId>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.79</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </dependency>
        </dependencies>
        <dependencyManagement>
    
            <dependencies>
                <!--整合Spring Cloud-->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.SR3</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!--整合Spring Cloud Alibaba-->
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>2.1.0.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  2. 新增配置

    spring.application.name=shop-product
    server.port=8090
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    
  3. 在主類上新增@EnableDiscoveryClient

    package com.example.demo;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    /**
     * @author chao.z
     */
    @SpringBootApplication
    @EnableDiscoveryClient
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }
    
  4. 啟動服務,觀察nacos的控制皮膚中是否有註冊上來的商品微服務

3.3.3 將訂單微服務註冊到nacos上

3.4 實現服務呼叫的負載均衡

3.4.1什麼是負載均衡

通俗的講,負載均衡就是將負載(工作任務,訪問請求)進行分攤到多個操作單元(伺服器,元件)上進行執行

根據負載均衡發生位置的不同,一版分為服務端負載均衡和客戶端負載均衡

服務端負載均衡指的時發生在服務提供者一方,比如常見的nginx負載均衡

而客戶端負載均衡指的是發生在服務請求的一方,也就是在傳送請求之前已經選好了由哪個例項處理請求

我們在微服務呼叫關係中,一版會選擇客戶端負載均衡,也就是在服務呼叫的乙方決定服務由哪個提供者執行

3.4.2 自定義實現負載均衡

  1. 通過idea 在啟動一個shop-product微服務,設定其埠為8082

  1. 通過nacos檢視微服務的啟動情況

  1. 修改shop-order的程式碼

    @RestController
    @Slfj
    public class OrderController{
    	
    	@Autowired
    	private RestTemplate restTemplate;
    	
    	@Autowired
    	private OrderService orderService;
    	
    	@Autowired
    	private DiscoveryClient discoveryClient;
    	
    	@GetMapping("order/prod/{pid}")
    	public Order order(@Pathvariable("pid") Integer pid){
    		List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
    		int index = new Random().nextInt(instances.size());
    		ServiceInstance serviceInstance = instances.get(index);
    		String url  = serviceInstance.getHost() + ":" + serviceInstance.getPort();
    		Product product = restTemplate.getForObject("http://"+ url + "/product/" + pid ,Product.class);
        
        Order order  = new Order();
        order.setUid(1);
        order.setUsername("測試使用者");
        order.setPid(product.getPname());
        order.setPprice(product.getPprice());
        order.setNum(1);
        orderService.save(order);
        return order;
    	}
    
    }
    
  2. 啟動兩個服務提供者和一個服務消費者,多訪問幾次消費者測試效果

3.4.3 基於Ribbon實現負載均衡

Ribbon 是SpringCloud的一個元件,它可以讓我們使用一個註解就能輕鬆的搞定負載均衡

第一步: 在RestTemplate 剩生成方法上新增@LoadBalanced註解

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
	return new RestTemplate();
}

第二步:修改服務呼叫的方法

@RestController
@Slfj
public class OrderController{
	
	@Autowired
	private RestTemplate restTemplate;
	
	@Autowired
	private OrderService orderService;
	
	@Autowired
	private DiscoveryClient discoveryClient;
	
	@GetMapping("order/prod/{pid}")
	public Order order(@Pathvariable("pid") Integer pid){
		String url  = serviceInstance.getHost() + ":" + serviceInstance.getPort();
		Product product = restTemplate.getForObject("http://"+ url + "/product/" + pid ,Product.class);
    
    Order order  = new Order();
    order.setUid(1);
    order.setUsername("測試使用者");
    order.setPid(product.getPname());
    order.setPprice(product.getPprice());
    order.setNum(1);
    orderService.save(order);
    return order;
	}

}

Ribbon支援的負載均衡策略

Ribbon內建了多種負載均衡策略,內部負載均衡的頂級介面為com.netflix.loadbalancer.IRule,具體的負載策略如下圖所示:

我們通過修改配置類調整Ribbon的負載均衡策略,具體程式碼如下:

service-product: #呼叫的提供者的名稱
ribbon:
NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule

3.5基於Feign實現負載均衡

3.5.1什麼是Feign

Feign是Spring Cloud提供的一個宣告式的偽Http客戶端,它使得呼叫遠端服務就想呼叫本地服務一樣簡單,只需要建立一個藉口並新增一個註解即可

Nacos很好的相容了Feign,Feign 預設整合了Ribbon,所以在Nacos下使用Feign預設就實現了負載均衡的效果

3.5.2Feign的使用

  1. 接入Feign的依賴

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
        <version>2.2.3.RELEASE</version>
    </dependency>
    
  2. 在主類上新增@Feign的註解

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients // 開啟Fegin
    public class OrderApplication{}
    
  3. 建立一個service,並使用Fegin實現微服務呼叫

    //宣告呼叫的提供者的name
    @FeignClient("service-product")
    public insterface ProductService{
      // 指定呼叫提供者的哪個方法
      //@FeignClient+A@GetMapping 就是一個完整的請求路徑 http://service-product/product/{pid}
      @Getmapping(value = "/product/{pid}")
      Product findByPid(@PathVariable("pid") Integer pid);
    }
    
  4. 修改controller程式碼,並啟動驗證

    @RestController
    @Sl4j
    public class OrderController{
    
      @Autowired
    	private OrderService orderService;
      
      @Autowired
      private ProductService productService;
      
      @GetMapping("order/prod/{pid}")
    	public Order order(@Pathvariable("pid") Integer pid){
    		
    		Product product = productService.findByPid(pid);
        
        Order order  = new Order();
        order.setUid(1);
        order.setUsername("測試使用者");
        order.setPid(product.getPname());
        order.setPprice(product.getPprice());
        order.setNum(1);
        orderService.save(order);
        return order;
    	}
    }
    
  5. 重啟Order 微服務,檢視效果

相關文章