前面部分我们一起简单学习了dockerkubernetes,本篇我们将从一个简单的demo上手Spring Cloud kubernetes,当然,我们只用到Spring Cloud kubernetes的服务注册与发现、配置中心模块。后续还有部分文章介绍如何将sck-demo部署到minikube以及阿里云容器服务kubernetes


本来计划是先写源码分析再写部署的,但考虑到大家都想先看到成果再去深究,所以就根据学习的过程按顺序写了。源码分析主要是解决一些疑惑,了解用到的功能都是怎么整合到一起的,遇到问题能够快速定位。


在完成sck-demo的服务注册发现、动态配置以及项目部署之后,我们也实现一个基于Spring Cloud Gateway整合SentinelAPI网关。


从入门到源码分析,带大家一起掌握Spring Cloud kubernetes的使用,知其然并知其所以然。笔者尽量保持至少周更,也希望感兴趣的朋友帮忙点下在看。


本篇内容:

  • 实现服务注册与发现;

  • 实现动态配置并监听配置改变事件;


sck-demo代码可在github下载: https://github.com/wujiuye/share-projects/tree/master/sck-demo


项目框架如下图所示(后续文章会加上网关的实现代码):


Spring Cloud kubernetes入门项目sck-demo_java



  • common-lib:通用组件,如全局统一异常处理、自定义参数校验注解、json解析等工具类、api响应以及响应状态码的定义等;

  • k8s:该目录存储本地部署到minikube搭建的单节点kubernetes集群的yaml文件;

  • sck-demo-provider-api:定义对外提供的接口,只是方便java语言开发的应用直接通过依赖该jar包调用,实现rpc远程进程调用;

  • sck-demo-provider:服务提供者,实现对外的接口;

  • sck-demo-consimer:服务消费者,依赖sck-demo-provider-api模块,调用sck-demo-provider暴露的接口;


项目pom.xml统一配置依赖的Spring Cloud kubernetes以及Spring Boot的版本。


    <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <java.version>1.8</java.version>
       <spring.boot.version>2.3.0.RELEASE</spring.boot.version>
       <spring.cloud.k8s.version>1.1.3.RELEASE</spring.cloud.k8s.version>
   </properties>
   <dependencyManagement>
       <dependencies>
           <!-- Spring Boot -->
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-dependencies</artifactId>
               <version>${spring.boot.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>

           <!-- Spring Cloud Kubernetes -->
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-kubernetes-dependencies</artifactId>
               <version>${spring.cloud.k8s.version}</version>
               <type>pom</type>
               <scope>import</scope>
           </dependency>
           <!-- 其它省略 -->
       </dependencies>
   </dependencyManagement>


sck-demo-provider-api


该模块定义sck-demo-provider服务提供给其它服务调用的接口,也是sck-demo-provider需要实现的接口。但这里的实现接口不是像dubbo那样,需要在service或者controller通过关键字implements实现接口,如果非要比喻的话,倒像是go语言中的接口定义与实现。



Spring Cloud kubernetes入门项目sck-demo_java_02



sck-demo-provider-api模块的maven依赖配置文件中加入openfeignstarter


<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>


DemoService类的代码如下:


@FeignClient(
       // 对应服务提供者的${spring.application.name}
       name = ProviderConstant.SERVICE_NAME,
       path = "/v1",
       // url用于本地调试,部署到容器中时配置为null,走服务发现
       url = "${fegin-client.sck-demo-provider-url}",
       primary = false)
public interface DemoService {

   @PostMapping("/services")
   ListGenericResponse<DemoDto> getServices();

}


服务提供者需要实现/v1/services接口,服务消费者可直接通过调用DemoServicegetServices方法来调用接口,不需要写http请求,这些重复的事情交由feign去实现。实际上是feign在运行时为DemoService接口编写了实现类。因此我们可以在spring项目中通过@Resource注入DemoService实例。


sck-demo-provider


该模块实现sck-demo-provider-api定义的接口。


Spring Cloud kubernetes入门项目sck-demo_java_03

sck-demo-provider模块的maven依赖配置文件中添加spring-cloud-starter-kubernetes的依赖:


<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-kubernetes</artifactId>
</dependency>


spring-cloud-starter-kubernetes会自动导入spring-cloud-kubernetes-core以及spring-cloud-kubernetes-discovery


依赖组件模块以及sck-demo-provider-api接口定义模块:


<dependency>
   <groupId>com.wujiuye</groupId>
   <artifactId>common-lib</artifactId>
   <version>${parent.version}</version>
</dependency>
<dependency>
   <groupId>com.wujiuye</groupId>
   <artifactId>sck-demo-provider-api</artifactId>
   <version>${parent.version}</version>
</dependency>


commom-lib模块的依赖配置如下:


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

<!-- 参数校验 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-validation</artifactId>
</dependency>


DemoController就是实现DemoService接口Controller,代码如下:


@RestController
@RequestMapping("/v1")
public class DemoController {

   @Resource
   private DemoProps demoProps;
   @Resource
   private DiscoveryClient discoveryClient;

   @PostMapping("/services")
   public ListGenericResponse<DemoDto> getServices() {
       ListGenericResponse<DemoDto> response = new ListGenericResponse<>();
       PageInfo<DemoDto> pageInfo = new PageInfo<>(discoveryClient.getServices()
               .stream().map(DemoDto::new)
               .collect(Collectors.toList()), discoveryClient.getServices().size(), 100, 1);
       response.setData(pageInfo);
       return response;
   }

   @GetMapping("/config")
   public Object testConfig() {
       return demoProps;
   }

}


testConfig是我们用来测试动态配置是否生效的。


注意,我们并没有使用implements关键字实现接口,当然,你也可以使用implements关键字实现接口,这样的强约束能够让你在改动接口时想起还有其它地方要改。


实现接口之后,我们还要使用@EnableDiscoveryClient注解开启服务注册与发现功能,将服务提供者注册到注册中心。与Dubbo不同,Spring Cloud是以应用为维度注册服务与发现服务的。


@EnableDiscoveryClient
@SpringBootApplication
public class SckProviderApplication {

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

}


使用Spring Cloud kubernetes实现Spring Cloud服务注册与发现接口的spring-cloud-kubernetes-discovery模块,我们不需要配置注册中心的地址,一个@EnableDiscoveryClient注解就能搞定。


实现动态配置


需要导入spring-cloud-starter-kubernetes-config的依赖:


<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
</dependency>


编写props类,给类加上@ConfigurationProperties@Component注解,当然,还需要在启动类加上@EnableConfigurationProperties注解。@RefreshScope如果不加,spring cloud也会自动给DemoProps这个Bean加上scoperefresh,即在配置改变之后,该bean会被销毁并重新初始化一个实例。


@RefreshScope
@Component
@ConfigurationProperties(prefix = "sck-demo")
public class DemoProps {
   private String message;
   // get、set
}


编写本地测试用的application配置文件:application-dev.yml,在配置文件中加上DemoProps的配置,用于本地Debug


sck-demo:
 message: sck-demo message!


编写bootstrap.yaml文件:


spring:
 application:
   name: sck-demo-provider
 cloud:
   kubernetes:
     reload:
       enabled: true
       mode: polling
       period: 5000
     config:
       sources:
         - name: ${spring.application.name}-config


spring.cloud.kubernetes.reload配置启动自动拉取新配置功能,配置为主动拉取,周期为5秒。spring.cloud.kubernetes.config.sources项配置引用的ConfigMap资源的名称。


该配置文件在sck-demo项目的k8s/dev/config目录下,文件名为sck-demo-provider-config.yaml


Spring Cloud kubernetes入门项目sck-demo_java_04

等到将服务部署到k8s集群时,需要使用kubectl apply -f sck-demo-provider-config.yaml将在k8s中创建该ConfigMap资源。线上的配置文件就不要放在项目中了。


apiVersion: v1
kind: ConfigMap
metadata:
 name: sck-demo-provider-config
 namespace: default
data:
 application-dev.yml: |-
   sck-demo:
     message: for dev config map


怎么监听DemoProps的属性改变呢?


笔者从spring-cloud-kubernetes-configspring-cloud-context的源码中找到了能够监听配置改变的方法。这两个模块是我们后面分析动态配置的实现原理时需要深入了解源码的两个模块。


当配置中心更新了配置文件之后,spring-cloud-kubernetes-config会拉取到新的配置,然后会先发送一个EnvironmentChangeEvent事件,再发送一个RefreshScopeRefreshedEvent事件。


我们可以通过监听这两个事件得知配置改变了。在监听到EnvironmentChangeEvent事件时,DemoProps的属性还是没有改变的,而在监听到RefreshScopeRefreshedEvent事件时,DemoProps属性时已经改变了的。详细内容我们后面分析源码时再详细分析。


@Component
public class PropsListener implements ApplicationListener<RefreshScopeRefreshedEvent> {

   @Resource
   private DemoProps demoProps;

   @Override
   public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
       System.out.println(demoProps);
       System.out.println(event.getName());
       System.out.println(event.getSource());
   }

}


sck-demo-consumer


在消费端依赖sck-demo-provider-api模块,使用sck-demo-provider-api模块调用服务提供者的API接口。



Spring Cloud kubernetes入门项目sck-demo_java_05

添加依赖配置:


    <dependency>
       <groupId>com.wujiuye</groupId>
       <artifactId>common-lib</artifactId>
       <version>${parent.version}</version>
   </dependency>

   <dependency>
       <groupId>com.wujiuye</groupId>
       <artifactId>sck-demo-provider-api</artifactId>
       <version>${parent.version}</version>
   </dependency>

   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-kubernetes</artifactId>
   </dependency>


在启动类添加@EnableDiscoveryClient注解开启服务注册与发现功能,同时还需要添加@EnableFeignClients注解,扫描sck-demo-provider-api模块下的接口,Feign自动生成接口的实现类,并创建一个实例注册到spring bean工厂。


@EnableFeignClients(basePackages = {"com.wujiuye.sck.provider.client"})
@EnableDiscoveryClient
@SpringBootApplication
public class SckConsumerApplication {

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

}


Serivce中调用API接口


@Service
public class DemoInvokeServiceImpl implements DemoInvokeService {

   @Resource
   private DemoService demoService;

   @Override
   public ListGenericResponse<DemoDto> invokeDemo() {
       return demoService.getServices();
   }

}


在消费端也对外提供一个http接口,用于测试服务间的调用是否成功。


public class DemoTestController {

   @Resource
   private DemoInvokeService demoInvokeService;

   @GetMapping("/demo")
   public Object test2() {
       return demoInvokeService.invokeDemo();
   }

}


END


下一篇我们开始将服务部署到本地kubernetes集群,验证服务调用是否可以,配置中心改变配置之后应用程序是否能够监听到配置改变事件,是否能读取到最新配置等。


https://mp.weixin.qq.com/s/e7FuABONJSg5S4uO5jjhCg