1. 程式人生 > >spring-cloud-kubernetes的服務發現和輪詢實戰(含熔斷)

spring-cloud-kubernetes的服務發現和輪詢實戰(含熔斷)

本文是《spring-cloud-kubernetes實戰系列》的第四篇,主要內容是在kubernetes上部署兩個應用:Web-Service和Account-Service,通過spring-cloud-kubernetes提供的註冊發現能力,實現Web-Service呼叫Account-Service提供的http服務;

系列文章列表

  1. 《spring-cloud-kubernetes官方demo執行實戰》
  2. 《你好spring-cloud-kubernetes》
  3. 《spring-cloud-kubernetes背後的三個關鍵知識點》
  4. 《spring-cloud-kubernetes的服務發現和輪詢實戰(含熔斷)》
  5. 《spring-cloud-kubernetes與SpringCloud Gateway》
  6. 《spring-cloud-kubernetes與k8s的configmap》

全文概覽

本文由以下段落組成:

  1. 環境資訊
  2. 常見的SpringCloud註冊發現服務一覽
  3. 分析kubernetes上如何實現服務註冊發現
  4. 本章實戰原始碼下載連結
  5. 實戰開發Account-Service服務(服務提供方)
  6. 實戰開發Web-Service服務(服務消費方)
  7. 擴容驗證ribbon輪詢能力
  8. 驗證熔斷能力

環境資訊

本次實戰的環境和版本資訊如下:

  1. 作業系統:CentOS Linux release 7.6.1810
  2. minikube:1.1.1
  3. Java:1.8.0_191
  4. Maven:3.6.0
  5. fabric8-maven-plugin外掛:3.5.37
  6. spring-cloud-kubernetes:1.0.1.RELEASE

上面的linux、minikube、java、maven,請確保已準備好,linux環境下minikube的安裝和啟動請參考《Linux安裝minikube指南 》。

常見的SpringCloud註冊發現服務一覽

SpringCloud環境最重要的功能是註冊發現服務,因此將SpringCloud應用遷移到kubernetes環境時,開發者最關心的問題是在kubernetes上如何將自身服務暴露出去,以及如何呼叫其他微服務。

先看看普通SpringCloud環境下的註冊發現,下圖來自spring官方部落格,地址是:https://spring.io/blog/2015/07/14/microservices-with-spring,

由上圖可見,應用Account-Service將自己註冊到Eureka,這樣Web-Service用"account-service"就能在Eureka找到Account-Service服務的地址,然後順利傳送RestFul請求到Account-Service,用上其提供的服務。

分析kubernetes上如何實現服務註冊發現

如果將上面的Web-Service和Account-Service兩個應用遷移到kubernetes上之後,註冊發現機制變成了啥樣呢?
第一種:沿用上圖的方式,將Eureka也部署在kubernetes上,這樣的架構和不用kubernetes時沒有啥區別;
第二種,就是今天要實戰的內容,使用spring-cloud-kubernetes框架,該框架可以呼叫kubernetes的原生能力來為現有SpringCloud應用提供服務,架構如下圖所示:

上圖表明,Web-Service應用在呼叫Account-Service應用的服務時,會用okhttp向API Server請求服務列表,API Server收到請求後會去etcd取資料返回給Web-Service應用,這樣Web-Service就有了Account-Service的資訊,可以向Account-Service的多個Pod輪詢發起請求;

上圖有個細節請注意:WebService應用並不是直接將請求傳送給Account-Service在kubernetes建立的service,而是直接傳送到具體的Pod上了,之所以具有這個能力,是因為spring-cloud-kubernetes框架通過service拿到了Account-Service對應的所有Pod資訊(endpoint),此邏輯可以參考原始碼KubernetesServerList.java,如下所示:

public List<Server> getUpdatedListOfServers() {
        //用namespace和serviceId做條件,得到該服務對應的所有節點(endpoints)資訊
        Endpoints endpoints = this.namespace != null
                ? this.client.endpoints().inNamespace(this.namespace)
                        .withName(this.serviceId).get()
                : this.client.endpoints().withName(this.serviceId).get();

        List<Server> result = new ArrayList<Server>();
        if (endpoints != null) {

            if (LOG.isDebugEnabled()) {
                LOG.debug("Found [" + endpoints.getSubsets().size()
                        + "] endpoints in namespace [" + this.namespace + "] for name ["
                        + this.serviceId + "] and portName [" + this.portName + "]");
            }
            //遍歷所有的endpoint,取出IP地址和埠,構建成Server例項,放入result集合中
            for (EndpointSubset subset : endpoints.getSubsets()) {

                if (subset.getPorts().size() == 1) {
                    EndpointPort port = subset.getPorts().get(FIRST);
                    for (EndpointAddress address : subset.getAddresses()) {
                        result.add(new Server(address.getIp(), port.getPort()));
                    }
                }
                else {
                    for (EndpointPort port : subset.getPorts()) {
                        if (Utils.isNullOrEmpty(this.portName)
                                || this.portName.endsWith(port.getName())) {
                            for (EndpointAddress address : subset.getAddresses()) {
                                result.add(new Server(address.getIp(), port.getPort()));
                            }
                        }
                    }
                }
            }
        }
        else {
            LOG.warn("Did not find any endpoints in ribbon in namespace ["
                    + this.namespace + "] for name [" + this.serviceId
                    + "] and portName [" + this.portName + "]");
        }
        return result;
    }

理論分析已經完成,接下來就開始實戰吧

原始碼下載

如果您不打算寫程式碼,也可以從GitHub上下載本次實戰的原始碼,地址和連結資訊如下表所示:

名稱 連結 備註
專案主頁 https://github.com/zq2599/blog_demos 該專案在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該專案原始碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該專案原始碼的倉庫地址,ssh協議


這個git專案中有多個資料夾,本章的Account-Service原始碼在spring-cloud-k8s-account-service資料夾下,Web-Service原始碼在spring-cloud-k8s-web-service資料夾下,如下圖紅框所示:

下面是詳細的編碼過程;

開發和部署Account-Service服務

Account-Service服務是個很普通的springboot應用,和spring-cloud-kubernetes沒有任何關係:

  1. 通過maven建立一個springboot應用,artifactId是account-service,pom.xml內容如下:
<?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 http://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.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>account-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>account-service</name>
    <description>Demo project for Spring Cloud service provider run in kubernetes</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.1.1.RELEASE</spring-boot.version>
        <maven-checkstyle-plugin.failsOnError>false</maven-checkstyle-plugin.failsOnError>
        <maven-checkstyle-plugin.failsOnViolation>false</maven-checkstyle-plugin.failsOnViolation>
        <maven-checkstyle-plugin.includeTestSourceDirectory>false</maven-checkstyle-plugin.includeTestSourceDirectory>
        <maven-compiler-plugin.version>3.5</maven-compiler-plugin.version>
        <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
        <maven-failsafe-plugin.version>2.18.1</maven-failsafe-plugin.version>
        <maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>
        <fabric8.maven.plugin.version>3.5.37</fabric8.maven.plugin.version>
        <springcloud.version>2.1.1.RELEASE</springcloud.version>
    </properties>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${springcloud.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <!--skip deploy -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>${maven-deploy-plugin.version}</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <skipTests>true</skipTests>
                    <!-- Workaround for https://issues.apache.org/jira/browse/SUREFIRE-1588 -->
                    <useSystemClassLoader>false</useSystemClassLoader>
                </configuration>
            </plugin>
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>fabric8-maven-plugin</artifactId>
                <version>${fabric8.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>fmp</id>
                        <goals>
                            <goal>resource</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>kubernetes</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.fabric8</groupId>
                        <artifactId>fabric8-maven-plugin</artifactId>
                        <version>${fabric8.maven.plugin.version}</version>
                        <executions>
                            <execution>
                                <id>fmp</id>
                                <goals>
                                    <goal>resource</goal>
                                    <goal>build</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <enricher>
                                <config>
                                    <fmp-service>
                                        <type>NodePort</type>
                                    </fmp-service>
                                </config>
                            </enricher>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

由上面的pom.xml內容可見,account-service應用是個簡單的web應用,和SpringCloud、spring-cloud-kubernetes都沒有任何關係,和其他springboot唯一的不同就是用到了fabric8-maven-plugin外掛,可以方便的將應用部署到kubernetes環境;

  1. application.yml內容如下,依舊很簡單:
spring:
  application:
    name: account-service

server:
  port: 8080
  1. 對外提供服務的是AccountController ,方法getName返回了當前容器的hostname,方法health用於響應kubernetes的兩個探針,方法ribbonPing用於響應使用了ribbon服務的呼叫方,它們會呼叫這個介面來確定當前服務是否正常:
@RestController
public class AccountController {

    private static final Logger LOG = LoggerFactory.getLogger(AccountController.class);

    private final String hostName = System.getenv("HOSTNAME");

    /**
     * 探針檢查響應類
     * @return
     */
    @RequestMapping("/health")
    public String health() {
        return "OK";
    }

    @RequestMapping("/")
    public String ribbonPing(){
        LOG.info("ribbonPing of {}", hostName);
        return hostName;
    }

    /**
     * 返回hostname
     * @return 當前應用所在容器的hostname.
     */
    @RequestMapping("/name")
    public String getName() {
        return this.hostName
                + ", "
                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}
  1. 將上述工程的原始碼放在minikube機器上,確保maven設定正常,然後在pom.xml檔案所在目錄執行以下命令,即可編譯構建工程並部署到kubernetes上:
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes

執行成功後控制檯輸出如下:

...
[INFO] Installing /usr/local/work/k8s/ribbon/spring-cloud-k8s-account-service/target/classes/META-INF/fabric8/kubernetes.json to /root/.m2/repository/com/bolingcavalry/account-service/0.0.1-SNAPSHOT/account-service-0.0.1-SNAPSHOT-kubernetes.json
[INFO] 
[INFO] <<< fabric8-maven-plugin:3.5.37:deploy (default-cli) < install @ account-service <<<
[INFO] 
[INFO] 
[INFO] --- fabric8-maven-plugin:3.5.37:deploy (default-cli) @ account-service ---
[INFO] F8: Using Kubernetes at https://192.168.121.133:8443/ in namespace default with manifest /usr/local/work/k8s/ribbon/spring-cloud-k8s-account-service/target/classes/META-INF/fabric8/kubernetes.yml 
[INFO] Using namespace: default
[INFO] Updating a Service from kubernetes.yml
[INFO] Updated Service: target/fabric8/applyJson/default/service-account-service.json
[INFO] Using namespace: default
[INFO] Updating Deployment from kubernetes.yml
[INFO] Updated Deployment: target/fabric8/applyJson/default/deployment-account-service.json
[INFO] F8: HINT: Use the command `kubectl get pods -w` to watch your pods start up
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.941 s
[INFO] Finished at: 2019-06-16T19:00:51+08:00
[INFO] ------------------------------------------------------------------------
  1. 檢查kubernetes上的部署和服務是否正常:
[root@minikube spring-cloud-k8s-account-service]# kubectl get deployments
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
account-service   1/1     1            1           69m
[root@minikube spring-cloud-k8s-account-service]# kubectl get services
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
account-service   NodePort    10.105.157.201   <none>        8080:32596/TCP   69m
kubernetes        ClusterIP   10.96.0.1        <none>        443/TCP          8d
  1. minikube的service命令可以得到指定服務的訪問地址:
[root@minikube spring-cloud-k8s-account-service]# minikube service account-service --url
http://192.168.121.133:32596

可見account-service的服務可以通過這個url訪問:http://192.168.121.133:32596

  1. 用瀏覽器訪問地址:http://192.168.121.133:32596/name ,如下圖所示,可以正常訪問account-service提供的服務:

    現在account-service服務已經就緒,接下來是開發和部署web-service應用。

    開發和部署Web-Service服務

    Web-Service服務是個springboot應用,用到了spring-cloud-kubernetes提供的註冊發現能力,以輪詢的方式訪問指定服務的全部pod:
  2. 通過maven建立一個springboot應用,artifactId是web-service,pom.xml內容如下,要重點關注的是spring-cloud-starter-kubernetes-ribbon的依賴:
<?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 http://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.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>web-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>web-service</name>
    <description>Demo project for Spring Cloud service consumer run in kubernetes</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.1.1.RELEASE</spring-boot.version>
        <maven-checkstyle-plugin.failsOnError>false</maven-checkstyle-plugin.failsOnError>
        <maven-checkstyle-plugin.failsOnViolation>false</maven-checkstyle-plugin.failsOnViolation>
        <maven-checkstyle-plugin.includeTestSourceDirectory>false</maven-checkstyle-plugin.includeTestSourceDirectory>
        <maven-compiler-plugin.version>3.5</maven-compiler-plugin.version>
        <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
        <maven-failsafe-plugin.version>2.18.1</maven-failsafe-plugin.version>
        <maven-surefire-plugin.version>2.21.0</maven-surefire-plugin.version>
        <fabric8.maven.plugin.version>3.5.37</fabric8.maven.plugin.version>
        <springcloud.kubernetes.version>1.0.1.RELEASE</springcloud.kubernetes.version>
        <springcloud.version>2.1.1.RELEASE</springcloud.version>
    </properties>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-core</artifactId>
            <version>${springcloud.kubernetes.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-discovery</artifactId>
            <version>${springcloud.kubernetes.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
            <version>${springcloud.kubernetes.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>${springcloud.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>${springcloud.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <!--skip deploy -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>${maven-deploy-plugin.version}</version>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <skipTests>true</skipTests>
                    <!-- Workaround for https://issues.apache.org/jira/browse/SUREFIRE-1588 -->
                    <useSystemClassLoader>false</useSystemClassLoader>
                </configuration>
            </plugin>
            <plugin>
                <groupId>io.fabric8</groupId>
                <artifactId>fabric8-maven-plugin</artifactId>
                <version>${fabric8.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>fmp</id>
                        <goals>
                            <goal>resource</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>kubernetes</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.fabric8</groupId>
                        <artifactId>fabric8-maven-plugin</artifactId>
                        <version>${fabric8.maven.plugin.version}</version>
                        <executions>
                            <execution>
                                <id>fmp</id>
                                <goals>
                                    <goal>resource</goal>
                                    <goal>build</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <enricher>
                                <config>
                                    <fmp-service>
                                        <type>NodePort</type>
                                    </fmp-service>
                                </config>
                            </enricher>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>
  1. application.yml的內容如下,增加了熔斷的配置:
spring:
  application:
    name: web-service

server:
  port: 8080

backend:
  ribbon:
    eureka:
      enabled: false
    client:
      enabled: true
    ServerListRefreshInterval: 5000

hystrix.command.BackendCall.execution.isolation.thread.timeoutInMilliseconds: 5000
hystrix.threadpool.BackendCallThread.coreSize: 5
  1. 建立一個ribbon的配置類RibbonConfiguration:
package com.bolingcavalry.webservice;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AvailabilityFilteringRule;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.PingUrl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

/**
 * @Description: ribbon配置類
 * @author: willzhao E-mail: [email protected]
 * @date: 2019/6/16 11:52
 */
public class RibbonConfiguration {

    @Autowired
    IClientConfig ribbonClientConfig;

    /**
     * 檢查服務是否可用的例項,
     * 此地址返回的響應的返回碼如果是200表示服務可用
     * @param config
     * @return
     */
    @Bean
    public IPing ribbonPing(IClientConfig config){
        return new PingUrl();
    }

    /**
     * 輪詢規則
     * @param config
     * @return
     */
    @Bean
    public IRule ribbonRule(IClientConfig config){
        return new AvailabilityFilteringRule();
    }
}
  1. 應用啟動類如下,注意增加了服務發現、熔斷、ribbon的配置,還定義了restTemplte例項,注意@LoadBalanced註解:
package com.bolingcavalry.webservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
@RibbonClient(name="account-service", configuration = RibbonConfiguration.class)
public class WebServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebServiceApplication.class, args);
    }

    @LoadBalanced
    @Bean
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  1. 遠端呼叫account-service的http介面的邏輯被放進服務類AccountService中,注意URL中用的是服務名account-service:
package com.bolingcavalry.webservice;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Description: 這裡面封裝了遠端呼叫account-service提供服務的邏輯
 * @author: willzhao E-mail: [email protected]
 * @date: 2019/6/16 12:21
 */
@Service
public class AccountService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "getFallbackName" ,commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") })
    public String getDataFromSpringCloudK8SProvider(){
        return this.restTemplate.getForObject("http://account-service/name", String.class);
    }

    /**
     * 熔斷時呼叫的方法
     * @return
     */
    private String getFallbackName() {
        return "Fallback"
                + ", "
                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}
  1. 最後是響應web請求的WebServiceController類,這裡面呼叫了AccountService的服務,這樣我們從web發起請求後,web-service就會遠端呼叫account-service的服務:
package com.bolingcavalry.webservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description: 測試用的controller,會遠端呼叫account-service的服務
 * @author: willzhao E-mail: [email protected]
 * @date: 2019/6/16 11:46
 */
@RestController
public class WebServiceController {

    @Autowired
    private AccountService accountService;

    /**
     * 探針檢查響應類
     * @return
     */
    @RequestMapping("/health")
    public String health() {
        return "OK";
    }

    /**
     * 遠端呼叫account-service提供的服務
     * @return 多次遠端調返回的所有結果.
     */
    @RequestMapping("/account")
    public String account() {

        StringBuilder sbud = new StringBuilder();

        for(int i=0;i<10;i++){
            sbud.append(accountService.getDataFromSpringCloudK8SProvider())
                .append("<br>");
        }

        return sbud.toString();
    }
}
  1. 將上述工程的原始碼放在minikube機器上,確保maven設定正常,然後在pom.xml檔案所在目錄執行以下命令,即可編譯構建工程並部署到kubernetes上:
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes

執行成功後控制檯輸出如下:

...
[INFO] Installing /usr/local/work/k8s/ribbon/spring-cloud-k8s-web-service/target/classes/META-INF/fabric8/kubernetes.json to /root/.m2/repository/com/bolingcavalry/web-service/0.0.1-SNAPSHOT/web-service-0.0.1-SNAPSHOT-kubernetes.json
[INFO] 
[INFO] <<< fabric8-maven-plugin:3.5.37:deploy (default-cli) < install @ web-service <<<
[INFO] 
[INFO] 
[INFO] --- fabric8-maven-plugin:3.5.37:deploy (default-cli) @ web-service ---
[INFO] F8: Using Kubernetes at https://192.168.121.133:8443/ in namespace default with manifest /usr/local/work/k8s/ribbon/spring-cloud-k8s-web-service/target/classes/META-INF/fabric8/kubernetes.yml 
[INFO] Using namespace: default
[INFO] Creating a Service from kubernetes.yml namespace default name web-service
[INFO] Created Service: target/fabric8/applyJson/default/service-web-service.json
[INFO] Using namespace: default
[INFO] Creating a Deployment from kubernetes.yml namespace default name web-service
[INFO] Created Deployment: target/fabric8/applyJson/default/deployment-web-service.json
[INFO] F8: HINT: Use the command `kubectl get pods -w` to watch your pods start up
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  12.792 s
[INFO] Finished at: 2019-06-16T19:24:21+08:00
[INFO] ------------------------------------------------------------------------
  1. 檢查kubernetes上的部署和服務是否正常:
[root@minikube spring-cloud-k8s-web-service]# kubectl get deployments
NAME              READY   UP-TO-DATE   AVAILABLE   AGE
account-service   1/1     1            1           109m
web-service       1/1     1            1           18m
[root@minikube spring-cloud-k8s-web-service]# kubectl get svc
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
account-service   NodePort    10.105.157.201   <none>        8080:32596/TCP   109m
kubernetes        ClusterIP   10.96.0.1        <none>        443/TCP          8d
web-service       NodePort    10.99.211.179    <none>        8080:30519/TCP   18m
  1. minikube的service命令可以得到指定服務的訪問地址:
[root@minikube spring-cloud-k8s-web-service]# minikube service web-service --url
http://192.168.121.133:30519

可見web-service的服務可以通過這個url訪問:http://192.168.121.133:30519

  1. 用瀏覽器訪問地址:http://192.168.121.133:30519/account ,如下圖所示,頁面上展示的內容都是web-service呼叫了account-service的介面返回的,證明kubernetes上的註冊發現能力正常:

    擴容驗證ribbon輪詢能力

    雖然web-service可以正常呼叫account-service的服務,但始終訪問的是一個pod,接下來我們就對account-service的pod進行擴容,將數量調整為2個,看看web-service是否可以輪詢呼叫每個account-service的pod:
  2. 執行以下命令即可將pod數量調整為2個:
kubectl scale --replicas=2 deployment account-service
  1. 檢查account-service的pod,發現已經有兩個了(account-service-5554576647-m29xr和account-service-5554576647-zwwml):
[root@minikube spring-cloud-k8s-web-service]# kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
account-service-5554576647-m29xr   1/1     Running   0          53m
account-service-5554576647-zwwml   1/1     Running   0          20s
web-service-6d775855c7-7lkvr       1/1     Running   0          29m
  1. 用瀏覽器訪問地址:http://192.168.121.133:30519/account ,如下圖所示,account-sercice返回的hostname已經變成了兩種,和前面查到的pod的name一致,可見web-service的確是通過ribbon輪詢訪問了多個account-service的pod:

    驗證熔斷能力

    接下來驗證web-service配置的熔斷服務是否可以生效:
  2. 執行以下命令將account-service的deployment刪除:
kubectl delete deployment account-service
  1. 再瀏覽器訪問地址:http://192.168.121.133:30519/account ,如下圖所示,頁面上的"Fallback"是配置的熔斷方法返回的內容,可見熔斷配置已經生效:
  2. 再回到web-service的pom.xml所在位置執行以下命令,這樣會重新構建部署一次web-service服務:
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes
  1. 再瀏覽器訪問地址:http://192.168.121.133:30519/account ,如下圖所示,服務成功恢復:

至此,spring-cloud-kubernetes的服務發現和輪詢實戰(含熔斷)就全部完成了,利用API Server提供的資訊,spring-cloud-kubernetes將原生的kubernetes服務帶給了SpringCloud應用,幫助傳統微服務更好的融合在kubernetes環境中,如果您也在考慮將應用遷移到kubernetes上,希望本文能給您一些參考。

歡迎關注我的公眾號:程式設計師欣宸

相關推薦

spring-cloud-kubernetes服務發現實戰(熔斷)

本文是《spring-cloud-kubernetes實戰系列》的第四篇,主要內容是在kubernetes上部署兩個應用:Web-Service和Account-Service,通過spring-cloud-kubernetes提供的註冊發現能力,實現Web-Service呼叫Account-Service提

SpringBoot + Spring Cloud Consul 服務註冊發現

![image.png](https://cdn.nlark.com/yuque/0/2020/png/359374/1595433507631-6d8b936d-72dc-4ec2-8148-15ae46e5ee12.png#align=left&display=inline&height=360&marg

Spring Cloud Consul—服務發現與Consul

列表 當前 host static 實例 cati edi 上下文 ota 服務發現是基於微服務架構的關鍵原則之一。嘗試配置每個客戶端或某種形式的約定可能非常困難,可以非常脆弱。Consul通過HTTP API和DNS提供服務發現服務。Spring Cloud Consul

Spring Cloud-02服務發現服務註冊Eureka + Eureka Server的搭建

文章目錄 服務發現元件概述 Eureka概述 Eureka原理 Maven父子工程的搭建 Eureka Server的搭建 新建 Maven Module 新增spring-cloud-starter-eureka-s

java面試題框架篇spring cloud服務發現consul

1.服務發現 spring cloud提供了多個服務發現框架整合,euerka已經停止開發了,目前最流行的是consul Feature euerka Consul zookeeper etcd 服務健康檢查 可配支援 服務狀態,記

SpringCloud實戰二:Spring Cloud Eureka 服務發現與註冊中心

  Spring Cloud Eureka 它是通過封裝 Netflix 開源的Eureka,一款基於 Rest 的服務發現與註冊元件,包括 Eureka Server 和 Eureka Client,最新版本為2018年8月釋出的1.9.4版本,最新的2.x版本已經不再開源,但是1.9

Spring Cloud Eureka —— 服務發現與消費

ribbon簡單介紹 1服務發現的任務由Eureka的客戶端完成,服務消費的任務由Ribbon完成。 2Ribbon是一個基於HTTP和TCP的客戶端負載均衡器,它可以在通過客戶端中配置的ribbonServerList服務端列表去輪詢訪問以達到負載均衡的作用。 構建例

Spring Cloud Eureka--服務發現

一、Spring Cloud      Spring Cloud 為開發者提供了在分散式系統(如配置管理、服務發現、斷路器、智慧路由、微代理、控制匯流排、一次性 Token、全域性鎖、決策競選、分散式會話和叢集狀態)操作的開發工具。最關鍵的是它足夠簡單,一般的開發人員只需要

Spring系列學習之Spring Cloud Zookeeper服務發現及分散式配置

英文原文:https://spring.io/projects/spring-cloud-zookeeper 目錄 概述 特性 快速開始 學習 文件 示例 概述 Spring Cloud Zookeeper通過自動配置和Spring環境以及其他Spring程式

四:Spring Cloud服務發現與呼叫-Ribbon

1. 簡介 Ribbon is a client side load balancer which gives you a lot of control over the behaviour of HTTP and TCP clients. F

四、Spring cloud服務發現/註冊(Eureka)

一、Spring Cloud Eureka (一)Eureka伺服器   Eureka Server 是 Eureka Client 的註冊服務中心,管理所有註冊服務、以及其例項資訊和狀態。 1、引入Maven依賴 <dependency>

Kubernetes服務發現kube-dns外掛

大綱 kube-dns的主要變化 kube-dns的實現原理 kubedns容器詳解 dnsmasq容器簡介 exech

spring cloud——eureka服務的註冊發現

https://blog.csdn.net/forezp/column/info/15197 參考方誌朋的部落格 學到了@LoadBalanced註釋,可以使得ribbon(負載均衡)起作用。還有就是 restTemplate.getForObject("http://provide

spring-cloud服務之路(三):服務註冊發現之Eureka、Consul

        在上一篇spring-cloud微服務之路(二):Spring Boot 我們介紹瞭如何快速的使用 Spring Boot 搭建一個微服務專案,這一篇我們演示如何分別使用 Spring Cloud Eureka 和 Spring Cloud Consul 完成

Spring Cloud + Kubernetes服務框架原理實踐

早在半年前,公司開始推行容器化部署方案 AppOS,雖然釋出介面過於極客,十分晦澀,不過仔細研究起來真的覺得十分強大,容器化推行後,計算資源(CPU、記憶體)的利用率可以極大提高,降低伺服器數量,從而節約技術成本。恰巧,若干個朋友所在創業公司最近也在嘗試做微服務、容器化。架構

spring cloud服務架構 服務提供者服務消費者

服務 lee 名詞 mave into gin tag bigint snap 服務提供者和服務消費者 下面這張表格,簡單描述了服務提供者/消費者是什麽: | 名詞 | 概念 | | ----- | ---------

Spring Cloud服務架構—服務註冊與發現

開源 查看 zookeeper rest 探討 ken 並且 tin services Spring Cloud簡介 Spring Cloud是一個基於Spring Boot實現的雲應用開發工具,它為基於JVM的雲應用開發中涉及的配置管理、服務發現、斷路器、智能路由、微代理

構建微服務架構Spring Cloud服務註冊與發現(Eureka、Consul)

comm 簡介 foundry 架構 eas args 包含 什麽 其他 Spring Cloud簡介 Spring Cloud是一個基於Spring Boot實現的雲應用開發工具,它為基於JVM的雲應用開發中涉及的配置管理、服務發現、斷路器、智能路由、微代理、控制總線、全

spring cloud註冊服務發現(踩著坑往前爬)

lse value 都是 一個 人員 aging 分享圖片 idea put spring cloud簡介 Spring Cloud為開發人員提供了快速構建分布式系統中的一些通用模式(例如配置管理,服務發現,斷路器,智能路由,微代理,控制總線,一次性令牌,全局鎖,領導選舉

你真的了解微服務架構嗎?聽聽八年阿裏架構師怎樣講述DubboSpring Cloud服務架構

微服務 架構 dubbo Spring Cloud 微服務架構是互聯網很熱門的話題,是互聯網技術發展的必然結果。它提倡將單一應用程序劃分成一組小的服務,服務之間互相協調、互相配合,為用戶提供最終價值。雖然微服務架構沒有公認的技術標準和規範或者草案,但業界已經有一些很有影響力的開源微服務架構框架