Spring Cloud學習

Spring Cloud學習

標籤(空格分隔): 微服務

1. Spring-boot

Restful API

@RestController
public class DemoController {


    @GetMapping("/hello")
    public String demoRest(){

        return "hello";
    }

}

pom.xml:

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ycc.cloud</groupId>
    <artifactId>cloud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud</name>
    <description>cloud</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourseEncoding>UTF-8</project.build.sourseEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

MockMvc Test

@RunWith(SpringRunner.class)
@SpringBootTest
@WebAppConfiguration
public class DemoControllerTest {
    MockMvc mvc;

    @Before
    public void setUp() {
        mvc = MockMvcBuilders.standaloneSetup(new DemoController()).build();
    }

    @Test
    public void demoRest() throws Exception {
        /**
         * 1、mockMvc.perform執行一個請求。
         * 2、MockMvcRequestBuilders.get("XXX")構造一個請求。
         * 3、ResultActions.param添加請求傳值
         * 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))設置返回類型
         * 5、ResultActions.andExpect添加執行完成後的斷言。
         * 6、ResultActions.andDo添加一個結果處理器,表示要對結果做點什麼事情
         *   比如此處使用MockMvcResultHandlers.print()輸出整個響應結果信息。
         * 5、ResultActions.andReturn表示執行完成後返回相應的結果。
         */

        mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)
        ).andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("hello"));
    }
}

多環境配置

多環境配置文件名需要滿足application-{profile}.properties的格式,其中{profile}對應環境標識.

  • application-dev.properties 開發
  • application-dev.properties 測試
  • application-dev.properties 生產

application.properties中通過spring.profile.active屬性指定


2. Spring Cloud Erueka

服務治理

  • 服務註冊
    在服務治理框架中,通常都會構建一個註冊中心,每個服務單元想註冊中心登記自己提供的服務,將主機與端口、版本號、通信協議等一些附加信息告知註冊中心,註冊中心按服務名分類組織服務清單。
    | 服務名 | 位置 |
    | ------ | :---------------------------------------------------------- |
    | 服務A | 192.168.0.100:8000、192.168.0.101:8000 |
    | 服務B | 192.168.0.1000:9000、192.168.0.101:9000、192.168.0.102:9000 |

  • 服務發現
    由於在服務治理框架下運作,服務間的調用不再通過指定具體的實例地址來實現,而是通過向服務名發起請求調用實現。所以,服務調用方在調用服務提供方接口的時候,並不知道具體的服務實例位置。因此,調用方需要向服務註冊中心諮詢服務,並獲取所有服務的實例清單,以實現對具體服務實例的訪問。

Netflix Erueka

Eureka服務端,我們也稱服務註冊中心。它同其他服務註冊中心一樣,支持高可用配置。它依託於強一致性1提供良好的服務實例可用性,可以應對多種不同的故障場景。如果Eureka以集羣模式部署,當集羣中有分片2出現故障時,那麼Eureka就轉入自我保護模式。它允許在分片故障期間繼續提供服務的發現和註冊,當故障分片恢復運行時,集羣中的其他分片會把他們的狀態再次同步回來。以在AWS上的實踐爲例,Netflix推薦每個可用的區域運行一個Eureka服務端,通過它來形成集羣。不同可用區域的服務註冊中心通過異步模式互相複製各自的狀態,這意味着在任意給定的時間點每個實例關於所有服務的狀態是有細微差別的。
Eureka客戶端,主要處理服務的註冊與發現。客戶端服務通過註解和參數配置的方式,嵌入在客戶端應用程序的代碼中,在應用程序運行時,Eureka客戶端向註冊中心註冊自身提供的服務並週期性地發送心跳來更新它的服務租約。同時,它也能從服務端查詢當前註冊的服務信息並把他們緩存到本地並週期性地刷新服務狀態。

搭建服務註冊中心

  • pom.xml
 <parent>
        <groupId>com.cjl</groupId>
        <artifactId>line-tb-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>line-eureka</artifactId>
    <name>line-eureka</name>
    <description>註冊中心</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
        </dependency>

    </dependencies>

    <build>
        <finalName>line-eureka</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.cjl.LineEurekaApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  • 通過@EnableEurekaServer註解啓動一個服務註冊中心提供給其他應用進行對話。這一步非常簡單,只需要在一個普通的Spring Boot應用中添加這個註解就能開啓此功能,
@EnableEurekaServer
@SpringBootApplication
public class LineEurekaApplication {
    public static void main(String[] args) {

        SpringApplication.run(LineEurekaApplication.class, args);
    }
}
  • 在默認設置下,該服務註冊中心也會將自己作爲客戶端來嘗試註冊它自己,所以我們需要禁用它的客戶端註冊行爲,只需要在application。properties中增加如下配置即可:
eureka:
  instance:
    hostname: 127.0.0.1
    prefer-ip-address: true
  server:
    wait-time-in-ms-when-sync-empty: 0
    eviction-interval-timer-in-ms: 60000 #清理間隔(默認毫秒)
    enable-self-preservation: true #false 關閉自我保護,不管如何都要剔除心跳檢測異常的服務
  client:
    register-with-eureka: false # 表示是否將自己註冊到Eureka Server,默認爲true
    fetch-registry: false  # 表示是否從Eureka Server獲取註冊信息,默認爲true
# eureka.client.serviceUrl.defaultZone :設置與Eureka Server交互的地址,查詢服務和註冊服務都需要依賴這個地址。
    service-url:
      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
  • 完成上面的配置後,啓動並訪問localhost:8765 可以看到Eureka信息面板,其中Instances currently registered with Eureka;欄爲空的,說明該註冊中心還沒有註冊任何服務。

    在這裏插入圖片描述

  • 註冊服務提供者

pom.xml

 <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
                <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
修改/hello請求處理接口,通過注入[^@Autowired]DiscoveryClient對象,在日誌中打印出服務的相關信息。
@RestController
public class HelloController {

    @Autowired
    private DiscoveryClient client;

    @GetMapping("/hello")
    public String index() {
        List<String> services = client.getServices();
        services.forEach(System.out::println);
        return "hello";
    }
}

​ 同時需要在application.yml中將自己註冊到Erueka註冊中心中。

#端口號
server:
  port: 8609

spring:
  #項目名稱
  application:
    name: demo-client

#服務註冊
eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://admin:123456@${eureka.instance.hostname}:8765/eureka/

​ 然後,在主類上通過加上@EnableDiscoveryClient 註解,激活Eureka中的DiscoveryClient實現(自動化配置,創建DiscoveryClient接口針對Eureka客戶端的EurekaDiscoveryClient實例),才能實現上述Controller中對服務信息的輸出。

@SpringBootApplication
@EnableEurekaClient
public class DemoServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoServiceApplication.class, args);
    }
}

在這裏插入圖片描述

​ 分別啓動服務註冊中心以及這裏改造的demo-client服務。在client控制檯中打印了該服務的註冊信息,表示服務註冊成功。

​ 也可以通過訪問Eureka信息面板,在Instances currently registered with Eureka中看到服務的註冊信息。

在這裏插入圖片描述

​ 通過訪問localhost:8609/hello,直接向服務器發起請求,在控制檯可以看到如下輸出:

在這裏插入圖片描述

​ 這些輸出內容就是之前我們在HelloController中注入DiscoveryClient接口對象,從服務註冊中心獲取的服務相關信息。

高可用註冊中心

待添加!!!


服務發現與消費

​ Ribbon是一個基於HTTP和TCP的客戶端負載均衡器,它可以在通過客戶端中配置的ribbonServerList服務端列表去輪詢訪問以達到均衡負載的作用。當Ribbon與Eureka聯合使用是,Ribbon的服務實例清單RibbonServerList會被DiscoveryEnabledNIWSServerList重寫,擴展成從Eureka註冊中心獲取服務端列表。同時它會用NIWSDiscoveryPing取代IPing,它將職責委託給Eureka來確定服務端是否已經啓動。

  • 使用java -Dfile.encoding=UTF-8 -jar demo-0.0.1-SNAPSHOT.jar --server.port=XXX啓動2個服務,注意改變端口。

在這裏插入圖片描述

  • 創建一個SpringBoot的基礎工程來實現服務消費者。

    pom.xml

     <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    

    application.yml

    
    
    #端口號
    server:
      port: 8705
    
    spring:
      #項目名稱
      application:
        name: ribbon-consumer
    
    #服務註冊
    eureka:
      instance:
        hostname: localhost
        prefer-ip-address: true
      client:
        service-url:
          defaultZone: http://admin:123456@${eureka.instance.hostname}:8765/eureka/
    

    ​ 啓動ribbon-consumer應用後,可以在Eureka信息面板上看到,除了當前的DEMO-CLIENT外,還多個RIBBON-CONSUMER服務。

    在這裏插入圖片描述

    • 通過 localhost:8705/ribbon-consumer發起GET請求,成功返回hello,對應的在後臺控制檯可以看到如下信息,Ribbon輸出了當前客戶端維護的DEMO-CLIENT的服務列表情況。其中包含了各個實例的位置,Ribbon就是按照此信息進行輪詢訪問,以實現基於客戶端的負載均衡。還輸出了各個實例的請求總數量、第一次連接信息、上一次連接信息、總的請求失敗數量等有用的信息。

      在這裏插入圖片描述

      ​ 嘗試多次發送請求,會發現兩個客戶端交替打印如下信息。以此也可以判斷當前ribbon-consumer對DEMO-CLIENT的調用是否是負載均衡的。

      在這裏插入圖片描述

Eureka詳解

待添加



  1. 分佈式系統之數據分片 ↩︎

  2. 租約機制簡介 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章