spring-cloud-kubernetes 的服务发现和轮询实战 (含熔断)

本文介绍了如何在Kubernetes环境中使用spring-cloud-kubernetes进行服务注册和发现,通过Ribbon实现负载均衡。实战内容包括开发和部署Account-Service和Web-Service,展示在Kubernetes上实现微服务间的调用,并验证了服务扩容后的轮询能力和熔断机制。
  • 本文是《spring-cloud-kubernetes 实战系列》的第四篇,主要内容是在 kubernetes 上部署两个应用:Web-Service 和 Account-Service,通过 spring-cloud-kubernetes 提供的注册发现能力,实现 Web-Service 调用 Account-Service 提供的 http 服务;

全文概览

  • 本文由以下段落组成:

  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 上下载本次实战的源码,地址和链接信息如下表所示:

  • 这个 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-dependenc
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值