SpringCloud Alibaba Nacos 服务注册和配置中心

一、前言

        接下来是开展一系列的 SpringCloud 的学习之旅,从传统的模块之间调用,一步步的升级为 SpringCloud 模块之间的调用,此篇文章为第十二篇,即介绍 SpringCloud Alibaba Nacos 服务注册和配置中心。

二、Nacos 简介

2.1 为什么叫 Nacos

        前四个字母分别为 Naming Configuration 的前两个字母,最后的 sService

2.2 Nacos 是什么

        Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

        Nacos 就是注册中心 + 配置中心的组合等价于 Eureka + Config  + Bus

2.3 作用

        替代 Eureka 做服务注册中心,替代 Config 做服务配置中心。

三、安装并运行 Nacos

3.1 前提

        确保本地有 Java8 Maven环境。

3.2 下载

        在官网下载合适的版本,如下图:

3.3 安装

        解压安装包,进入到 bin 目录下切换命令行,执行 startup.cmd -m standalone 命令即可,如下图:

3.4 测试

        命令运行成功后直接访问 http://localhost:8848/nacos,如下图:

        用户名和密码都是 nacos ,登录进去,如下:

四、Nacos作为服务注册中心演示

4.1 基于 Nacos 的服务提供者

4.1.1 创建工程

        创建一个服务提供者 cloudalibaba-provider-payment9001 模块,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>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloudalibaba-provider-payment9001</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

        我们可以这样引入的前提是:父工程引入了 alibaba 相关的依赖,如下:

<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-alibaba-dependencies</artifactId>
	<version>2.1.0.RELEASE</version>
	<type>pom</type>
	<scope>import</scope>
</dependency>

        application.yml 内容如下所示:

server:
  port: 9001

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

        主启动类的代码如下所示:

package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class PaymentMain9001
{
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9001.class, args);
    }
}

        业务类 PaymentController,代码如下所示:

package com.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/nacos/{id}")
    public String getPayment(@PathVariable("id") Integer id) {
        return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
    }
}

4.1.2 测试

        启动 cloudalibaba-provider-payment9001 模块,访问 http://localhost:9001/payment/nacos/1,如下图:

        打开 nacos 的控制台,可以看到服务注册的信息,如下图:

4.1.3 创建工程

        为了后面测试负载均衡,我们按照创建 9001 工程的步骤,再创建一个 9002 的工程,创建的步骤这里不再赘述。等到创建成功后,启动,打开 nacos 管理界面,如下图:

4.2 基于 Nacos 的服务消费者

4.2.1 创建工程

        创建一个服务消费模块 cloudalibaba-consumer-nacos-order83pom.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>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloudalibaba-consumer-nacos-order83</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

        application.yml 内容如下所示:

server:
  port: 83


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848


#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

        主启动类的代码如下所示:

package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain83
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderNacosMain83.class,args);
    }
}

        配置类的代码如下所示:

package com.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextBean
{
    @Bean
    // 设置支持负载均衡
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

         业务类 controller 的代码如下所示:

package com.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
public class OrderNacosController
{
    @Resource
    private RestTemplate restTemplate;

    @Value("${service-url.nacos-user-service}")
    private String serverURL;

    @GetMapping("/consumer/payment/nacos/{id}")
    public String paymentInfo(@PathVariable("id") Long id)
    {
        return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
    }

}

4.2.2 测试

        启动 cloudalibaba-consumer-nacos-order83 模块,打开 nacos 管理界面,如下图:

        在浏览器输入 http://localhost:83/consumer/payment/nacos/13,测试负载均衡,如下图:

        可以看到,实现了负载均衡的功能,Nacos 天生支持负载均衡的功能,如下图:

4.3 服务注册中心对比

4.3.1 Nacos 全景图

        Nacos 想要全景覆盖,每一种技术都想支持

4.3.2 Nacos 和 CAP

4.3.2 CP 和 AP 切换

        C 是所有节点在同一时间看到的数据是一致的;而 A 的定义是所有的请求都会收到响应。

        何时选择使用何种模式?一般来说,如果不需要存储服务级别的信息且服务实例是通过 nacos-client 注册,并能够保持心跳上报,那么就可以选择 AP 模式。当前主流的服务如 Spring cloudDubbo 服务,都适用于 AP 模式,AP 模式为了服务的可能性而减弱了一致性,因此 AP 模式下只支持注册临时实例。

        如果需要在服务级别编辑或者存储配置信息,那么 CP 是必须,K8S 服务和 DNS 服务则适用于 CP 模式。CP 模式下则支持注册持久化实例,此时则是以 Raft 协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。

        Nacos 支持 APCP 模式的切换,切换的命令为:

curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'

五、Nacos 作为服务配置中心演示

5.1 Nacos 作为配置中心-基础配置

5.1.1 创建工程

        创建一个 cloudalibaba-config-nacos-client3377 模块用来演示配置中心,pom.xml 内容如下所示,只是多了一个 nacos-config 的配置。

<?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>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloudalibaba-config-nacos-client3377</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--nacos-config-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--nacos-discovery-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--web + actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--一般基础配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

        创建两个配置文件 application.yml bootstarp.yml,为什么创建两个呢?因为 Nacos springcloud-config 一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。

        springboot 中配置文件的加载是存在优先级顺序的,bootstrap 优先级高于 application

        bootstarp.yml 的内容如下所示,里面标识出来了注册中心的地址和配置中心的地址,并标识除了读取哪种类型结尾的文件。

# nacos配置
server:
  port: 3377

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
      config:
        server-addr: localhost:8848 #Nacos作为配置中心地址
        file-extension: yaml #指定yaml格式的配置


  # ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}

        application.yml 内容如下所示:

spring:
  profiles:
    active: dev # 表示开发环境

        主启动类的代码如下所示:

package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377
{
    public static void main(String[] args) {
        SpringApplication.run(NacosConfigClientMain3377.class, args);
    }
}

        业务类 controller 的代码如下所示:

package com.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
// 在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
@RefreshScope 
public class ConfigClientController
{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}

5.1.2 Nacos 中添加配置信息

        我们想实现动态的获取远程配置文件的最新值,我们可以在 nacos 中添加配置信息,然后再获取。

        Nacos 中的 dataid 的组成格式及与 SpringBoot 配置文件中的匹配规则,官网的地址在这,匹配规则如下:

        最后的公式可以总结为:

${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

        打开 nacos 的管理界面,新增一个配置,如下图:

        总结起来就是如下图:

5.1.3 测试

        运行 cloud-config-nacos-client3377 的主启动类,调用接口查看配置信息:http://localhost:3377/config/info,如下图:

        修改下 Nacos 中的 yaml 配置文件,再次调用查看配置的接口,就会发现配置已经刷新,如下图:

5.1.4 历史配置

        Nacos 会记录配置文件的历史版本默认保留 30 天,此外还有一键回滚功能,回滚操作将会触发配置更新。

5.2 Nacos 作为配置中心-分类配置

5.2.1 问题

        实际开发中,通常一个系统会准备 dev 开发环境、test 测试环境、prod 生产环境。如何保证指定环境启动时服务能正确读取到 Nacos 上相应环境的配置文件呢?

        一个大型分布式微服务系统会有很多微服务子项目,每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境......那怎么对这些微服务配置进行管理呢?

5.2.2 解决方案

        我们可以在 Nacos 的图形化管理界面通过配置命名空间、Group Data Id 来解决上述的问题。

5.2.3 设计原因

        Namespace+Group+Data ID 三者关系?为什么这么设计?这三者之间的关系类似于 Java 里面的 package 名和类名,最外层的 namespace 是可以用于区分部署环境的,Group DataID 逻辑上区分两个目标对象。如下图:

        默认情况:Namespace=publicGroup=DEFAULT_GROUP,默认 Cluster DEFAULT

        Nacos 默认的命名空间是 publicNamespace 主要用来实现隔离。比方说我们现在有三个环境:开发、测试、生产环境,我们就可以创建三个 Namespace,不同的 Namespace 之间是隔离的。

        Group 默认是 DEFAULT_GROUPGroup 可以把不同的微服务划分到同一个分组里面去。

        Service 就是微服务;一个 Service 可以包含多个 Cluster(集群),Nacos 默认 ClusterDEFAULTCluster 是对指定微服务的一个虚拟划分。比方说为了容灾,将 Service 微服务分别部署在了杭州机房和广州机房,这时就可以给杭州机房的 Service 微服务起一个集群名称 HZ,给广州机房的 Service 微服务起一个集群名称 GZ,还可以尽量让同一个机房的微服务互相调用,以提升性能。

        最后是 Instance,就是微服务的实例。

5.2.4 DataID 方案

        指定 spring.profile.active 和配置文件的 DataID 来使不同环境下读取不同的配置,新建 pro 配置 DataID,如下图:

        新建 test 配置 DataID,如下图:

        通过 spring.profile.active 属性就能进行多环境下配置文件的读取,如下:

        输入 http://localhost:3377/config/info,进行测试,如下:

5.2.5 Group 方案

        可以通过 Group 实现环境区分,新建一个 Group,如下图:

        在 nacos 图形界面控制台上面新建配置文件 DataID,如下图:

        接下来在 config 下增加一条 group 的配置即可。可配置为 DEV_GROUP TEST_GROUP 

         输入 http://localhost:3377/config/info,进行测试,如下:

5.2.6 Namespace 方案

        新建 dev test 的 Namespace,如下图:

        接下来回到服务管理-服务列表查看,如下图:

        按照域名配置填写,如下图:

        在 bootstarp.yml 中添加 namespace 的属性,如下,值为上图生成的 id

          输入 http://localhost:3377/config/info,进行测试,如下:

 六、Nacos 集群和持久化配置

6.1 官网说明

6.1.1 架构图说明

        官网的地址在这,官网的架构图如下所示:

        看上面的架构图可能看的不是很懂,如果翻译一下,可能会更懂一些,翻译过来就是下面的这张图,如下:

6.1.2 重点说明

        默认 Nacos 使用嵌入式数据库实现数据的存储。所以,如果启动多个默认配置下的 Nacos 节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos 采用了集中式存储的方式来支持集群化部署,目前只支持 MySQL 的存储。下图摘抄自官网

6.2 Nacos 持久化配置解释

6.2.1 默认数据库

        Nacos 默认自带的是嵌入式数据库 derby

6.2.2 数据库切换

        derby mysql 切换配置步骤,首先在安装包的 nacos\conf 目录下找到 sql 脚本,如下:

        在 mysql 的客户端中执行里面的 sql 脚本,执行完毕后如下图:

        修改 nacos\conf 目录下的 application.properties 文件,填写自己的数据库信息,如下:

        修改完毕后重新启动 nacos,然后打开 nacos 的管理界面,如下图,可以看见,我们以前创建的配置列表都没有了,证明切换成功了。

6.3 Linux 版 Nacos + MySQL 生产环境配置

        我们此次搭建 1Nginx + 3nacos 注册中心 + mysql

ip 地址 用途
192.168.229.1 Mysql 数据库服务器
192.168.229.166 安装 nacos 节点
192.168.229.169 安装 nacos 节点
192.168.229.172 安装 nacos 节点和 Nginx

6.3.1 Nacos 下载 Linux 版

        下图摘抄自官网,可以从官网选取合适的 linux 版本,下载完成后拷贝到 linux 环境里面,如下图:

        分别拷贝三份到不同的服务器里面,并解压安装包并将解压后的文件拷贝到 opt 文件夹下,如下图: 

6.3.2 集群配置

6.3.2.1 mysql 数据库配置

        首先确保你的数据库的版本为 5.x 版本,若为 8.0 版本可能会出问题。

        然后进行 linux 服务器上 mysql 数据库配置,sql 语句在如下的位置

        运行上面的 mysql 数据库脚本,会创建一个 nacos_config 的数据库,如下:

6.3.2.2 application 配置

        分别修改三个服务器上的 /opt/nacos/conf/application.properties 文件,位置如下:

        在配置文件中添加如下的内容,修改完成后记得保存退出

spring.datasource.platform=mysql
 
db.num=1
db.url.0=jdbc:mysql://192.168.229.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=123456
6.3.2.3 集群配置

        接下来需要修改 cluster.conf 配置文件,文件位置如下所示:

        每台服务器都需要添加一样的内容,内容如下:

192.168.229.166:8848
192.168.229.169:8848
192.168.229.172:8848

6.3.2.4 启动验证

        在启动 nacos 之前,先将虚拟机里面三台服务器的防火墙关掉,命令如下:

systemctl status firewalld

systemctl stop firewalld

        分别启动三台服务器里面的 nacos,如下图:

        可以输入 tail -f /opt/nacos/logs/start.out  命令来查看日志的输出,当出现下面的提示时,就证明启动成功了。

        然后在浏览器分别访问刚刚开启的这三台 nacos,如下图:

6.3.2.5 nginx 配置

        Nginx 的下载和安装,请参考我的这篇博客,安装完成后,进入到 conf 文件夹下,准备修改 nginx.conf 配置文件,如下图:

        修改的内容如下图: 

        修改完成后,启动 nginx,如下图:

        在浏览器输入 http://192.168.229.172:1111/nacos,如下图:

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-23 23:54:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-23 23:54:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-23 23:54:03       82 阅读
  4. Python语言-面向对象

    2024-03-23 23:54:03       91 阅读

热门阅读

  1. 《单例模式(极简c++)》

    2024-03-23 23:54:03       45 阅读
  2. 冒泡排序和选择排序--C语言

    2024-03-23 23:54:03       40 阅读
  3. 2024.3.5 校招 实习 内推 面经

    2024-03-23 23:54:03       39 阅读
  4. linux arm64 opencv 使用MJPG获取摄像头图片

    2024-03-23 23:54:03       37 阅读
  5. Springboot集成jersey打包jar找不到class处理

    2024-03-23 23:54:03       43 阅读
  6. 动态更改 Spring 定时任务 Cron 表达式的优雅方案

    2024-03-23 23:54:03       36 阅读
  7. 运维工作内容分类以及简略介绍

    2024-03-23 23:54:03       41 阅读
  8. vue中v-if和v-show的区别

    2024-03-23 23:54:03       40 阅读
  9. SQL语句每日一练四

    2024-03-23 23:54:03       44 阅读
  10. 第一章 python自动化模块

    2024-03-23 23:54:03       45 阅读
  11. 内网环境中申请SSL证书

    2024-03-23 23:54:03       36 阅读
  12. [思考记录]技术欠账

    2024-03-23 23:54:03       43 阅读
  13. (九)任务通知

    2024-03-23 23:54:03       40 阅读
  14. 黑盒测试和白盒测试总结

    2024-03-23 23:54:03       38 阅读
  15. C++简单实现哈希查找

    2024-03-23 23:54:03       41 阅读