Service discovery and polling for spring-cloud-kubernetes (with fuses)

This is the fourth article in the series of spring-cloud-kubernetes. The main content is to deploy two applications on kubernetes: Web-Service and Acount-Service. Web-Service calls the http service provided by Account-Service through the registration discovery capabilities provided by spring-cloud-kubernetes.

List of Series Articles

  1. Official demo of spring-cloud-kubernetes Runs Actual War
  2. Hello spring-cloud-kubernetes
  3. Three Key Points of Knowledge Behind spring-cloud-kubernetes
  4. Service Discovery and Polling for spring-cloud-kubernetes (with Fused)
  5. spring-cloud-kubernetes and SpringCloud Gateway
  6. configmap of spring-cloud-kubernetes and k8s

Full-text overview

This article consists of the following paragraphs:

  1. environmental information
  2. Overview of common SpringCloud registration discovery services
  3. Analyzing how service registration discovery is implemented on kubernetes
  4. Link to download the source code for this chapter
  5. Actual Development Account-Service Service (Service Provider)
  6. Actual Development of Web-Service Services (Service Consumers)
  7. Extended Verification ribbon Polling Capability
  8. Verify Fusing Capability

environmental information

The environment and version information for this battle is as follows:

  1. Operating System: 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 plugin: 3.5.37
  6. spring-cloud-kubernetes: 1.0.1.RELEASE

Above linux, minikube, java, maven, make sure you're ready to install and start minikube under Linux minikube Guide to Linux Installation.

Overview of common SpringCloud registration discovery services

The most important function of the SpringCloud environment is to register discovery services, so when migrating SpringCloud applications to the kubernetes environment, developers are most concerned with how to expose their services on kubernetes and how to invoke other microservices.

Looking at the registration in the normal Spring Cloud environment, the following image is from the official Spring blog at https://spring.io/blog/2015/07/14/microservices-with-spring.

As you can see from the figure above, Account-Service is applied to register itself with Eureka, so that Web-Service can find the address of Account-Service in Eureka using account-service, and then smoothly send RestFul requests to Account-Service to use the services it provides.

Analyzing how service registration discovery is implemented on kubernetes

What happens to the registration discovery mechanism after migrating the two Web-Service and Acount-Service applications above to kubernetes?
First, follow the pattern shown above and deploy Eureka on kubernetes, which is no different from not using kubernetes.
Second, what you're going to do today is use the spring-cloud-kubernetes framework, which calls on kubernetes'native capabilities to service existing SpringCloud applications, as shown in the following diagram:

The diagram above shows that when a Web-Service application invokes a service of an Account-Service application, it requests a list of services from the API Server using okhttp. When the API Server receives the request, it goes to etcd to fetch the data and returns it to the Web-Service application. Thus, the Web-Service has the information of Account-Service and can poll multiple Pod s of Account-Service for initiation.Request;

Note that the WebService application does not send requests directly to the service created by Account-Service in Kubernetes, but to specific Pods. This capability is due to the fact that the spring-cloud-kubernetes framework retrieves all the Pod information corresponding to Account-Service through the service.(endpoint), which refers to the source KubernetesServerList.java as follows:

public List<Server> getUpdatedListOfServers() {
        //Conditions are namespace and serviceId to get all the endpoints for the service
        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 + "]");
            }
            //Traverse through all endpoint s, take out IP addresses and ports, build Server instances, and put them in the result collection
            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;
    }

The theoretical analysis is complete, so let's start the actual battle

Source Download

If you don't plan to write code, you can download the source code for this battle from GitHub, with the address and link information shown in the following table:

Name link Remarks
Project Home https://github.com/zq2599/blog_demos The project's home page on GitHub
git repository address (https) https://github.com/zq2599/blog_demos.git Warehouse address for the project source code, https protocol
git warehouse address (ssh) git@github.com:zq2599/blog_demos.git Warehouse address for the project source code, ssh protocol

This git project has multiple folders. The Acount-Service source code for this chapter is in the spring-cloud-k8s-account-service folder, and the Web-Service source code is in the spring-cloud-k8s-web-service folder, as shown in the red box below:

The following is a detailed encoding process;

Develop and deploy Account-Service services

The Account-Service service is a very common springboot application and has nothing to do with spring-cloud-kubernetes:

  1. Create a springboot application from maven with artifactId as account-service and pom.xml as follows:
<?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>

As can be seen from the pom.xml content above, account-service application is a simple web application, and has nothing to do with Spring Cloud, spring-cloud-kubernetes. The only difference with other springboot s is that it uses the fabric8-maven-plugin plug-in, which makes it easy to deploy applications to the kubernetes environment.

  1. application.yml is as simple as this:
spring:
  application:
    name: account-service

server:
  port: 8080
  1. AccountController is the external provider of services, the getName method returns the hostname of the current container, the health method responds to the two probes of kubernetes, and the ribbonPing method responds to the caller using the ribbon service, which calls this interface to determine if the current service is working:
@RestController
public class AccountController {

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

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

    /**
     * Probe Check Response Class
     * @return
     */
    @RequestMapping("/health")
    public String health() {
        return "OK";
    }

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

    /**
     * Return hostname
     * @return The hostname of the container where the current application is located.
     */
    @RequestMapping("/name")
    public String getName() {
        return this.hostName
                + ", "
                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}
  1. Place the source code for the above project on the minikube machine, make sure the maven is set up properly, and then execute the following command in the directory where the pom.xml file is located to compile the build project and deploy it to kubernetes:
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes

After successful execution, the console output is as follows:

...
[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. Check that deployments and services on kubernetes are working:
[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. The service command of minikube gets the access address of the specified service:
[root@minikube spring-cloud-k8s-account-service]# minikube service account-service --url
http://192.168.121.133:32596

Visible account-service services can be accessed through this url: http://192.168.121.133:32596

  1. Access address with browser: http://192.168.121.133:32596/name, as shown below, account-service provides normal access to services:

    Now that the account-service service service is ready, the next step is to develop and deploy web-service applications.

    Develop and deploy Web-Service services

    The Web-Service Service is a springboot application that uses the registration discovery capabilities provided by spring-cloud-kubernetes to access all pod s of a specified service in a polling fashion:
  2. Create a springboot application from maven, artifactId is a web-service, pom.xml is as follows, focus on the dependency of 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 adds the configuration of the fuse as follows:
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. Create a ribbon configuration class, 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 Configuration Class
 * @author: willzhao E-mail: zq2599@gmail.com
 * @date: 2019/6/16 11:52
 */
public class RibbonConfiguration {

    @Autowired
    IClientConfig ribbonClientConfig;

    /**
     * Check if the service is available.
     * The return code of the response returned by this address if 200 indicates that the service is available
     * @param config
     * @return
     */
    @Bean
    public IPing ribbonPing(IClientConfig config){
        return new PingUrl();
    }

    /**
     * Polling Rules
     * @param config
     * @return
     */
    @Bean
    public IRule ribbonRule(IClientConfig config){
        return new AvailabilityFilteringRule();
    }
}
  1. Apply the startup class as follows, noting the addition of service discovery, melting, ribbon configurations, and defining restTemplte instances, noting the @LoadBalanced annotation:
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. The logic for remotely calling the http interface of account-service is put into the service class AccountService, note that the service name account-service is used in the URL:
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: This encapsulates the logic for remotely invoking account-service to provide services
 * @author: willzhao E-mail: zq2599@gmail.com
 * @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);
    }

    /**
     * Method invoked when a fuse occurs
     * @return
     */
    private String getFallbackName() {
        return "Fallback"
                + ", "
                + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}
  1. Finally, there is the WebServiceController class that responds to the web request, where the service of AccountService is invoked so that when we make a request from the web, the service of account-service is called remotely by the web-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 for testing, which remotely calls account-service services
 * @author: willzhao E-mail: zq2599@gmail.com
 * @date: 2019/6/16 11:46
 */
@RestController
public class WebServiceController {

    @Autowired
    private AccountService accountService;

    /**
     * Probe Check Response Class
     * @return
     */
    @RequestMapping("/health")
    public String health() {
        return "OK";
    }

    /**
     * Remote invocation of service provided by account-service
     * @return All results returned by multiple remote calls.
     */
    @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. Place the source code for the above project on the minikube machine, make sure the maven is set up properly, and then execute the following command in the directory where the pom.xml file is located to compile the build project and deploy it to kubernetes:
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes

After successful execution, the console output is as follows:

...
[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. Check that deployments and services on kubernetes are working:
[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. The service command of minikube gets the access address of the specified service:
[root@minikube spring-cloud-k8s-web-service]# minikube service web-service --url
http://192.168.121.133:30519

Visible web-service services can be accessed through this url: http://192.168.121.133:30519

  1. Access address with browser: http://192.168.121.133:30519/account, as shown in the following figure, the content displayed on the page is returned by the web-service calling the account-service interface, proving that the registration discovery capability on kubernetes is normal:

    Extended Verification ribbon Polling Capability

    Although web-service can call account-service services normally, it always accesses a pod. Next, we will expand the pod of account-service to two to see if web-service can poll the pod that calls each account-service:
  2. You can adjust the number of pod s to two by executing the following command:
kubectl scale --replicas=2 deployment account-service
  1. Check the pod of account-service and find that there are already two (account-service-5554576647-m29xr and account-service-5554576647-zwml):
[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. Browser access address: http://192.168.121.133:30519/account, as shown in the following figure, account-sercice returns two hostname s, which are identical to the name of the pod found earlier. It is clear that web-service does access multiple account-service pods through ribbon polling:

    Verify Fusing Capability

    Next, verify that the web-service configured fuse service is valid:
  2. Delete the deployment of account-service by executing the following command:
kubectl delete deployment account-service
  1. Re-browser access address: http://192.168.121.133:30519/account, as shown below, "Fallback" on the page is the content returned by the configured fuse method, so the fuse configuration is in effect:
  2. Return to the pom.xml location of the web-service and execute the following command, which will rebuild the deployed web-service service once:
mvn clean install fabric8:deploy -Dfabric8.generator.from=fabric8/java-jboss-openjdk8-jdk -Pkubernetes
  1. Re-browser access address: http://192.168.121.133:30519/account, as shown in the following figure, the service was successfully restored:

So far, the service discovery and polling of spring-cloud-kubernetes (with the melt) is complete. Using the information provided by API Server, spring-cloud-kubernetes brings native kubernetes services to SpringCloud applications, helping traditional micro services integrate better in the Kubernetes environment, if you are also considering applying themMigrating to kubernetes, I hope this article can give you some reference.

Welcome to my public number: programmer Xin Chen

Keywords: Java Maven Spring Kubernetes Apache

Added by conquest on Wed, 04 Sep 2019 04:45:51 +0300