原创 吴就业 155 0 2020-06-20
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/b199a2eaa1354c83972691092ea9d8d2
作者:吴就业
链接:https://wujiuye.com/article/b199a2eaa1354c83972691092ea9d8d2
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
本篇文章写于2020年06月20日,从公众号|掘金|CSDN手工同步过来(博客搬家),本篇为原创文章。
本篇我们将从一个简单的demo
上手Spring Cloud kubernetes
,当然,我们只用到Spring Cloud kubernetes
的服务注册与发现、配置中心模块。后续还有部分文章介绍如何将sck-demo
部署到minikube
以及阿里云容器服务kubernetes
。
本来计划是先写源码分析再写部署的,但考虑到大家都想先看到成果再去深究,所以就根据学习的过程按顺序写了。源码分析主要是解决一些疑惑,了解用到的功能都是怎么整合到一起的,遇到问题能够快速定位。
在完成sck-demo
的服务注册发现、动态配置以及项目部署之后,我们也实现一个基于Spring Cloud Gateway
整合Sentinel
的API
网关。
从入门到源码分析,带大家一起掌握Spring Cloud kubernetes
的使用,知其然并知其所以然。笔者尽量保持至少周更,也希望感兴趣的朋友帮忙点下在看。
本篇内容: * 实现服务注册与发现; * 实现动态配置并监听配置改变事件;
sck-demo
代码可在github
下载:
https://github.com/wujiuye/share-projects/tree/master/sck-demo
。
项目框架如下图所示(后续文章会加上网关的实现代码):
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
服务提供给其它服务调用的接口,也是sck-demo-provider
需要实现的接口。但这里的实现接口不是像dubbo
那样,需要在service
或者controller
通过关键字implements
实现接口,如果非要比喻的话,倒像是go
语言中的接口定义与实现。
sck-demo-provider-api
模块的maven
依赖配置文件中加入openfeign
的starter
:
<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
接口,服务消费者可直接通过调用DemoService
的getServices
方法来调用接口,不需要写http
请求,这些重复的事情交由feign
去实现。实际上是feign
在运行时为DemoService
接口编写了实现类。因此我们可以在spring
项目中通过@Resource
注入DemoService
实例。
该模块实现sck-demo-provider-api
定义的接口。
在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
加上scope
为refresh
,即在配置改变之后,该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
。
等到将服务部署到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-config
和spring-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-provider-api
模块,使用sck-demo-provider-api
模块调用服务提供者的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>
<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();
}
}
下一篇我们开始将服务部署到本地kubernetes
集群,验证服务调用是否可以,配置中心改变配置之后应用程序是否能够监听到配置改变事件,是否能读取到最新配置等。
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。
如果指定了URL,那么getOptional方法不会返回null,且返回的Client是LoadBalancerFeignClient,但不会抛出异常。如果不指定URL,则走负载均衡逻辑,走的是loadBalance方法,且抛出异常。
选择Spring Cloud Kubernetes意味着我们想要将服务部署到Kubernetes集群,Spring Cloud Kubernetes为我们实现了Spring Cloud的一些接口,让我们可以快速搭建Spring Cloud微服务项目框架,并能使用Kubernetes云原生服务。
作为开发者,只有足够了解容器技术,才能做好技术选型,以及开发部署在Kubernetes容器服务之上的应用应该要注意哪些问题。如果运维不了解代码,开发也不了解Kubernetes,谁能解决将服务迁移到Kubernetes上遇到的各种问题呢?
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。