Docker-Compose 入门到实战详尽笔记

news2024/10/6 2:23:05

本文首发自「慕课网」(www.imooc.com),想了解更多IT干货内容,程序员圈内热闻,欢迎关注"慕课网"或慕课网公众号!

作者:暮闲 | 慕课网讲师


使用过 Docker 的小伙伴们都知道,启动 Docker 时一般会附带很多的启动参数,如 -v 指定挂载目录,-p 指定端口等等。除此之外,很多时候我们的业务系统中一般都会有几个 Docker 组合运行,容器间网络通信,容器的启动顺序等有明确的要求。基于这些问题,Docker-Compose 技术诞生。

本文将从基础到实战举例,共计7个部分,前面5个部分讲解基础,后面2个部分则时以实战为主,详细讲解 Docker-Compose 的使用。主要讲解如下内容:

  1. Docker-Compose 和 Docker 的联系
  2. 安装 Docker-Compose
  3. YAML 语法概述
  4. Docker-Compose 语法总结
  5. Docker-Compose 常用命令
  6. 实战演练一:Docker-Compose 部署伪分布式的 Elasticsearch
  7. 实战演练二:Docker-Compose 部署 Kafka(Zookeeper)

如果基础好一点的同学,可以直接跳到第6部分开始学习实战环节,有问题再倒回来查看基础。
阅读并实践本篇文章内容,大致需要30分钟左右。

Docker-Compose 和 Docker 的联系

相信你们在使用 Docker 时都遇到过以下两个场景:

  1. Docker 启动要指定参数,例如 -p 指定端口,-v 挂载目录等,当下次启动时,由于参数太多,忘记启动参数。
  2. 多个 Docker 容器启动时有依赖关系,不确定启动的先后顺序。

而 Docker-Compose 的出现就是为了解决上述两个问题,通过编写 yaml 文件定义 Docker 启动参数和编排容器。

安装 Docker-Compose

Docker-Compose 支持目前主流平台的安装,如图:

参考连接:Overview | Docker Documentation

小伙伴们根据自己的情况安装对应的版本即可,我这里以 Ubuntu 16.04 环境作为演示,也可以按照官网所述的安装步骤,也可以直接一句命令即可(当然前提时已经安装 Docker ):

apt install docker-compose

安装完毕后 docker-compose --version 查看版本

每次输入 docker-compose 这个命令确实比较长,我这里教搭建一个给 docker-compose 取别名的方式,操作如下:

vi ~/.bashrc
alias dc='docker-compose'
source ~/.bashrc

上述操作的原理就是在系统的环境变量里面为某个指令添加别名,执行完上述三条命令,docker-compose 使用 dc 就可以调用了,如图:

YAML 语法概述

Docker-Compose 容器编排的主要利用到 YAML 语法。学习 YAML 语法可以和 JSON 类比学习,例如 JSON 中有对象、数组等,利用 YAML 语法均可以表示出来。详细的内容推荐看阮一峰的《YAML语言教程》。

Docker-Compose 语法总结

前面的铺垫打好之后,这一章我们开始学习 Docker-Compose 语法,如果熟悉 Docker 语法的小伙伴,这一章将会学得很快。Docker-Compose 主要目的就是用作容器的编排,编排容器用的是 YAML 语法,编排文件内容都写在 docker-compose.yml 文件中。下面我们根据 Docker-Compose 的编排常用的语法依次做讲解,部分不常用的就小伙伴们根据自己的实际需求去官方网站查询即可,Docker 支持的操作,Docker-Compose 是肯定支持的。

version

每个 docker-compose.yml 文件第一个字段就是 version,version 字段是表明使用那个版本的 Compose ,Compose 有如下的版本,目前的最新版是 3.7,此外还有1、2、2.x、3.x,不同版本的 compose 支持了不同的 Docker 版本。

compose 与 Docker 的版本对应关系表:

除了表中显示的Compose文件格式版本外,Compose本身也处于发布计划中,如Compose版本中所示,但是文件格式版本不一定会随每个版本而增加。例如,Compose文件格式3.0最初是在Compose版本1.10.0中引入的,并在随后的版本中逐渐版本化。

语法结构如下:

version: "2"
version: "3.7"
#可以简写为
version: "3"

image

services:
  web:
    image: hello-world

在 services 标签下的第二级标签是 web,这个名字是用户自己自定义,它就是服务名称。
image 则是指定服务的镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。

build

服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。Compose 将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器。

build: /path/to/build/dir

也可以是相对路径,只要上下文确定就可以读取到 Dockerfile

build: ./dir

设定上下文根目录,然后以该目录为准指定 Dockerfile

build:
  context: ../
  dockerfile: path/of/Dockerfile

注意 build 都是一个目录,如果你要指定 Dockerfile 文件需要在 build 标签的子级标签中使用 dockerfile 标签指定,如上面的例子。
如果你同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。

build: ./dir
image: webapp:tag

既然可以在 docker-compose.yml 中定义构建任务,那么一定少不了 arg 这个标签,就像 Dockerfile 中的 ARG 指令,它可以在构建过程中指定环境变量,但是在构建成功后取消,在 docker-compose.yml 文件中也支持这样的写法:

build:
  context: .
  args:
    buildno: 1
    password: secret

下面这种写法也是支持的,一般来说下面的写法更适合阅读。

build:
  context: .
  args:
    - buildno=1
    - password=secret

与 ENV 不同的是,ARG 是允许空值的,这样构建过程可以向它们赋值。。例如:

args:
  - buildno
  - password

注意:YAML 的布尔值(true, false, yes, no, on, off)必须要使用引号引起来(单引号、双引号均可),否则会当成字符串解析。

command

使用 command 可以覆盖容器启动后默认执行的命令。

command: echo 'hello world'

也可以写成类似 Dockerfile 中的格式:

command: ['echo','hello world']

container_name

前面说过 Compose 的容器名称格式是:<项目名称><服务名称><序号>
虽然可以自定义项目名称、服务名称,但是如果你想完全控制容器的命名,可以使用这个标签指定:

container_name: app

这样容器的名字就指定为 app 了。

depends_on

在使用 Compose 时,最大的好处就是少打启动命令,但是一般项目容器启动的顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。
例如在没启动数据库容器的时候启动了应用容器,这时候应用容器会因为找不到数据库而退出,为了避免这种情况我们需要加入一个标签,就是 depends_on,这个标签解决了容器的依赖、启动先后的问题。
例如下面容器会先启动 redis 和 db 两个服务,最后才启动 web 服务:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

注意的是,默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系。

dns

和 --dns 参数一样用途,格式如下:

dns: 8.8.8.8

也可以是一个列表:

dns:
  - 8.8.8.8
  - 9.9.9.9

此外 dns_search 的配置也类似:

dns_search: example.com
dns_search:
  - dc1.example.com
  - dc2.example.com

tmpfs

挂载临时目录到容器内部,与 run 的参数一样效果:

tmpfs: /run
tmpfs:
  - /run
  - /tmp

entrypoint

在 Dockerfile 中有一个指令叫做 ENTRYPOINT 指令,用于指定接入点,ENTRYPOINT 配置容器启动时的执行命令,如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。(不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令)。
在 docker-compose.yml 中可以定义接入点,覆盖 Dockerfile 中的定义:

entrypoint: /code/entrypoint.sh

env_file

还记得前面提到的 .env 文件吧,这个文件可以设置 Compose 的变量。而在 docker-compose.yml 中可以定义一个专门存放变量的文件。
如果通过 docker-compose -f FILE 指定了配置文件,则 env_file 中路径会使用配置文件路径。

如果有变量名称与 environment 指令冲突,则以后者为准。格式如下:

env_file: .env

或者根据 docker-compose.yml 设置多个:

env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

注意的是这里所说的环境变量是对宿主机的 Compose 而言的,如果在配置文件中有 build 操作,这些变量并不会进入构建过程中,如果要在构建中使用变量还是首选前面刚讲的 arg 标签。

environment

与上面的 env_file 标签完全不同,反而和 arg 有几分类似,这个标签的作用是设置镜像变量,它可以保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置,这是与 arg 最大的不同。
一般 arg 标签的变量仅用在构建过程中。而 environment 和 Dockerfile 中的 ENV 指令一样会把变量一直保存在镜像、容器中,类似 docker run -e 的效果。

environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:
 
environment:
  - RACK_ENV=development
  - SHOW=true
  - SESSION_SECRET

expose

这个标签与Dockerfile中的EXPOSE指令一样,用于指定暴露的端口,但是只是作为一种参考,实际上docker-compose.yml的端口映射还得ports这样的标签。

expose:
 - "3000"
 - "8000"

external_links

在使用Docker过程中,我们会有许多单独使用docker run启动的容器,为了使 Compose 能够连接这些不在 docker-compose.yml 中定义的容器,我们需要一个特殊的标签,就是external_links,它可以让 Compose 项目里面的容器连接到那些项目配置外部的容器(前提是外部容器中必须至少有一个容器是连接到与项目内的服务的同一个网络里面)。
格式如下:

external_links:
 - redis_1
 - project_db_1:mysql
 - project_db_1:postgresql

extra_hosts

添加主机名的标签,就是往/etc/hosts文件中添加一些记录,与Docker client的–add-host类似:

extra_hosts:
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"

启动之后查看容器内部hosts:

162.242.195.82  somehost
50.31.209.229   otherhost

labels

向容器添加元数据,和 Dockerfile 的 LABEL 指令一个意思,类似于代码注释,这个就是对 Compose 做一个陈述,格式如下:

labels:
  com.example.description: "Accounting webapp"
  com.example.department: "Finance"
  com.example.label-with-empty-value: ""
labels:
  - "com.example.description=Accounting webapp"
  - "com.example.department=Finance"
  - "com.example.label-with-empty-value"

links

前面提到的参数 depends_on 解决的是启动顺序问题,这个标签解决的是容器连接问题,与 Docker client的–link 一样效果,会连接到其它服务中的容器。

links:
 - db
 - db:database
 - redis

ports

映射端口的标签。
使用 HOST:CONTAINER 格式或者只是指定容器的端口,宿主机会随机映射端口。

ports:
 - "3000"
 - "8000:8000"
 - "49100:22"
 - "127.0.0.1:8001:8001"

注意:当使用 HOST:CONTAINER 格式来映射端口时,如果你使用的容器端口小于60你可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。

volumes

挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER] 这样的格式,或者使用 [HOST:CONTAINER:ro] 这样的格式,后者对于容器来说,数据卷是只读的,这样可以有效保护宿主机的文件系统。
Compose的数据卷指定路径可以是相对路径,使用 . 或者 … 来指定相对目录。
数据卷的格式可以是下面多种形式:

volumes:
  # 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。
  - /var/lib/mysql
 
  # 使用绝对路径挂载数据卷
  - /opt/data:/var/lib/mysql
 
  # 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。
  - ./cache:/tmp/cache
 
  # 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。
  - ~/configs:/etc/configs/:ro
 
  # 已经存在的命名的数据卷。
  - datavolume:/var/lib/mysql

network_mode

网络模式,与Docker client的–net参数类似,只是相对多了一个service:[service name] 的格式。
例如:

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"

可以指定使用服务或者容器的网络,这几种网络模式有很大的区别,有空的话都可以去了解一下,默认是 bridge,即桥接模式。

networks

加入指定网络,格式如下:

services:
  some-service:
    networks:
     - some-network
     - other-network

关于这个标签还有一个特别的子标签aliases,这是一个用来设置服务别名的标签,例如:

services:
  some-service:
    networks:
      some-network:
        aliases:
         - alias1
         - alias3
      other-network:
        aliases:
         - alias2

相同的服务可以在不同的网络有不同的别名。

Docker-Compose 常用命令

这一章我们来继续学习有关 Docker-Compose 的执行命令,Docker-Compose 总体来看和 Docker 命令很类似,具体有哪些命令,我们可以通过 docker-compose -h 查看,如图:

下面我来依次对常用的做说明,并做适当的举例辅助理解。

docker-compose up -d nginx	构建建启动nignx容器
docker-compose exec nginx bash	登录到nginx容器中
docker-compose down	删除所有nginx容器,镜像
docker-compose ps	显示所有容器
docker-compose restart nginx	重新启动nginx容器
docker-compose run --no-deps --rm php-fpm php -v	在php-fpm中不启动关联容器,并容器执行php -v 执行完成后删除容器
docker-compose build nginx	构建镜像
docker-compose build --no-cache nginx	不带缓存的构建
docker-compose logs  nginx	查看nginx的日志
docker-compose logs -f nginx	查看nginx的实时日志
docker-compose config  -q	验证(docker-compose.yml)文件配置,当配置正确时,不输出任何内容,当文件配置错误,输出错误信息。
docker-compose events --json nginx	以json的形式输出nginx的docker日志
docker-compose pause nginx	暂停nignx容器
docker-compose unpause nginx	恢复ningx容器
docker-compose rm nginx	删除容器(删除前必须关闭容器)
docker-compose stop nginx	停止nignx容器
docker-compose start nginx	启动nignx容

到这里基础环境我们已经学习完毕,下面我开始进入紧张刺激的实战环节,小伙伴也可以在学完基础章节后,只看实战的标题,试着自己去写写,然后和我的对照这看。

实战演练一:Docker-Compose 部署伪分布式的 Elasticsearch

Elasticsearch 是一个开源、高扩展的分布式全文检索引擎,可以近乎实时的存储、检索数据。而且扩展性很好,条件允许下可以扩展到上百台服务器,处理 PB 级别的数据,已经广泛应用于业务系统及大数据项目中。通过 Docker-Compose 搭建的目的是我们可以快速搭建一个伪分布式的 Elasticsearch 集群,这样做的目的是为了学习方便同时也阔以用于小型系统的生产环境,从而不用为了搭建环境而浪费时间。

Elastisearch 官网 Docker-Compose 安装说明:Install Elasticsearch with Docker | Elasticsearch Guide [8.7] | Elastic

下面的 docker-compose 文件来自于 Elasticsearch 官网推荐,为了运行更方便,我提前将 Elasticsearch 镜像下载到阿里云镜像。详细的解释以注释的方式在文件中表示。

version: '2'
services:
  # 服务名称,这里启动了三个,分别是es01,es02,es03
  es01:
   #将官方地址替换为了我的阿里云地址,这样下载速度会更快
    image: registry.cn-hangzhou.aliyuncs.com/chand/elasticsearch:7.10.1
    #指定容器的名称
    container_name: es01
    environment:
      # 配置es相关的环境变量
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es02,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
     #ulimit设置限制的时候会设置两条线soft和hard线,当资源到达了soft线那么只是告警,如果达到了hard线那么内核就强制限制了。
    ulimits:
      memlock:
        soft: -1
        hard: -1
     #数据挂载到本地目录
    volumes:
      - data01:/usr/share/elasticsearch/data
     #指定暴露的端口
    ports:
      - 9200:9200
     #指定网路
    networks:
      - elastic
  #es02、es03与上述的es01配置几乎一致,后面就不赘述了
  es02:
    image: registry.cn-hangzhou.aliyuncs.com/chand/elasticsearch:7.10.1
    container_name: es02
    environment:
      - node.name=es02
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es03
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data02:/usr/share/elasticsearch/data
    
    networks:
      - elastic
  es03:
    image: registry.cn-hangzhou.aliyuncs.com/chand/elasticsearch:7.10.1
    container_name: es03
    environment:
      - node.name=es03
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=es01,es02
      - cluster.initial_master_nodes=es01,es02,es03
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data03:/usr/share/elasticsearch/data
    networks:
      - elastic
volumes:
  data01:
    driver: local
  data02:
    driver: local
  data03:
    driver: local
networks:
  elastic:
    driver: bridge

配置好上述命令后,我们来执行命令 docker-compose up -d 启动 Compose,docker-compose ps 查看启动状态,如图:

ip:9200/_cluster/health?pretty浏览器查看是否启动集群成功,我本地 ip 是 192.168.10.107,访问地址:http://192.168.10.107:9200/_cluster/health?pretty,结果如图:

可以看到成功启动了集群,至此我们的第一实战演示完毕。我这里在运行时候由于内存不足报错了异常信息如下:

[max virtual memory areas vm.max_map_count [65530\] is too low, increase to at least [262144]](https://www.cnblogs.com/yidiandhappy/p/7714489.html)

解决方案:

sysctl -w vm.max_map_count=262144

为了防止重启后失效,修改虚拟机配置文件 vi /etc/sysctl.conf,在最后一行添加如下内容:

vm.max_map_count=262144

实战演练二:Docker-Compose 部署 Kafka(Zookeeper)

紧接着我们来到第二个实战演示,这个也是小伙伴们在日常工作会遇到的场景,搭建 Kafka。搭建 kafka 比较麻烦一点的是因为 Kafka 高度依赖 Zookeeper。下面我们来看一下通过 Docker-Compose 如何解决,详细说明在文件中以注解的形式表示。

version: '2'
services:
  zookeeper:
    #拉取 zookeeper镜像,更换为阿里云镜像源,提升拉取速度
    image: registry.cn-hangzhou.aliyuncs.com/chand/zookeeper
    #暴露映射的端口
    ports:
      - "2181:2181"
    #指定zookeeper的容器名称
    container_name: "zookeeper"
    #自动重启,即使主机重启也会重启容器
    restart: always
  kafka:
    #拉取 kafka 镜像,更换为阿里云镜像源,提升拉取速度
    image: registry.cn-hangzhou.aliyuncs.com/chand/kafka:2.12-2.3.0
    #指定kafka容器名称
    container_name: "kafka"
    #指定端口
    ports:
      - "9092:9092"
    #设置相关kafka的配置
    environment:
      - TZ=CST-8
      - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
      # 非必须,设置自动创建 topic
      - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
      # 修为自己本机实际的IP地址
      - KAFKA_ADVERTISED_HOST_NAME=192.168.10.107
      - KAFKA_ADVERTISED_PORT=9092
      # 修改为自己实际的IP地址
      - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.10.107
      - KAFKA_LISTENERS=PLAINTEXT://:9092
      # 非必须,设置对内存
      - KAFKA_HEAP_OPTS=-Xmx1G -Xms1G
      # 非必须,设置保存7天数据,为默认值
      - KAFKA_LOG_RETENTION_HOURS=1
    volumes:
      # 将 kafka 的数据文件映射出来
      - /home/kafka:/kafka
      - /var/run/docker.sock:/var/run/docker.sock
     #自动重启,即使主机重启也会重启容器
    restart: always

执行命令 dc up -d 启动服务,dc ps查看服务的启动状态。

启动完毕后,我们来验证是否成功部署 kafka,我这里通过 Java 调用 kafka 接口,演示创建主题和遍历主题,以测试 kafka 是否联通为目的。

AdminClientTest

package cn.czyfwpla.kafka.examples.admin;

import java.util.concurrent.ExecutionException;

/**
 * @author mxq
 * 调用kafka admin api 代码示例
 */
public class AdminClientTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        AdminExampleApi adminExampleApi = new AdminExampleApi();
        adminExampleApi.listTopics();
        adminExampleApi.createTopic("mxq-test");
        adminExampleApi.listTopics();
    }

}

AdminExampleApi

package cn.czyfwpla.kafka.examples.admin;

import cn.czyfwpla.kafka.examples.KafkaConstant;
import org.apache.kafka.clients.admin.*;
import org.apache.kafka.common.KafkaFuture;

import java.util.*;
import java.util.concurrent.ExecutionException;

/**
 * @author mxq
 */
public class AdminExampleApi {


    //创建主题
    public void createTopic(String topicName) throws ExecutionException, InterruptedException {
        Properties properties = new Properties();
        properties.setProperty(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KafkaConstant.KAFKA_HOST);


        AdminClient adminClient = AdminClient.create(properties);

        //副本数量
        Short replaceNum = 1;


        NewTopic newTopic = new NewTopic(topicName, 1, replaceNum);


        Collection collection = new ArrayList();
        collection.add(newTopic);
        CreateTopicsResult topics = adminClient.createTopics(collection);
        KafkaFuture<Void> all = topics.all();
        all.get();
    }

    //遍历主题
    public void listTopics() throws ExecutionException, InterruptedException {
        Properties properties = new Properties();
        properties.setProperty(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, KafkaConstant.KAFKA_HOST);

        AdminClient adminClient = AdminClient.create(properties);

        ListTopicsResult listTopicsResult = adminClient.listTopics();

        KafkaFuture<Set<String>> names = listTopicsResult.names();

        Set<String> stringSet = names.get();

        Iterator<String> iterator = stringSet.iterator();

        //遍历主题的名称
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        KafkaFuture<Collection<TopicListing>> listings = listTopicsResult.listings();
        Collection<TopicListing> topicListings = listings.get();
        //遍历主题、消息类型
        for (TopicListing topicListing : topicListings) {
            System.out.println(topicListing.toString());
        }
    }

}

结果:

总结

到这里文章也该结束了,对整个文章做一个总结吧,为什么说已经有很多作者都写了 Docker-Compose ,我这里又来写呢。原因目前市面上的 Docker-Compose 文章主要是讲解理论为主,小伙伴们在学习的时候如果不亲自实践,我觉得学到的东西也仅仅停留在理论,难以在工作中起到帮助作用。我这篇文章前面讲解理论为基础,后面以讲解实战为主。前两个实战在我们学习或搭建小型生产环境时,极大提升效率,第三个实战则是以实际项目发布为背景,四个容器组合发布,让小伙伴们直观感受到 Docker-Compose 给我们带来的便利。实战环境由于我的环境和小伙伴们的环境多少会有一些不同,如果你在运行的时候遇到问题,亦或者是文章有写得不对的地方,欢迎在下方留言指出。


欢迎关注「慕课网」帐号,我们会一直坚持内容原创,提供IT圈优质内容,分享干货知识,大家一起共同成长吧!

本文原创发布于慕课网 ,转载请注明出处,谢谢合作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/539931.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

职场小白如何在工作中快速的升职加薪

缘起 近来连续两个季度很轻松的获得优秀&#xff0c;在这轻松的背后&#xff0c;一定有些原因支撑这领导给了这个评价。坦白说&#xff0c;最近两个季度&#xff0c;无一天加班&#xff0c;因为我们团队不提倡加班&#xff1b;我这边离领导较远&#xff0c;属于两个城市异地办…

一天吃透Java面试八股文

Java的特点 Java是一门面向对象的编程语言。面向对象和面向过程的区别参考下一个问题。 Java具有平台独立性和移植性。 Java有一句口号&#xff1a;Write once, run anywhere&#xff0c;一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编…

chatgpt赋能Python-pyhton如何安装

Python的安装方法 Python是一种高级编程语言&#xff0c;适用于多种开发需求&#xff0c;从网站构建到机器学习。其易用和灵活的语法使其成为一种非常受欢迎的编程语言。本文将向您介绍如何安装Python。 Python的安装步骤 以下是安装Python的步骤。 步骤1&#xff1a;下载P…

[笔记]初识Burpsuit

文章目录 前言一、安装配置1.1 环境1.2 安装过程1.3 科技过程 二、常用功能2.1 Manual penetration testing features2.2 Advanced/custom automated attacks2.3 Automated scanning for vulnerabilities2.4 Productivity tools2.5 Extensions 三、拓展功能 前言 Burp Suite(b…

【C++】 制作游戏壳

目录 前言 GameFrame游戏壳 搭建游戏壳 游戏初始化 游戏重绘 游戏运行 用回调函数实现游戏运行 关闭窗口&#xff0c;退出程序 测试 增加子类继承游戏壳子 继承 多态 优化 测试 总结 使用方法 常见错误 完整代码 GameFrame.h main.cpp 前言 为了方便以后制…

数据存储梳理记录

目录 1、FMDB-第三方SQLite数据库框架1.1 现状1.2 线程安全问题1.2.1 FMDatabase1.2.2 FMDatabaseQueue1.2.3 FMDatabasePool 2、进程间通信2.1 URL Scheme2.2 keyChain2.3 UIPasteboard2.4 UIDocumentInteractionController2.5 local socket2.6 AirDrop2.7 UIActivityViewCont…

Blender 建模小飞机(基础着色、Cycles渲染引擎)

目录 1. 飞机建模1.1 机身1.2 机身表面细分1.3 机翼1.4 尾翼1.5 尾翼镜像1.6 涡轮1.7 添加经纬球1.8 螺旋桨1.9 螺旋桨调整1.10 柱子1.11 柱子镜像1.12 起落架1.13 轮胎1.14 管1.15 镜像1.16 调整飞机角度 2. 着色 渲染2.1 添加地面2.2 飞机着色2.3 其他材质着色2.4 环境纹理2.…

JSDoc 拥抱 Javascript

JSDoc 在 vs code 已经内置了. 可以在 js 文件的开头添加 // ts-check 即可. 在注释中标注来实现一些 ts 的功能. JSDoc 支持以下注解. Types typeparam (or arg or argument)returns (or return)typedefcallbacktemplate Classes Property Modifiers public, private, p…

Go1.21 速览:Go 终于打算进一步支持 WebAssembly 了。。。

大家好&#xff0c;我是煎鱼。 之前写过一篇关于 Go WebAssembly 的相关文章 《一分钟搞明白&#xff01;快速掌握 Go WebAssembly》&#xff0c;今天带来一则新消息。 想着 Go 过去了那么多年了&#xff0c;只在 Go1.11 支持了 WebAssembly1.0 的部分功能&#xff08;js/sysca…

一个实际音视频开发问题!

前言&#xff1a; 大家好&#xff0c;今天给大家分享的内容是关于平时在做音频编解码会遇到的一些问题&#xff0c;比如说&#xff1a; 解码播放的时候&#xff0c;播不出来解码播放的时候&#xff0c;画面有条纹编码的时候&#xff0c;修改分辨率大小&#xff0c;没有反应 这三…

深圳先进院李骁健团队:植入式脑机接口技术向医疗器械转化的问题与挑战

近几年植入式脑机接口技术取得了非常显著的进步&#xff0c;从工程实现能力和服务功能场景来说&#xff0c;脑机接口技术已经达到了临床应用的临界点&#xff0c;在实验室科研成果向临床医疗器械转化过程中将会面临新的挑战。本文章由此出发&#xff0c;首先介绍了脑机接口技术…

工程监测无线中继采集仪的常用功能与设置

工程监测无线中继采集仪的常用功能与设置 LoRA 频道与中心频率 无线中继采集发送仪使用频道来设置不同的射频中心频率。 中心频率 MHz 基频 (频道) &#xff0c;无线中继采集发送仪 的 LoRA 基频已设置为 420 或者 854MHz&#xff0c;可以使用$STRFxxx 重新设置基频。 例如&a…

RT-Thread 1. GD32移植RT-Thread Nano

1. RT-Thread Nano 下载 RT-Thread Nano 是一个极简版的硬实时内核&#xff0c;它是由 C 语言开发&#xff0c;采用面向对象的编程思维&#xff0c;具有良好的代码风格&#xff0c;是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小&#xff0c;功能包括任务处理…

Ansys Zemax | 设计抬头显示器时要使用哪些工具 – 第三部分

本文为使用OpticStudio工具设计优化HUD抬头显示器系统的第三部分&#xff0c;主要包含演示了如何使用OpticStudio非序列模式工具正向分析HUD系统的性能以及后续可能的扩展分析。 上两篇文章中(第一部分点此查看&#xff0c;第二部分点此查看)&#xff0c;我们主要介绍了如何以逆…

js 定时去重复显示和隐藏的思路

文章目录 思路一 每秒执行思路二 直接用定时器&#xff0c;但是不每秒思路三 es9 异步迭代异步迭代例子1 直接使用例子2 async await例子3 * yield异步遍历器 --》要想用for await of 必须遍历这个简化 for await of解决1秒出现第一个结果&#xff0c;再过2秒出现第二个&#x…

电脑录屏软件哪个好用?3款屏幕录制大师分享!

案例&#xff1a;如何快速录制电脑屏幕&#xff1f; 【每次录制电脑屏幕都要花费我很多时间&#xff0c;十分影响我工作的效率。有没有什么方法可以帮助我实现电脑快速录屏&#xff1f;蹲一款好用的电脑录屏软件!】 电脑录屏是我们在工作或学习中常常需要使用到的功能&#x…

Swagger介绍及Springboot整合Swagger避坑

Swagger简介 前后端分离 vueSpringboot 后端时代&#xff1a;前端之管理静态页面&#xff0c;html>后端。模板引擎 JSP>后端是主力 前后端分离时代&#xff1a; 后端&#xff1a;后端控制层、服务层、数据访问层【后端团队】前端&#xff1a;前端控制层、视图层【前端…

网易云音乐开发--recommendSong搭建

recommendSong页面头部静态搭建 先新建一个 recommendSong 修改上面的nav部分 写上面的样式。我们现在写在这个框里面放个日期 就是让这个文字基于父元素垂直水平居中 样式搞定 recommendSong日期动态显示 之前我们将recommendSong静态头部做好了&#xff0c;现在我们需…

Springboot idea 中 maven配置问题,找不到依赖:Could not find artifact xxxx

现象&#xff1a;当我们从代码仓拉取新项目时&#xff0c;从该项目的开发同事拿到其maven的settings文件&#xff0c;作为项目的maven配置&#xff0c;为了是能找到工程中所依赖的包&#xff0c;能从远程仓下载下来。 然后本地仓的包&#xff0c;也从同事那边拷贝一份过来&…

二叉搜索树详解及代码实现

目录 一、什么是二叉搜索树 二、二叉搜索树的有关操作 2.1 查找&#xff1a; 2.2插入&#xff1a; 2.3 删除&#xff1a; 2.4 打印 三、二叉搜索树的应用 3.1 K模型&#xff1a; 3.2 KV模型&#xff1a; 四、整体代码&#xff1a; K模型&#xff1a; KV模型&#xff…