spring-boot 2.5.4,nacos 作为配置、服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD

本文主要介绍 Java 通过 Cloud Native Buildpacks 打包镜像,通过 Gitlab 配置 CI/CD。以及使用 nacos 作为配置中心,使用 grpc 作为 RPC 框架。

前置条件:

  • JDK 版本:1.8
  • gradle 版本:7.1
  • spring-boot 版本:2.5.4
  • nacos 版本:1.3.1
  • GitLab 配置

spring-boot gradle 插件

spring-boot gradle 插件在 gradle 中提供 spring-boot 支持。该插件可以打 jar 或者 war 包。

  1. plugins {
  2. id 'org.springframework.boot' version '2.5.4'
  3. }

新建一个 gradle 项目,该项目在只引用 id 'org.springframework.boot' version '2.5.4' 插件的情况下,gralde 任务分布完全没有变化,如下图所示。

引入 java 插件

  1. plugins {
  2. id 'java'
  3. id 'org.springframework.boot' version '2.5.4'
  4. }

但当引入 java 插件后,情况就大大不同了,可见,spring-boot 插件和 java 插件一起应用后,将产生如下反应:

  1. 创建bootJar任务,执行该任务会生成一个 fat jar。该 jar 包把所有的类文件打包进 BOOT-INF/classes 中,把项目依赖的所有 jar 包打包进 BOOT-INF/lib 中。

  2. 配置 assemble 任务,该任务依赖于 bootJar 任务,所以执行 assemble 任务的时候也会执行 bootJar

  3. 配置 jar 任务,该任务可以配置 jar 包的 classifier。配置方式如下,默认情况下 classifier 为空字符串:

    1. bootJar {
    2. classifier = 'boot'
    3. }
    4. jar {
    5. classifier = ''
    6. }
  4. 创建 bootBuildImage 任务,该任务可以使用 CNB 打包 OCI 镜像。后面会详细介绍如何使用 CNB。

  5. 创建 bootRun 任务用于运行应用程序。

  6. 创建 bootArchives 配置,注意这里是配置,不是任务。当应用 maven 插件时会为 bootArchives 配置创建 uploadBootArchives 任务。bootArchives 默认情况下包含 bootJarbootWar 任务生成的文件。

    1. uploadBootArchives {
    2. repositories {
    3. mavenDeployer {
    4. repository url: 'https://repo.example.com'
    5. }
    6. }
    7. }
  7. 创建 developmentOnly 配置。该配置用于管理开发时的依赖,比如 org.springframework.boot:spring-boot-devtools,该依赖仅在开发时使用,无需打进 jar 包中。

    1. dependencies {
    2. developmentOnly 'org.springframework.boot:spring-boot-devtools'
    3. }
  8. 创建 productionRuntimeClasspath 配置。它等价于 runtimeClasspath 中的依赖减去 developmentOnly 配置中的依赖。

  9. 配置 JavaCompile 任务默认使用 UTF-8

  10. 配置 JavaCompile 任务使用 -parameters 配置编译器参数。

引入 io.spring.dependency-management 插件

引入该插件后,将自动管理依赖版本。

  1. plugins {
  2. id 'java'
  3. id 'org.springframework.boot' version '2.5.4'
  4. id "io.spring.dependency-management" version "1.0.11.RELEASE"
  5. }
  6. group 'com.toy'
  7. version '1.0.0-SNAPSHOT'
  8. repositories {
  9. mavenCentral()
  10. }
  11. dependencies {
  12. developmentOnly 'org.springframework.boot:spring-boot-devtools'
  13. }

引入 grpc 框架

基于本示例使用 nacos 作为服务发现中心,本示例将使用 net.devh:grpc-spring-boot-starter 依赖作为框架。

工程结构

目前为止,我们介绍了 java 项目中引入 spring gradle 所需的插件,以及各个组件的作用。接下来我们介绍如何引入 grpc,以及引入 grpc 后,我们的工程结构。

改造后工程结构总体如下:

protobuf

用于保存 proto 文件,以及发布 proto 文件,当客户端引用时,保证 jar 包最小。build.gradle 文件内容如下:

  1. plugins {
  2. id 'java'
  3. id 'idea'
  4. id 'com.google.protobuf' version '0.8.17' //google proto 插件
  5. id 'maven-publish'
  6. }
  7. group 'com.toy'
  8. version '1.0.0-SNAPSHOT'
  9. repositories {
  10. mavenCentral()
  11. }
  12. dependencies {
  13. //用于生成 java 类
  14. compileOnly 'io.grpc:grpc-protobuf:1.39.0'
  15. compileOnly 'io.grpc:grpc-stub:1.39.0'
  16. }
  17. protobuf {
  18. protoc {
  19. artifact = "com.google.protobuf:protoc:3.17.3"
  20. }
  21. plugins {
  22. grpc {
  23. artifact = 'io.grpc:protoc-gen-grpc-java:1.39.0'
  24. }
  25. }
  26. generateProtoTasks {
  27. all()*.plugins {
  28. grpc {
  29. }
  30. }
  31. }
  32. }
  33. publishing {
  34. publications {
  35. proto_package(MavenPublication) {
  36. }
  37. }
  38. repositories {
  39. maven {
  40. allowInsecureProtocol = true
  41. url '你的 Maven 仓库地址'
  42. credentials {
  43. username = 'Maven 账号'
  44. password = 'Maven 密码'
  45. }
  46. }
  47. }
  48. }

生成的 Java 类路径为 $projectName/build/.. 如下所示,生成的所有 class 文件位于 proto 文件夹下:

rpc

  1. 在 rpc 项目中添加启动类 ToyApplication,内容如下:

    1. package com.toy.rpc;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. /**
    5. * @author Zhang_Xiang
    6. * @since 2021/8/20 15:34:58
    7. */
    8. @SpringBootApplication(scanBasePackages = {"com.toy.*"})
    9. public class ToyApplication {
    10. public static void main(String[] args) {
    11. SpringApplication.run(ToyApplication.class, args);
    12. }
    13. }
  2. 在包 com.toy.rpc.impl 中添加 HelloImpl 文件,内容如下:

    1. package com.toy.rpc.impl;
    2. import com.toy.proto.GreeterGrpc;
    3. import com.toy.proto.HelloReply;
    4. import com.toy.proto.HelloRequest;
    5. import io.grpc.stub.StreamObserver;
    6. import net.devh.boot.grpc.server.service.GrpcService;
    7. /**
    8. * @author Zhang_Xiang
    9. * @since 2021/8/20 15:35:56
    10. */
    11. @GrpcService
    12. public class HelloImpl extends GreeterGrpc.GreeterImplBase {
    13. @Override
    14. public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
    15. HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + request.getName()).build();
    16. responseObserver.onNext(reply);
    17. responseObserver.onCompleted();
    18. }
    19. }
  3. 添加集成测试

    (1)添加集成测试配置

    1. package com.toy.config;
    2. import net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration;
    3. import net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration;
    4. import net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration;
    5. import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
    6. import org.springframework.boot.test.context.TestConfiguration;
    7. /**
    8. * @author Zhang_Xiang
    9. * @since 2021/8/12 16:26:25
    10. */
    11. @TestConfiguration
    12. @ImportAutoConfiguration({
    13. GrpcServerAutoConfiguration.class, // Create required server beans
    14. GrpcServerFactoryAutoConfiguration.class, // Select server implementation
    15. GrpcClientAutoConfiguration.class}) // Support @GrpcClient annotation
    16. public class IntegrationTestConfigurations {
    17. }

    (2)添加测试类

    1. package com.toy;
    2. import com.toy.config.IntegrationTestConfigurations;
    3. import com.toy.proto.GreeterGrpc;
    4. import com.toy.proto.HelloReply;
    5. import com.toy.proto.HelloRequest;
    6. import net.devh.boot.grpc.client.inject.GrpcClient;
    7. import org.junit.jupiter.api.Test;
    8. import org.springframework.boot.test.context.SpringBootTest;
    9. import org.springframework.test.annotation.DirtiesContext;
    10. import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
    11. import static org.junit.jupiter.api.Assertions.assertEquals;
    12. /**
    13. * @author Zhang_Xiang
    14. * @since 2021/8/20 16:02:41
    15. */
    16. @SpringBootTest(properties = {
    17. "grpc.server.inProcessName=test", // Enable inProcess server
    18. "grpc.server.port=-1", // Disable external server
    19. "grpc.client.inProcess.address=in-process:test" // Configure the client to connect to the inProcess server
    20. })
    21. @SpringJUnitConfig(classes = {IntegrationTestConfigurations.class})
    22. @DirtiesContext
    23. public class HelloServerTest {
    24. @GrpcClient("inProcess")
    25. private GreeterGrpc.GreeterBlockingStub blockingStub;
    26. @Test
    27. @DirtiesContext
    28. public void sayHello_replyMessage() {
    29. HelloReply reply = blockingStub.sayHello(HelloRequest.newBuilder().setName("Zhang").build());
    30. assertEquals("Hello Zhang", reply.getMessage());
    31. }
    32. }
  4. build.gradle

    1. plugins {
    2. id 'java'
    3. id 'idea'
    4. id 'org.springframework.boot' version '2.5.4'
    5. id "io.spring.dependency-management" version "1.0.11.RELEASE"
    6. }
    7. group 'com.toy'
    8. version '1.0.0-SNAPSHOT'
    9. repositories {
    10. mavenCentral()
    11. }
    12. dependencies {
    13. implementation platform('io.grpc:grpc-bom:1.39.0') //使所有 protobuf 插件的版本保持一致
    14. implementation 'net.devh:grpc-spring-boot-starter:2.12.0.RELEASE'
    15. developmentOnly 'org.springframework.boot:spring-boot-devtools'
    16. implementation project(':protobuf') //引入 protobuf 项目
    17. testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
    18. testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
    19. testImplementation 'io.grpc:grpc-testing'
    20. testImplementation('org.springframework.boot:spring-boot-starter-test')
    21. }
    22. bootBuildImage {
    23. imageName = "harbor.xxx.com/rpc/${project.name}:${project.version}"
    24. publish = true
    25. docker {
    26. publishRegistry {
    27. username = "admin"
    28. password = "admin"
    29. url = "harbor.xxx.com"
    30. }
    31. }
    32. }
    33. test {
    34. useJUnitPlatform()
    35. }

至此,整个 grpc 项目基础结构完成。

添加 nacos 配置中心、服务发现

  1. 在 rpc 项目 build.gradle 文件中引入读取 nacos 配置的 jar 包和注册服务到 nacos 中的 jar 包。

    1. dependencies{
    2. implementation 'org.springframework.boot:spring-boot-starter-web' //用于注册服务
    3. //添加此引用的原因是为了解决 spring boot 2.5.4 无法读取 nacos 配置的问题
    4. implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap:3.0.3'
    5. implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2021.1'
    6. implementation 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config:2021.1'
    7. }
  2. 添加读取服务配置,在 rpc 项目中添加 bootstrap.propertise,内容如下:

    1. spring.profiles.active=dev
    2. spring.application.name=toy

    添加 bootstrap-dev.properties,内容如下:

    1. spring.cloud.nacos.config.server-addr=127.0.0.1:8848
    2. spring.cloud.nacos.config.namespace=52f2f610-46f6-4c57-a089-44072099adde
    3. spring.cloud.nacos.config.file-extension=yaml
    4. spring.cloud.nacos.config.group=DEFAULT_GROUP
    5. spring.cloud.nacos.discovery.namespace=52f2f610-46f6-4c57-a089-44072099adde
    6. spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

至此,完成了服务端通过 nacos 读取配置,并且把服务端注册到 nacos 中。

gitlab CI/CD

在根项目目录下添加 .gitlab-ci.yml 文件。当 gitlab 安装了 runner 后,将自动触发 CI/CD,内容如下:

  1. variables:
  2. CONTAINER_NAME: toy
  3. IMAGE_VERSION: 1.0.0
  4. IMAGE_TAG: harbor.xxx.com/toy/rpc
  5. PORT: 10086
  6. stages:
  7. - test
  8. - publishJar
  9. - bootBuildImage //spring-boot 从 2.3.0 版本以后引入了 BootBuildImage 任务。
  10. - deploy
  11. test:
  12. stage: test
  13. script:
  14. - gradle clean
  15. - gradle rpc:test
  16. publishProtoBuf:
  17. stage: publishJar
  18. script:
  19. - gradle protobuf:publish
  20. bootBuildImage:
  21. stage: bootBuildImage
  22. script:
  23. - gradle rpc:bootBuildImage
  24. deployDev:
  25. stage: deploy
  26. script:
  27. - ssh $SERVER_USER@$SERVER_IP "docker login --username=$REGISTERY_NAME --password=$REGISTRY_PWD harbor.xxx.com; docker pull $IMAGE_TAG:$IMAGE_VERSION;"
  28. - ssh $SERVER_USER@$SERVER_IP "docker container rm -f $CONTAINER_NAME || true"
  29. - ssh $SERVER_USER@$SERVER_IP "docker run -d -p $PORT:$PORT -e JAVA_OPTS='-Xms512m -Xmx512m -Xss256K' --net=host --name $CONTAINER_NAME $IMAGE_TAG:$IMAGE_VERSION"
  30. when: manual

这几个步骤什么意思呢?

  • 定义项目级别的变量
  • 定义了 4 个步骤,其中每个步骤中的任务又是可以并行的
    • test:运行项目中的单元测试(项目中没有写单元测试)、集成测试
    • publishJar:发布项目中 protobuf 项目到私有 maven 仓库中
    • bootBuildImage:打包镜像,并根据配置发布到镜像仓库中,这里打包过程需要详细说明
    • deploy:部署镜像到远程服务器中,在此步骤中配置了 when:manual,意思是手动触发此步骤

注意: 这里 SERVER_USERSERVER_IP$REGISTERY_NAME$REGISTRY_PWD 在 Gitlab 中通过超级管理员做了全局配置,即在所有项目中都可以使用。

定义 gitlab CI/CD 变量

CI/CD 变量一共有 4 种定义方式,如下:

  1. .gitlab-ci.yml 文件中定义

  2. 在项目中定义
  3. 在组中定义
  4. gitlab 全局变量

变量优先级(从高到低)

  1. 触发变量、流水线变量、手动流水线变量
  2. 项目变量
  3. 组变量
  4. 全局变量
  5. 继承变量
  6. .gitlab-ci.yml 文件中,job 中定义的变量
  7. .gitlab-ci.yml 中定义的变量,job 外的变量
  8. 部署变量
  9. 预定义变量

源码地址

spring-boot 2.5.4,nacos 作为配置、服务发现中心,Cloud Native Buildpacks 打包镜像,GitLab CI/CD的更多相关文章

  1. Spring Boot 系列(三)属性配置&自定义属性配置

    在使用spring boot过程中,可以发现项目中只需要极少的配置就能完成相应的功能,这归功于spring boot中的模块化配置,在pom.xml中依赖的每个Starter都有默认配置,而这些默认配 ...

  2. 分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)

    本文描述spring boot基于Atomikos+DruidXADataSource分布式事务配置(100%纯动态),也就是增加.减少数据源只需要修改application.properties文件 ...

  3. 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean

    让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...

  4. Spring Boot 多模块项目创建与配置 (一) (转)

    Spring Boot 多模块项目创建与配置 (一) 最近在负责的是一个比较复杂项目,模块很多,代码中的二级模块就有9个,部分二级模块下面还分了多个模块.代码中的多模块是用maven管理的,每个模块都 ...

  5. Spring Boot 源码分析 数据源 + Mybatis 配置

    公司今年开始使用 Spring Boot 开发,当然使用 Spring Boot 也是大势所趋,尤其是现在微服务的趋向,当然是选择基于Spring Boot 的 Spring Cloud.(所谓的 S ...

  6. Spring Boot 多模块项目创建与配置 (一)

    最近在负责的是一个比较复杂项目,模块很多,代码中的二级模块就有9个,部分二级模块下面还分了多个模块.代码中的多模块是用maven管理的,每个模块都使用spring boot框架.之前有零零散散学过一些 ...

  7. Spring Boot 2.x Redis多数据源配置(jedis,lettuce)

    Spring Boot 2.x Redis多数据源配置(jedis,lettuce) 96 不敢预言的预言家 0.1 2018.11.13 14:22* 字数 65 阅读 727评论 0喜欢 2 多数 ...

  8. 51. spring boot属性文件之多环境配置【从零开始学Spring Boot】

    原本这个章节是要介绍<log4j多环境不同日志级别的控制的>但是没有这篇文章做基础的话,学习起来还是有点难度的,所以我们先一起了解下spring boot属性文件之多环境配置,当然文章中也 ...

  9. spring boot和maven的约定大于配置体现在哪些方面

    spring boot和maven的约定大于配置体现在哪些方面? 两者都遵从了约定大于配置的路线 约定优于配置体现点: 1.maven的目录文件结构 1)默认有resources文件夹,存放资源配置文 ...

随机推荐

  1. Thymeleaf模板引擎语法

    th:text    用于显示值 th:object      接收后台传来的对象 th:action      提交表单 th:value       绑定值 th:field         绑定 ...

  2. 二、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Modules的几种加载方式

    这一篇梳理Prism中07示例Module的几种加载方式. 07示例分为了5个,有5种不同的Module加载方式. 我们开始学习加载Modules 观察07-Modules-Appconfig示例 分 ...

  3. c语言:getchar() getch()回显

    //getch() 不回显函数,当用户按下某个字符时,函数自动读取,无需按回车 //所在头文件:conio.h 从控制台读取一个字符,但不显示在屏幕上 //int getchar() //头文件:#i ...

  4. Java基础00-集合基础15

    1. 集合基础 1.1 集合概述 1.2 ArrayList构造方法和添加方法 代码示例: 想通过指定索引添加到最后一个位置的话就填写,现有索引的+1,比如3个索引就是4.如果在指定索引的位置添加没有 ...

  5. React 之 组件生命周期

    React 之 组件生命周期 理解1) 组件对象从创建到死亡它会经历特定的生命周期阶段2) React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调3) 我们在定义组件时, ...

  6. 微信小程序云开发-云开发环境配置工作

    一.注册小程序 打开[微信开发者工具],点击界面上的[注册],进入注册微信小程序页面.(也可以直接进入微信小程序注册地址:https://mp.weixin.qq.com/进行注册) 进入[小程序注册 ...

  7. 从源码构建Vim

    从源码构建Vim 引言 事情是介样滴,因为我是个Vim 重度使用者了差不多.. 但在大部分系统上能安装到的或者自带的都是比较老的版本,可能是7.x 之类的.也或者是你需要使用到Vim 的某些特性或者功 ...

  8. transform和tolower

    transform:<algorithm> tolower:<ctype.h> transform有两种使用方法 第一种(参数): 源目标起始迭代器地址 源目标结束迭代器地址 ...

  9. [考试总结]noip模拟20

    第五场,再挂分就没了.. 然后就没了.. 考场上一直想方法. 似乎想到了 \(T1\) 正解. 然而几个 \(k\) 的调试信息都让我迷失了自我. 然后有几句啥都没用的语句加在了上面.. 挂分... ...

  10. Qt 入门 ---- 布局管理

    这是运行后的程序界面: 这是点击右上角"最大化"之后的程序界面: 接下来讲一下如何进行自动布局解决窗口拉伸问题. ① 原理: 在项目"设计"模式的左侧有如下两个 ...