Chendi WuMediaProjectsBlog
Back to blog

工程化与运维

前端工程化与运维笔记:HTTP、Docker、Jenkins、Nginx、Linux、PM2、Monorepo、私有服务(Nexus/Verdaccio)等。

2023-11-21
NotesEngineering

前言

前端工程化

前端工程化 是指将前端开发的流程规范化、标准化,包括开发流程、技术选型、代码规范、构建发布等,用于提升前端工程师的开发效率和代码质量。

diagram1.jpg

复杂度高:前端项目的多功能、多页面、多状态、多系统 规模大:团队开发,多人协作,代码质量管理 要求高:页面性能优化(CDN、异步,请求合并),CSS兼容性、单页面应用、服务端渲染...

实践流程介绍

diagram2.jpg

缺陷控制工具

代码类: ESLint、JSLint、StyleLint... 流程类:Jira、禅道、Redmine、tapd... 工具类:Trello、Teambition、钉钉、石墨...

diagram3.jpg

diagram4.jpg

落地考虑

业务着手

简单的单页面应用,使用 gulp 打包+同步工具实现开发全流程

复杂度考虑

Jenkins git/gitlab webpack react、vue、 angular

从已知向未知扩展

不同的技术有不同的适应点,选择适合的才是最好的

考虑:
  • 前后端技术是否更好的融合
  • 前后端分离,接口安全性
  • vue 或 react 是否适合制作静态页面
  • 后端技术点是否更好的集合
项目实践:
  • 组件化实现代码复用
  • 脚手架让我们代码成为一种框架,在多个项目进行使用
  • 工程cli工具提高我们项目构建与发布
  • Git flow
  • webpack,gulp
  • Jenkins,Docker

前端理想架构

diagram5.jpg

Lerna

https://github.com/lerna/lerna

Monorepo

Monorepo 的全称是 monolithic repository,即单体式仓库,与之对应的是 Multirepo(multiple repository),这里的“单”和“多”是指每个仓库中所管理的模块数量。

Monorep 是把所有相关的 package 都放在一个仓库里进行管理,每个 package 独立发布。例如:React, Angular, Babel, Jest, Umijs, Vue ...

Lerna 是业界知名度最高的 Monorepo 管理工具,功能完整。

Lerna

PM2

PM2 使用指南

安装

npm install pm2@latest -g
yarn global add pm2

基础命令

进程管理
# 基础启动
pm2 start app.js

# 指定应用名称
pm2 start app.js --name "myapp"

# 启用监视模式(文件变化时自动重启)
pm2 start app.js --watch

# 启动4个实例(集群模式)
pm2 start app.js -i 4

# 根据CPU核心数启动最大实例数
pm2 start app.js -i max
# 硬重启
pm2 restart app.js

# 零停机重载(推荐用于生产环境)
pm2 reload app.js

# 优雅重载所有应用
pm2 gracefulReload all
# 停止特定应用
pm2 stop app.js

# 停止所有应用
pm2 stop all
# 删除特定应用
pm2 delete app.js

# 删除所有应用
pm2 delete all
监控和查看
# 显示所有进程状态
pm2 list

# 等同于 list
pm2 status
# 显示应用详细信息
pm2 show <app-name>

# 等同于 show
pm2 describe <app-name>
# 监控所有进程资源使用情况
pm2 monit
# 显示所有应用日志
pm2 logs

# 显示特定应用日志
pm2 logs <app-name>

# 显示最后200行日志
pm2 logs --lines 200

配置文件使用

配置文件示例
{
  "apps": {
    "name": "ws", // 应用名
    "script": "index.js", // 启动应用文件位置
    "cwd": "./", // 当前工作路径
    "watch": true, // 监听模式
    "ignore_watch": [
      // 忽略不想监听的文件改动
      "node_modules",
      "logs"
    ],
    "instances": 4, // 多进程,0表示根据CPU数量启动尽可能多的进程(集群模式)
    "error_file": "logs/err.log", // 普通输出日志文件及位置
    "out_file": "logs/out.log", // 错误输出日志文件及位置,使用pm2 install pm2-logrotate进行日志文件拆分
    "log_type": "json", // 日志类型
    "log_date_format": "YYYY-MM-DD HH:mm:ss" // 日志日期记录格式
  }
}
启动配置
# 使用默认环境
pm2 start ecosystem.config.js
# 使用生产环境配置
pm2 start ecosystem.config.js --env production

常用维护命令

# 保存当前应用列表
pm2 save

# 热更新 PM2
pm2 update
# 生成开机自启脚本
pm2 startup

# 保存当前应用列表以便开机启动
pm2 save
# 清空所有日志
pm2 flush

StoryBook

📖 简介

react-components 属于前端基础组件库项目,可使用于所有项目。

使用 React+TypeScript+StoryBook 开发 npm 组件,可发布至公司内部私有 npm 库中使用

🔧 开始使用

import { Button } from 'xxx-ui'

const renderButton = () => (
  <>
    <Button btnType='primary'> primary button </Button>
    <Button btnType='danger'> danger button </Button>
    <Button btnType='link' href='https://github.com/WuChenDi'>
      link github
    </Button>
  </>
)

🤝 参与共建

如果您对这个项目感兴趣,欢迎提 pull request,也欢迎 "Star" 支持一下 _

🚗 本地运行

1、克隆项目

git clone https://github.com/WuChenDi/react-components.git

2、安装依赖

yarn

3、运行项目

yarn start # 访问 http://localhost:6006

👀 项目效果图

index.png

GitHub

https://github.com/WuChenDi/react-components

版本管理

Semver:https://semver.org/lang/zh-CN/

参考

https://juejin.cn/post/6956532675473571876

使用nodejs 获取 npm 官方平台 包最新版本

async function getLatestVersion(id, range = '') {
  const registry = 'https://registry.npmmirror.com'
  return axios.get(`${registry}/${encodeURIComponent(id).replace(/^%40/, '@')}/${range}`)
}

const res = await getLatestVersion('axios', 'latest')
console.log(res.data.version)

组件的设计

https://juejin.cn/post/6991261103141421092

前言

组件是页面不可或缺的部分,而设计组件就成为了前端同学每日工作。

知识点

  • 组件是如何分类的
  • Vue 和 React 封装组件模式
  • 怎样才是一个好的可扩展、通用的、健壮性组件
  • 思考讨论,提出问题

组件分类

diagram6.jpg 无论是 业务组件 或者 通用组件都具备组件本质所包含的三个性质扩展、通用、健壮

  • 扩展性:在原有组件基础上可 二次封装 扩展成新的组件符合设计的开闭原则
  • 通用性:根据组件接受的参数和组件中与业务的解耦比来衡量组件的通用性,并不是通用性占比100%的组件就是最好的组件,需要根据 不同的场景 分析
  • 健壮性:避免组件中参数处理和函数执行过程可能出现的奔溃和错误导致程序的直接挂断,单测以对组件内部 做好边界处理,异常错误的捕获来衡量这一标准
业务组件

服务与业务的组件称为业务组件,项目中组件的划分是分页面级组件、全局级别组件

Docker / docker-compose

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

安装

# 安装扩展源
$ sudo yum -y install epel-release

# 安装python-pip模块
$ sudo yum install python-pip

# 注:Centos 8已经换成python3
$ sudo yum install python3-pip

# 通过以命令进行安装

# 方式一
$ cd /usr/local/bin/

$ wget https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64

$ rename docker-compose-Linux-x86_64 docker-compose docker-compose-Linux-x86_64

# 方式二
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 更改权限
$ chmod +x /usr/local/bin/docker-compose

# 打印输入信息
$ docker-compose version

卸载

# 如果通过 curl 命令安装 Docker Compose:
$ sudo rm /usr/local/bin/docker-compose

# 如果通过 pip 命令安装 Docker Compose:
$ pip uninstall docker-compose

常见命令

# 列出所有运行容器
$ docker-compose ps

# 查看服务日志输出
$ docker-compose logs

# 打印绑定的公共端口,例如:输出 eureka 服务 8761 端口所绑定的公共端口
$ docker-compose port eureka 8761

# 构建或者重新构建服务
$ docker-compose build

# 启动指定服务已存在的容器
$ docker-compose start eureka

# 停止已运行的服务的容器
$ docker-compose stop eureka

# 删除指定服务的容器
$ docker-compose rm eureka

# 构建、启动容器(-d:表示在后台运行)
$ docker-compose up

# 通过发送 SIGKILL 信号来停止指定服务的容器
$ docker-compose kill eureka

# 下载服务镜像

# 设置指定服务运气容器的个数,以 service=num 形式指定
$ docker-compose scale user=3 movie=3

# 在一个服务上执行一个命令
$ docker-compose run web bash

问题记录

https://stackoverflow.com/questions/58747879/docker-compose-usr-local-bin-docker-compose-line-1-not-command-not-found

Docker / docker-gitlab

官网:https://docs.gitlab.com/omnibus/docker/

GitHub:https://github.com/sameersbn/docker-gitlab

安装

docker run

$ sudo docker run --detach \
    --hostname 10.100.68.163 \
    --publish 13800:80 --publish 13822:22 \
    --name gitlab_test \
    --restart always \
    gitlab/gitlab-ee:latest

# 查看
$ docker ps | grep gitlab_test

# 开放端口
$ firewall-cmd --add-port=13800/tcp --permanent

# 更新防火墙规则
$ firewall-cmd --reload

# 持续输出日志
$ docker logs -f gitlab_test

image.png

设置root密码

image.png

docker-gitlab

docker-compose.yml

wget https://raw.githubusercontent.com/sameersbn/docker-gitlab/master/docker-compose.yml

version: "2.3"

services:
  redis:
    restart: always
    image: redis:5.0.9
    command:
    - --loglevel warning
    volumes:
    - redis-data:/var/lib/redis:Z

  postgresql:
    restart: always
    image: sameersbn/postgresql:12-20200524
    volumes:
    - postgresql-data:/var/lib/postgresql:Z
    environment:
    - DB_USER=gitlab
    - DB_PASS=password
    - DB_NAME=gitlabhq_production
    - DB_EXTENSION=pg_trgm,btree_gist

  gitlab:
    restart: always
    image: sameersbn/gitlab:13.12.1
    depends_on:
    - redis
    - postgresql
    ports:
    - "13800:80"
    - "13822:22"
    volumes:
    - gitlab-data:/home/git/data:Z
    healthcheck:
      test: [CMD, /usr/local/sbin/healthcheck]
      interval: 5m
      timeout: 10s
      retries: 3
      start_period: 5m
    environment:
    - DEBUG=false

    - DB_ADAPTER=postgresql
    - DB_HOST=postgresql
    - DB_PORT=5432
    - DB_USER=gitlab
    - DB_PASS=password
    - DB_NAME=gitlabhq_production

    - REDIS_HOST=redis
    - REDIS_PORT=6379

    - TZ=Asia/Kolkata
    - GITLAB_TIMEZONE=Kolkata

    - GITLAB_HTTPS=false
    - SSL_SELF_SIGNED=false

    - GITLAB_HOST=10.100.68.163
    - GITLAB_PORT=13800
    - GITLAB_SSH_PORT=13822
    - GITLAB_RELATIVE_URL_ROOT=
    - GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alphanumeric-string
    - GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alphanumeric-string
    - GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alphanumeric-string

    - GITLAB_ROOT_PASSWORD=123456789
    - GITLAB_ROOT_EMAIL=996194720@qq.com

    - GITLAB_NOTIFY_ON_BROKEN_BUILDS=true
    - GITLAB_NOTIFY_PUSHER=false

    - GITLAB_EMAIL=notifications@example.com
    - GITLAB_EMAIL_REPLY_TO=noreply@example.com
    - GITLAB_INCOMING_EMAIL_ADDRESS=reply@example.com

		# 备份
    - GITLAB_BACKUP_SCHEDULE=daily
    - GITLAB_BACKUP_TIME=01:00
    # 备份超时
    - GITLAB_BACKUP_EXPIRY=604800

    - SMTP_ENABLED=false
    - SMTP_DOMAIN=www.example.com
    - SMTP_HOST=smtp.gmail.com
    - SMTP_PORT=587
    - SMTP_USER=mailer@example.com
    - SMTP_PASS=password
    - SMTP_STARTTLS=true
    - SMTP_AUTHENTICATION=login

    - IMAP_ENABLED=false
    - IMAP_HOST=imap.gmail.com
    - IMAP_PORT=993
    - IMAP_USER=mailer@example.com
    - IMAP_PASS=password
    - IMAP_SSL=true
    - IMAP_STARTTLS=false

    - OAUTH_ENABLED=false
    - OAUTH_AUTO_SIGN_IN_WITH_PROVIDER=
    - OAUTH_ALLOW_SSO=
    - OAUTH_BLOCK_AUTO_CREATED_USERS=true
    - OAUTH_AUTO_LINK_LDAP_USER=false
    - OAUTH_AUTO_LINK_SAML_USER=false
    - OAUTH_EXTERNAL_PROVIDERS=

    - OAUTH_CAS3_LABEL=cas3
    - OAUTH_CAS3_SERVER=
    - OAUTH_CAS3_DISABLE_SSL_VERIFICATION=false
    - OAUTH_CAS3_LOGIN_URL=/cas/login
    - OAUTH_CAS3_VALIDATE_URL=/cas/p3/serviceValidate
    - OAUTH_CAS3_LOGOUT_URL=/cas/logout

    - OAUTH_GOOGLE_API_KEY=
    - OAUTH_GOOGLE_APP_SECRET=
    - OAUTH_GOOGLE_RESTRICT_DOMAIN=

    - OAUTH_FACEBOOK_API_KEY=
    - OAUTH_FACEBOOK_APP_SECRET=

    - OAUTH_TWITTER_API_KEY=
    - OAUTH_TWITTER_APP_SECRET=

    - OAUTH_GITHUB_API_KEY=
    - OAUTH_GITHUB_APP_SECRET=
    - OAUTH_GITHUB_URL=
    - OAUTH_GITHUB_VERIFY_SSL=

    - OAUTH_GITLAB_API_KEY=
    - OAUTH_GITLAB_APP_SECRET=

    - OAUTH_BITBUCKET_API_KEY=
    - OAUTH_BITBUCKET_APP_SECRET=
    - OAUTH_BITBUCKET_URL=

    - OAUTH_SAML_ASSERTION_CONSUMER_SERVICE_URL=
    - OAUTH_SAML_IDP_CERT_FINGERPRINT=
    - OAUTH_SAML_IDP_SSO_TARGET_URL=
    - OAUTH_SAML_ISSUER=
    - OAUTH_SAML_LABEL="Our SAML Provider"
    - OAUTH_SAML_NAME_IDENTIFIER_FORMAT=urn:oasis:names:tc:SAML:2.0:nameid-format:transient
    - OAUTH_SAML_GROUPS_ATTRIBUTE=
    - OAUTH_SAML_EXTERNAL_GROUPS=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_EMAIL=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_NAME=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_USERNAME=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_FIRST_NAME=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_LAST_NAME=

    - OAUTH_CROWD_SERVER_URL=
    - OAUTH_CROWD_APP_NAME=
    - OAUTH_CROWD_APP_PASSWORD=

    - OAUTH_AUTH0_CLIENT_ID=
    - OAUTH_AUTH0_CLIENT_SECRET=
    - OAUTH_AUTH0_DOMAIN=
    - OAUTH_AUTH0_SCOPE=

    - OAUTH_AZURE_API_KEY=
    - OAUTH_AZURE_API_SECRET=
    - OAUTH_AZURE_TENANT_ID=

volumes:
  redis-data:
  postgresql-data:
  gitlab-data:

启动

$ vi docker-compose.yml

$ docker-compose up -d

$ docker ps

$ docker logs -f di-got_gitlab_1

image.png

image.png

image.png

设置GITLAB_BACKUP_EXPIRY

自动备份
$ vi ./docker-compose.yml

# ...
- GITLAB_BACKUP_SCHEDULE=daily
- GITLAB_BACKUP_TIME=01:00
- GITLAB_BACKUP_EXPIRY=604800
#...

# 更新配置
$ docker-compose up -d

image.png

手动备份
$ docker-compose run --rm gitlab app:rake gitlab:backup:create

image.png

image.png

Warning解释: 敏感信息也已经备份成功

恢复
# 恢复可用的备份
$ docker-compose run --rm gitlab app:rake gitlab:backup:restore

# 指定恢复备份
$ docker-compose run --rm gitlab app:rake gitlab:backup:restore BACKUP=1515629493_2020_12_06_13.10.0

image.png 恢复文件需要使用恢复文件的全称(包括 .tar 结尾)

image.png

Docker / 命令与学习笔记

Docker

安装

$ curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

卸载

# Ubuntu | Debian
$ sudo apt-get remove docker docker-engine

# CentOS
$ sudo yum remove docker \
docker-common \
container-selinux \
docker-selinux \
docker-engine

# 卸载Docker后,/var/lib/docker/目录下会保留原Docker的镜像,网络,存储卷等文件. 如果需要全新安装Docker,需要删除/var/lib/docker/目录
$ rm -fr /var/lib/docker/

常用命令

# 查看 Docker 版本信息
$ docker version

# 查看 Docker 简要信息
$ docker -v

# 启动 Docker
$ systemctl start docker

# 关闭 Docker
$ systemctl stop docker

# 设置开机启动
$ systemctl enable docker

# 重启 Docker 服务
$ service docker restart

# 关闭 Docker 服务
$ service docker stop

镜像

docker 仓库地址凭证由 docker login 管理 docker 与各个仓库地址默认以 https:// 来通信,除非仓库地址允许其他方式访问,参考 docker-insecure-registries

# 检索镜像
$ docker search [image-name]

# 拉取镜像
$ docker pull [OPTIONS] NAME[:TAG|@DIGEST]

# 例如 从镜像地址为:10.100.68.85:5000, 拉取镜像文件:testing/test-image
# docker pull [选项] [地址:端口号]仓库名
$ docker pull 10.100.68.85:5000/testing/test-image

镜像管理

# 列出镜像
$ docker images
# or
$ docker image ls

# 删除镜像
$ docker rmi [image-ID]

# 导出镜像
$ docker save

# 导入镜像
$ docker load

# 镜像构建
$ docker build

# 镜像运行
$ docker run [image-ID]

容器

# 启动:启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。
# 新建并启动
$ docker run [image-name/image-ID]
# 启动已终止容器
$ docker start [image-ID]

# 例如: 运行 mysql 容器
# 加-itd参数是让docker启动后能一直运行,如果不加,则在容器启动后执行完就立刻退出了。
# -p 3306:3306: 映射容器服务的 3306 端口到宿主机的 3306 端口,外部主机可以直接通过 宿主机ip:3306 访问到 MySQL 的服务。
# MYSQL_ROOT_PASSWORD=123456: 设置 MySQL 服务 root 用户的密码。
# --restart=always: 添加此参数以确保容器在启动时自动重新启动。
$ docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --restart=always mysql

# 查看容器
# 列出本机运行的容器
$ docker ps
# 列出本机所有的容器(包括停止和运行)
$ docker ps -a

# 停止容器
# 停止运行的容器
$ docker stop [image-ID]
# 杀死容器进程
$ docker kill [image-ID]

# 重启容器
$ docker restart [image-ID]

# 删除容器
$ docker rm [image-ID]

# 强制停止容器 + 删除容器
$ docker rm [image-ID] -f

# 进入容器
# 如果从这个 stdin 中 exit,会导致容器的停止
$ docker attach [image-ID]
# 交互式进入容器
$ docker exec [image-ID]

# docker exec 说明
# docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
# OPTIONS: -d: 分离模式: 在后台运行; -i :即使没有附加也保持STDIN 打开; -t :分配一个伪终端
$ docker exec -it redis-dd /bin/bash

# 导出容器
$ docker export [image-ID]

# 导入容器
$ docker import [image-ID]

# 查看日志
$ docker logs [image-ID]

# docker logs 说明
# docker logs [OPTIONS] CONTAINER
# OPTIONS: -f : 跟踪日志输出; --since :显示某个开始时间的所有日志; -t : 显示时间戳; --tail :仅列出最新N条容器日志
$ docker logs -f nginx-dd

# 查看容器nginx-dd从2021年4月16日后的最新10条日志。
$ docker logs --since="2021-04-16" --tail=10 nginx-dd

# 复制
# 将主机/www/runoob目录拷贝到容器 2baa66d08aae 的/www目录下。
$ docker cp /www/runoob 2baa66d08aae:/www/

# 将主机/www/runoob目录拷贝到容器 2baa66d08aae 中,目录重命名为www。
$ docker cp /www/runoob 2baa66d08aae:/www

# 将容器 2baa66d08aae 的/www目录拷贝到主机的/tmp目录中。
$ docker cp  2baa66d08aae:/www /tmp/

# 设置自动启动(运行中的任务)
$ docker update --restart=always [image-ID]

常用的批量操作

# 启动所有容器
$ docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 关闭所有容器
$ docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 删除所有容器
$ docker rm $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 删除所有镜像(慎用)
$ docker rmi $(docker images | awk '{print $3}' |tail -n +2)

常用命令总结

Docker环境信息   info, version
容器生命周期管理  create, exec, kill, pause, restart, rm, run, start, stop, unpause
镜像仓库命令      login, logout, pull, push, search
镜像管理          build, images, import, load, rmi, save, tag, commit
容器运维操作      attach, export, inspect, port, ps, rename, stats, top, wait, cp, diff, update
容器资源管理      volume, network
系统信息日志      events, history, logs

events打印容器的实时系统事件
history 打印出指定镜像的历史版本信息
logs打印容器中进程的运行日志

Dockerfile

Dockerfile 构建镜像 Dockerfile 是一个文本格式的配 文件,用户可以使用 Dockerfile 来快速创建自定义的镜像。 Dockerfile 由一行行行命令语句组成,并且支持以#开头的注释行。

常见指令

FROM: 指定基础镜像
RUN: 镜像运行脚本
CMD: 容器启动命令
EXPOSE: 设置镜像暴露端口
ENV: 设置环境变量
COPY: 复制文件,可将文件和目录复制到容器
ADD: 	更高级的复制文件,可将文件和目录复制到容器,可以支持本地.tar文件
CMD: 容器启动命令,可以覆盖参数,只能有一个CMD
ENTRYPOINT: 镜像入口程序
ARG: 编译镜像加入的参数
VOLUME: 容器的挂载卷 or 创建目录安装点以访问和存储持久数据
WORKDIR: 用于设置工作目录
USER: 指定当前用户

实例

FROM nginx:1.19-alpine
ARG DOMAIN=''
ENV NGINX_HOST ${DOMAIN}
COPY ./dist /usr/share/nginx/html/
COPY ./middleend.aeonbuy.com.conf.template /etc/nginx/templates/
可运行测试
# 加入当前项目 运行命令
# -t 是给镜像命名 . 是基于当前目录的Dockerfile来构建镜像
$ docker build -t reactnginxcontainer .

# 查看本地镜像
$ docker image ls | grep reactnginxcontainer

# 启动 reactnginxcontainer 镜像容器
# docker run 基于镜像启动一个容器
# -p 3000:80 端口映射,将宿主的3000端口映射到容器的80端口
# -d 后台方式运行
# --name 容器名 查看 docker 进程
$ docker run \
    -p 3000:80 \
    -d --name reactApp \
    reactnginxcontainer

镜像加速,Docker 换源

阿里云镜像加速:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

在 /etc/docker/daemon.json 文件中添加以下参数

# 没有该文件则新建
$ mkdir -p /etc/docker

$ vi /etc/docker/daemon.json

{
  "registry-mirrors": ["https://9cpn8tt6.mirror.aliyuncs.com"]
}

# 加载新的配置文件
$ systemctl daemon-reload

# 重启 docker 服务
$ systemctl restart docker

learning Docker

Docker 官方主页:https://www.docker.com Docker 官方博客:https://blog.docker.com/ Docker 官方文档:https://docs.docker.com/ Docker 镜像下载:https://hub.docker.com/search?q=&type=image Docer从入门到实践:https://yeasy.gitbook.io/docker_practice/ DaoCloud Services 文档:http://guide.daocloud.io/dcs/

Docker / 实战练习

安装MongoDB

安装:https://hub.docker.com/_/mongo

image.png

# 创建数据目录
mkdir mongotest
mkdir mongo

vi docker-compose.yml
version: "3.1"
services:
  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
    ports:
      - 27017:27017
    volumes:
    - /home/mongotest:/data/db

docker-compose up -d

# 查看是否安装成功
docker ps | grep mongotest

# 连接MongoDB
docker exec -it mongotest_mongo_1 mongo

MongoDB设置角色:https://docs.mongodb.com/manual/reference/built-in-roles/ 如下举例:

# 连接MongoDB
docker exec -it mongotest_mongo_1 mongo

use admin

db.auth('root','example')

# 创建test用户
db.createUser({user:'test',pwd:'123456',roles:[{role:'dbOwer',db:'testdb'}]})

image.png

image.png

安装showdoc

参考文档:https://www.showdoc.com.cn/help?page_id=65610

 # 原版官方镜像安装命令
 docker pull star7th/showdoc

 # 创建showdoc数据目录
 mkdir /showdoc_data
 mkdir /showdoc_data/html

 # 修改读写权限
 chmod -R 777 /showdoc_data/

 # 创建showdocw 13500端口下
 # 1.所有设置端口的地方,都需要在服务器上进行端口的放行
 # 2.在自己云服务商,防火墙添加端口进行放行
 docker run -d --name showdoc_test -p 13500:80 -v /showdoc_data/html/:/var/www/html/ star7th/showdoc

 # 查看是否安装成功
 docker ps |grep showdoc_test

 # 吧数据进行转移
 docker exec showdoc_test \cp -fr /showdoc_data/html/ /var/www

 # 修改读写权限
 chmod -R 777 /showdoc_data/

image.png

image.png

访问:http://服务器ip地址:13500/install/index.php 初始默认账号密码:showdoc/123456

image.png

image.png

部署Vue项目

Jenkins / start

安装

Docker

https://www.jenkins.io/doc/book/installing/docker/ 宿主机目录:/var/lib/docker/volumes/jenkins-data/_data

$ docker run -u root --rm -d -p 8080:8080 -p 50000:50000 -v jenkins-data:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkinsci/blueocean

$ docker exec -it boring_margulis /bin/bash

$ cat /var/jenkins_home/secrets/initialAdminPassword

image.png

Ubuntu

https://www.jenkins.io/doc/book/installing/linux/

# Java SDK
sudo apt-get install openjdk-8-jdk

wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/ > \
    /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins

# 验证是否安装成功及相关命令
# 使用 systemctl启动Jenkins
sudo systemctl start jenkins

# 使用其status命令来验证是否成功启动
sudo systemctl status jenkins

# 安装成功会显示以下
wuchendi@WuChendi:~/下载$ sudo systemctl status jenkins
● jenkins.service - LSB: Start Jenkins at boot time
     Loaded: loaded (/etc/init.d/jenkins; generated)
     Active: active (exited) since Mon 2020-10-19 22:00:52 CST; 12h ago
       Docs: man:systemd-sysv-generator(8)
      Tasks: 0 (limit: 18959)
     Memory: 0B
     CGroup: /system.slice/jenkins.service

# 重启Jenkins
sudo systemctl restart jenkins

image.png

配置Jenkins

默认初始本地地址

// ip:8080	(默认为8080端口,可修改)
192.168.xx.xxx:8080

// 重启Jenkins
192.168.xx.xxx:8080/restart
  1. 解锁 Jenkins

image.png

  1. 安装插件

install suggestes plugins(安装建议插件)

image.png

  1. 创建用户

    安装完成后,系统将提示设置一个管理员用户。可以跳过,使用上面使用的初始化密码作为admin登录。

image.png

  1. 配置 URL

image.png

  1. 重启

image.png

  1. 初始化页面

image.png

修改时区

验证的话可以在 [http://ip:808](http://ip:8081)0/systemInfo中查看 user.timezone 因为 Jenkins 容器使用的是 UTC 时间,和我们的北京时间差了正好 8 个小时,针对我们是 Docker 部署的,解决方法如下

# 推荐(已验证)
# 容器修改完时间后需要重新启动容器
mkdir -p /usr/share/zoneinfo/Asia
touch /etc/timezone
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "Asia/Shanghai" > /etc/timezone
docker restart practical_khayyam

# 未验证
# 猜想可以设置时区的方式
docker run -u root --rm -d -p 8080:8080 -p 50000:50000 -v jenkins-data:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock jenkinsci/blueocean -e JAVA_OPTS=-Duser.timezone=Asia/Shanghai

构建参数化

  1. 添加选项参数

image.png

  1. 添加字符参数

image.png

  1. 最终页面显示

image.png

  1. 构建 - 执行shell
#!/bin/bash
DATE=$(date "+%Y%m%d-%H%M%S")

# 备份目录
FileDir="/fe_release_history/$JOB_NAME"
# 创建每次要备份的目录
PATH="$FileDir/$BUILD_NUMBER"
# 回滚目录文件
ROLLBACK_FILEDIR=$FileDir/$VERSION
# 保留备份个数
BUILD_SAVE=5

case $METHOD in
deploy)
	echo "$DATE -- $JOB_NAME项目部署 - $METHOD"

	# 进入备份目录
	cd $FileDir
	# 获取目录个数
	FileNum=$(ls -l | grep '^d' | wc -l)

	while (($FileNum > $BUILD_SAVE)); do
		# 获取排序最小的文件夹
		OldFile=$(ls -rt | head -1)
		echo $DATE "Delete File:"$OldFile
		rm -rf $FileDir/$OldFile
		let "FileNum--"
	done

	if [ -d $PATH ]; then
		echo "The file already exists"
	else
		mkdir -p $PATH
	fi
	# 进入项目目录-执行构建命令
	cd $WORKSPACE
	npm install
	npm run build
	# 将打包好的资源备份到相应目录,覆盖已存在的目标
	cp -rf ${WORKSPACE}/dist/* $PATH
	echo "备份完成"
	echo "Completing!!!"
	;;
rollback)
	if [ -n "$VERSION" ]; then
		echo "$DATE -- $JOB_NAME项目回滚版本: $VERSION"

		if [ -d $ROLLBACK_FILEDIR ]; then
			# 删除当前构建产物
			rm -rf dist*
			if [ -d 'dist' ]; then
				echo "The file already exists"
			else
				mkdir -p dist
			fi
			# 进入回滚版本目录
			cd $ROLLBACK_FILEDIR
			# TODO: 这里需要调整,生产环境的包不需要复制到项目目录下,发到cos上(或资源服务器)
			# 将备份拷贝到程序打包目录中,并覆盖之前的资源
			cp -rf * ${WORKSPACE}/dist/
			echo "Completing!!!"
		else
			echo "找不到备份目录, 请检查输入版本是否存在构建失败任务. tip: 设置保留备份个数最大值:5, 请检查恢复版本是否存在!!!"
			exit 1
		fi
	else
		echo "当前回滚版本为空, 请输入需要回滚的版本号!!!"
		exit 1
	fi
	;;
*)
	exit
	;;
esac
  1. 构建成功会保存5个最新的备份

image.png

Linux / install-node

install node

  • <https://github.com/WuChenDi/Front-End/blob/master/16-shell/install-nodejs.sh>
# 1. 进入 opt 目录
cd /opt/

# 2. 下载 Node.js 压缩包
wget https://npm.taobao.org/mirrors/node/v14.17.2/node-v14.17.2-linux-x64.tar.xz // [!code --]
wget https://nodejs.org/dist/v22.13.0/node-v22.13.0-linux-x64.tar.xz // [!code ++]

# 3. 解压文件(合并了两个解压步骤)
tar -xJf node-v14.17.2-linux-x64.tar.xz // [!code --]
tar -xJf node-v22.13.0-linux-x64.tar.xz // [!code ++]

# 4. 配置环境变量
echo "export NODE_HOME=/opt/node-v14.17.2-linux-x64" >> /etc/profile // [!code --]
echo "export NODE_HOME=/opt/node-v22.13.0-linux-x64" >> /etc/profile // [!code ++]

echo "export PATH=\$NODE_HOME/bin:\$PATH" >> /etc/profile

# 5. 使环境变量生效
source /etc/profile

# 6. 验证安装
node -v
npm -v

image.png

Linux / 常用

镜像下载

阿里云: http://mirrors.aliyun.com/ubuntu-releases/ http://mirrors.aliyun.com/centos/7/isos/x86_64/

清华大学: https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/21.04/ https://mirrors.tuna.tsinghua.edu.cn/centos/8.4.2105/isos/x86_64/ https://mirrors.tuna.tsinghua.edu.cn/deepin-cd/20.2.1/

yum install -y  redhat-lsb lsb_release -a

sudo dpkg -i xxx

打开 hosts 文件 sudo gedit /etc/hosts

ssh root@ip地址

目录操作

  • 创建目录 mkdir <目录名称>
  • 删除目录 rm <目录名称>
  • 定位目录 cd <目录名称>
  • 查看目录文件 ls ll
  • 修改目录名 mv <目录名称> <新目录名称>
  • 拷贝目录 cp <目录名称> <新目录名称>
  • 修改文件权限 chomd 777 test.txt

权限 一般有三种用户角色,文件创建者(u), 文件创建者同组(g), 其它人(o)

  • r读权限,用数字4表示
  • w写权限,用数字2表示
  • x执行权限,用数字1表示

文件操作

  • 创建文件 touch <文件名称> vi <文件名称>

  • 删除文件 rm <文件名称>

  • 修改文件名 mv <文件名称> <新文件名称>

  • 拷贝文件 cp <文件名称> <新文件名称>

  • ls xxx 查看文件夹(平铺展开)

  • ls -a (all的意思,包括以.开头的隐藏文件)

  • ll 查看文件夹(列表)

  • ll xxx (查看xxx文件夹下的文件)

  • mkdir xxx (创建xxx文件夹)

  • rm -rf xxx (把xxx及其所有子文件全部删除)

  • cd xxx (进入xxx目录)

  • mv abc xxx (把abc文件重命名为xxx文件)

  • mv xxx sss路径 (把xxx文件移到sss路径目录)

  • cp a.js a1.js (拷贝、复制a.js为a1.js)

  • rm a1.js (直接删除单个文件)

  • touch d.js (新建文件d.js)

  • vi d.js (新建或进入一个文件d.js并且进入编辑)

  • cat xxx (查看xxx文件所有内容但不进入)

  • head xxx (查看xxx文件前面几行也不进入)

  • grep "babel" xxx (在xxx文件里查找babel内容)

文件内容操作

  • 查看文件 cat <文件名称> head <文件名称> tail <文件名称>
  • 编辑文件内容 vi <文件名称>
  • 查找文件内容 grep '关键字' <文件名称>

「Vim编辑器操作」

  • 按 i 开始编辑
  • Esc 可退出编辑模式
  • :w 保存写入
  • :q 退出
  • :wq (保存后退出)
  • :q! (强制退出,不保存)

防火墙

  1. 查看防火墙状态

systemctl status firewalld.service

image.png

  1. 开启防火墙

systemctl start firewalld.service

  1. 关闭防火墙

systemctl stop firewalld.service

  1. 禁用防火墙

systemctl disable firewalld.service

  1. 防火墙开放端口,比如:
$ firewall-cmd --permanent --zone=public --add-port=80/tcp
# 防火墙中开启80端口,如果有另行配置,就开启你配置的端口
# success

$ firewall-cmd --reload
# 重新加载防火墙配置
# success

$ firewall-cmd --zone=public --query-port=80/tcp
# 查看是否生效
# yes

image.png

Nginx / start

安装 install

http://nginx.org/en/download.html 注: 可能 一些 centos 系统,需要 root 权限执行 nginx 命令

# 安装
apt install nginx

# 查看是否可以正常访问, 顺便验证下安装是否成功
root@iZxjv9e22z6q0aZ:~# curl -I 127.0.0.1
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Fri, 14 Jan 2022 03:17:14 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 14 Jan 2022 03:17:07 GMT
Connection: keep-alive
ETag: "61e0eb33-264"
Accept-Ranges: bytes

# 查看配置文件语法
root@iZxjv9e22z6q0aZ:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@iZxjv9e22z6q0aZ:~#

# 其他命令
nginx 启动
nginx -s reload 重启
nginx -s stop 停止
nginx -c xxx.conf 修改配置文件位置

Windows 不推荐

windows 运行 Nginx 命令需要接入对应的安装目录下,或者配置环境变量 参考:http://tengine.taobao.org/nginx_docs/cn/docs/windows.html#known_issues

PS C:\> cd C:\Users\wudi\Desktop\nginx-1.20.1
PS C:\Users\wudi\Desktop\nginx-1.20.1> start nginx
PS C:\Users\wudi\Desktop\nginx-1.21.5> tasklist /fi "imagename eq nginx.exe"

映像名称                       PID 会话名              会话#       内存使用
========================= ======== ================ =========== ============
nginx.exe                    41436 Console                    1      8,352 K
nginx.exe                    37696 Console                    1      8,392 K
PS C:\Users\wudi\Desktop\nginx-1.21.5>

nginx -s stop	快速退出
nginx -s quit	优雅退出
nginx -s reload	更换配置,启动新的工作进程,优雅的关闭以往的工作进程
nginx -s reopen	重新打开日志文件

Nginx中的gzip的设置参数

# 开启gzip压缩服务
gzip on;

# gzip压缩是要申请临时内存空间的,假设前提是压缩后大小是小于等于压缩前的。
# 例如,如果原始文件大小为10K,那么它超过了8K,所以分配的内存是8 * 2 = 16K;再例如,
# 原始文件大小为18K,很明显16K也是不够的,那么按照 8 * 2 * 2 = 32K的大小申请内存。
# 如果没有设置,默认值是申请跟原始数据相同大小的内存空间去存储gzip压缩结果。

# 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。
# 例如 4 4k 代表以4k为单位,按照原始数据大小以4k为单位的4倍申请内存。
# 4 8k 代表以8k为单位,按照原始数据大小以8k为单位的4倍申请内存。
# 如果没有设置,默认值是申请跟原始数据相同大小的内存空间去存储gzip压缩结果。
gzip_buffers 2 8k;

# nginx对于静态文件的处理模块。
# 该模块可以读取预先压缩的gz文件,这样可以减少每次请求进行gzip压缩的CPU资源消耗。
# 该模块启用后,nginx首先检查是否存在请求静态文件的gz结尾的文件,如果有则直接返回该gz文件内容。
# 为了要兼容不支持gzip的浏览器,启用gzip_static模块就必须同时保留原始静态文件和gz文件。
# 这样的话,在有大量静态文件的情况下,将会大大增加磁盘空间。我们可以利用nginx的反向代理功能实现只保留gz文件。
gzip_static on|off

# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;

# gzip压缩基于的http协议版本,默认就是HTTP 1.1
gzip_http_version 1.1;

# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;

# 需要进行gzip压缩的Content-Type的Header的类型。建议js、text、css、xml、json都要进行压缩;
# 图片就没必要了,gif、jpge文件已经压缩得很好了,就算再压,效果也不好,而且还耗费cpu。
# javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;

# 默认值:off
# Nginx作为反向代理的时候启用,开启或者关闭后端服务器返回的结果,匹配的前提是后端服务器必须要返回包含"Via"的 header头。
# off - 关闭所有的代理结果数据的压缩
# expired - 启用压缩,如果header头中包含 "Expires" 头信息
# no-cache - 启用压缩,如果header头中包含 "Cache-Control:no-cache" 头信息
# no-store - 启用压缩,如果header头中包含 "Cache-Control:no-store" 头信息
# private - 启用压缩,如果header头中包含 "Cache-Control:private" 头信息
# no_last_modified - 启用压缩,如果header头中不包含 "Last-Modified" 头信息
# no_etag - 启用压缩 ,如果header头中不包含 "ETag" 头信息
# auth - 启用压缩 , 如果header头中包含 "Authorization" 头信息
# any - 无条件启用压缩
gzip_proxied [off|expired|no-cache|no-store|private|no_last_modified|no_etag|auth|any] ...

# 是否在http header中添加Vary: Accept-Encoding,建议开启
# 和http头有关系,加个vary头,给代理服务器用的,有的浏览器支持压缩,
# 有的不支持,所以避免浪费不支持的也压缩,所以根据客户端的HTTP头来判断,是否需要压缩
gzip_vary on;

# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";

OM / Vagrant

使用 Vagrant + VirtualBox 在 Windows 上快速创建三台能 ping 通局域网及公网的安装好 docker 的 centos7 虚拟机

下载并且安装软件

https://www.virtualbox.org/wiki/Downloads https://www.vagrantup.com/downloads https://app.vagrantup.com/centos/boxes/7

添加虚拟机

# 下载 box 文件添加到虚拟机中,执行如下命令
$ vagrant box add centos7 CentOS-7-x86_64.box

image.png

新建配置文件

脚本文件:https://github.com/WuChenDi/Front-End/tree/master/15-vagrant/centos7

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.require_version ">= 1.6.0"

boxes = [
    {
        :name => "manager",
        :eth1 => "192.168.31.160",
        :mem => "1024",
        :cpu => "1"
    },
    {
        :name => "worker1",
        :eth1 => "192.168.31.161",
        :mem => "1024",
        :cpu => "1"
    },
    {
        :name => "worker2",
        :eth1 => "192.168.31.162",
        :mem => "1024",
        :cpu => "1"
    }
]

Vagrant.configure(2) do |config|

  config.vm.box = "centos7"
  config.ssh.username = 'root'
  config.ssh.password = 'vagrant'
  config.ssh.insert_key = 'true'

  boxes.each do |opts|
      config.vm.define opts[:name] do |config|
        config.vm.hostname = opts[:name]
        config.vm.provider "vmware_fusion" do |v|
          v.vmx["memsize"] = opts[:mem]
          v.vmx["numvcpus"] = opts[:cpu]
        end

        config.vm.provider "virtualbox" do |v|
          v.customize ["modifyvm", :id, "--memory", opts[:mem]]
          v.customize ["modifyvm", :id, "--cpus", opts[:cpu]]
        end

        config.vm.network :private_network, ip: opts[:eth1]
      end
  end

  config.vm.synced_folder ".", "/home/vagrant"
  config.vm.provision "shell", privileged: true, path: "./setup.sh"

end

setup.sh

#/bin/sh

# install some tools
sudo yum install -y git vim gcc glibc-static telnet psmisc

# install docker
curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh

if [ ! $(getent group docker) ]; then
    sudo groupadd docker
else
    echo "docker user group already exists"
fi

sudo gpasswd -a $USER docker
sudo systemctl start docker

rm -rf get-docker.sh

# open password auth for backup if ssh key doesn't work, bydefault, username=vagrant password=vagrant
sudo sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
sudo systemctl restart sshd

执行 vagrant up 命令,按照配置 Vagrantfile 文件遍历创建虚拟机,并执行 shell 脚本去初始化虚拟机

image.png

Vagrant 常用命令

命令作用
vagrant box add添加box的操作
vagrant init初始化box的操作,会生成vagrant的配置文件Vagrantfile
vagrant up启动本地环境
vagrant ssh通过ssh登录本地环境所在虚拟机
vagrant halt关闭本地环境
vagrant suspend暂停本地环境
vagrant resume恢复本地环境
vagrant reload修改了Vagrantfile后,使之生效(相当于先halt,再up)
vagrant destroy彻底移除本地环境
vagrant box list显示当前已经添加的box列表
vagrant box remove删除相应的box
vagrant package打包命令,可以把当前的运行的虚拟机环境进行打包
vagrant plugin用于安装卸载插件
vagrant status获取当前虚拟机的状态
vagrant global-status显示当前用户Vagrant的所有环境状态

http / HTTP协议

概念
  • http(HyperText Transfer Protocol),超文本传输协议,是互联网上应用最广泛的一种网络协议,以ASCII码传输,建立在TCP/IP协议之上的应用层规范。传输文字、图片、音频、视频等超文本数据的约定和规范(或规则)来进行通信。
  • http也可以说成是一种客户端和服务器端请求和应答的标准(TCP),通过使用客户端或工具发起一个到应答服务器上指定端口(如Tomcat的8080或imx的1099等)的http请求,或者反过来,服务器给客户端发送一个回应,在客户端和应答服务器端在多个中间H如代理网关或者隧道。
特点
  • 灵活可扩展:一个是语法上只规定了基本格式,空格分隔单词,换行分隔字段等。另外一个就是传输形式上不仅可以传输文本,还可以传输图片,视频等任意数据
  • 请求-应答模式:通常而言,就是一方发送消息,另外一方要接受消息(或响应等),如后端通信
  • 可靠传输:HTTP是建立在TCP/IP协议之上的应用层规范
  • 无状态
缺点
  • 无状态:有时需要保存信息,如购物,需要保留下顾客信息等等;另外一方面,无状态也会减少网络开销,比如类似直播行业这样子等,这个还是分场景来说。
  • 明文传输:即协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。这让HTTP的报文信息暴露给了外界,给攻击者带来了便利。
  • 队头阻塞:当http开启长连接时,共用一个TCP连接,当某个请求时间过长时,其他的请求只能处于阻塞状态,这就是队头阻塞问题。
http请求过程

image2.jpg

URI、URL、URN
HTTP三次握手

. 首先需要理解一个概念:    user  http request          ---------------- TCP connection ---------------------->  服务器    在客户端与服务端进行一个http请求的发送和返回的过程当中,需要创建一个叫TCP connection。    因为http不存在“连接”这个概念的。http只有“请求”和“响应”这个概念。    请求和响应都是数据包,它们之间需要经过一个传输的通道,就是TCP connection。这个TCP连接,可以一直保持在那边,    HTTP请求是在TCP连接基础上发送的。    在一个TCP连接上面,是可以发送多个HTTP请求的。    在不同的版本里面,这个模式是不一样的:        在HTTP/1.0里面,一个http请求发送到服务器,服务器接收请求并处理,然后返回响应,之后,这个TCP连接就会关闭。        在HTTP/1.1里面,一个TCP连接可以声明为长连接。使这个TCP连接保持在那边的。意思是第一个请求发送并返回完成之后,        第二个请求还可以在这个tcp连接上进行发送。        这样的好处是:TCP连接在创建的过程中是有三次握手这个消耗的,三次握手就代表有三次网络传输。只有创建了TCP连接,        才能去发送http请求。如果把tcp连接保持在那边,这时候,新的http请求就没有了三次握手的开销。        在HTTP2里面,还有一个好处,tcp连接上的http请求是可以并发的。        同一个用户对同一个服务器发起网络请求的时候,它只需要一个tcp连接。        具体含义会在tcp长连接那节课中讲解。    在这里,只需要明白 tcp连接与http请求的关系。     . 三次握手时序图    client                           server      ----------SYN=1,Seq=X ----------->        服务端接收到创建连接的请求之后,会开启一个端口,返回给客户端,      <------ SYN=1,ACK=X+1,Seq=Y -----        返回的ACK等于客户端的Seq+1,并且会返回一个服务端自己的Seq=Y      ----------ACK=1,Seq=Z ----------->        客户端收到服务端的返回,知道已经允许创建tcp连接了,再去发送ACK等于服务端的Seq+1,                                                 并且发送客户端自己的Seq等于一个新的数字。      SYN是一个标志位,代表它是一个创建请求的数据包。      Seq一般来说初始默认等于1      以上就是tcp连接创建的一个过程。为什么要经过三次握手呢?      .为了防止服务端开启一些无用的服务。规避网络传输延迟而导致的服务器开销问题。        因为我们知道,网络传输是有延时的。...

HTTP报文格式

(a) 请求报文                                                (b) 响应报文 注意:HTTP的首行并不属于HTTP的headers。 它的首行和headers是有所区分的。   首行换行之后,都是HTTP的headers; HTTP的headers与主体之间,有一个完整的空行,来区分。 (a) 在请求报文中, .首行包括:    . HTTP的method,就是请求的方法,---用来定义对于资源的操作        HTTP的method的很多种,有GET,POST,PUT,DELETE,它们        有各自的语义,GET是获取,POST是创建,PUT是修改/更新,DELETE是删除        希望按照语义规范实现web服务。    . 路由    . HTTP协议版本   . headers包括:后面具体讲,这节主要讲HTTP的首行 (b)在响应报文中, . 首行包括:    . HTTP协议版本      . status code ---定义服务器对请求的处理结果        . 各区间的code有各自的语义        . 好的HTTP服务可以通过code判断结果    . status description

HTTP缓存的应用,不仅仅是max-age

HTTP协议中主要涉及缓存的Header就是Cache-Control和Expires(已渐渐被淘汰) Cache-Control并不是只有指定max-age过期时间这么一种使用方式, 事实上这只是Cache-Control最基础的用法,我们来看看Cache-Control有哪些可以设置的值:        1. private、public和no-cache用来指定客户端和代理缓存的可缓存性        2. max-age和s-maxage用来指定客户端和代理缓存的缓存时间        3. must-revalidate和proxy-revalidate用来指定缓存过期后是否必须验证才能使用        4. no-store用来表明任何地方都不应该缓存内容        5. 其他一些不常用的设置

首先大家必须要弄清楚的一点是,缓存不仅仅只有浏览器可以缓存,互联网中存在着各式各样的代理缓存。    HTTP仅仅是一个应用层的协议,在数据传输的过程中逃不开各种中继的设备,而本身HTTP是明文传输的,    所以每个中继设备都可以解析HTTP数据包中的内容,所以如果某个中继设备想,他就可以成为一个代理缓存。    当然HTTP的代理缓存更多还是服务假设者自己做的,但是本质上是一个意思。    代理缓存最明显的优势就是:客户端缓存是一对一的,但是代理缓存是一对多的。    对于同一个源服务器可以存在不同的代理(有些CDN就近获取资源就有用到缓存的知识)。    如果这些代理都开启来缓存功能,那么用户一在第一次访问数据的时候,代理通过源服务器获取资源返回给用户,并同时缓存来这个请求,    这时候用户二再次来请求的时候,就不需要经过源服务器,直接从代理缓存读取就可以来。    所以对于耗时操作而且数据修改不频繁的数据,开启代理缓存对于性能的提升是非常明显的,哪怕你每次缓存的时间只有5秒,对于并发量很高请求带来的性能提升也是不可估量的。    当然这个操作源服务器通过自己设置缓存也可以实现,但是代理缓存的好处是,如果你的代理离用户足够近,那么减少的延时也是非常明显的。    比如如果你的服务器在美国纽约,如果你不在国内设置一个代理缓存,那么所有数据都要跨国半个地球再绕回来,而有代理缓存的情况就不一样来。    那么说回来,怎么控制代理缓存的使用?还是靠HTTP协议,在HTTP协议的发展历史中,已经有非常多的实践让协议进行修改和发展,    所以目前的缓存方案可以说已经比较完善了。一般来说,代理缓存服务器都会对最新的HTTP协议的标准进行实现,并且适当兼容老得标准,    一般不会出现一些魔改实现。所以只要你对HTTP协议的缓存方案充分了解,那么你就可以很好得使用代理服务器的缓存了。    最主要的你需要知道:    如何控制代理服务是否缓存或者缓存多少时间    代理缓存时候可以提供过期缓存的内容    如何进行缓存验证    知道这些之后,配合一个好用的代理缓存,相信能对你的服务带来很大的性能提升。    以上的内容并不是特别好理解,如果大家有兴趣可以去看一下我的新课HTTP协议,里面对HTTP协议的原理,不同HEADER的作用和实践、Nginx部署代理和缓存、HTTPS、HTTP2都有使用例展示进行深入的讲解。    最后,HTTP协议是所有相关开发都必须要牢牢掌握的基础,仅仅知道POST、GET、Content-Type并不算理解HTTP协议,

http / 常见状态码

image1.jpg

private-service / Nexus

由于我是新机器(Debian10.2), 需要安装下 JDK(1.8), 需要下面地址下载

链接:https://pan.baidu.com/s/1uAhva9EXmVZ2sdc4uwx2eA 提取码:ufuu

JDK 安装

# 下载 JDK 上传到服务器

# 进入目录
$ cd /usr/

# 解压
$ tar -zxvf jdk-8u161-linux-x64.tar.gz

# 重命名
$ mv jdk1.8.0_161/ jdk1.8

# 配置环境变量

# 编辑/etc/profile文件并加入配置, 保存退出
$ vi /etc/profile

export JAVA_HOME=/usr/jdk1.8
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

# 重新加载配置
$ source /etc/profile

# 最后可验证 Java 版本
$ java -version

docker 安装 Nexus

遇到最多的问题了,就是没有给予宿主机文件夹权限,重来一次就好 新建文件夹并且给予权限:chmod 777 /home/nexus/nexus-data 安装等待之后可以打开查看:http://ip:8081

# 使用参数 -v 建立宿主机与 Docker 目录映射关系, /nexus-data:docker里存nexus数据目录,所以将数据目录存放到宿主机/home/nexus-data
$ docker run -d --restart=always --net=host -p 8081:8081 -p 5000:5000 --name nexus -v /home/nexus/nexus-data:/nexus-data sonatype/nexus3

# 查看日志
$ docker logs nexus -f

image.png

登录 Nexus

内部有一个初始密码文件 admin.password,这个密码将用来进行第一次登陆的密码,用户名是admin。 密码:cat /home/nexus/nexus-data/admin.password

image.png

登录之后配置Setup

Setup1

image.png

Setup2

重新设置admin密码

image.png

Setup3

image.png

Setup4

image.png

创建仓库

仓库类型

  • hosted(**私有|宿主 **仓库):用于发布个人开发的npm组件
  • proxy(代理仓库):从配置的远程仓库中下载包到该仓库,下次用户访问时会优先检查本仓库,如果有对应的包则使用本仓库中的包,不会再从远程仓库下载(可以代理npm和淘宝镜像)。
  • group(组合仓库):仓库组,把多个仓库聚合起来,这样对用户只需要提供一个地址即可访问组中所有仓库(集合了hosted和proxy)

image.png

image.png

新建仓库

npm(hosted) -> npm-releases

这里的 Allow redeploy 是更新包的意思,其实不建议开启,重复包会覆盖

image.png

npm(proxy) -> npm-proxy

image.png

npm(group) -> npm-public

image.png

创建完成三个仓库

image.png

用户与权限

如果没有什么要求,可以使用admin账号来发版,但是不推荐这样做。 目前我的做法:新建两个角色与用户 账号用途:一个提供获取代码(只读),一个用于发布版本(admin)

权限

image.png

用户

image.png

设置 npm 发布权限

这一步不设置,是不能发包的

image.png

发布私服 npm 测试

用 element-ui 组件库来进行演示

git clone https://github.com/ElemeFE/element.git

登录 Nexus

输入admin

npm login --registry=http://ip:8081/repository/npm-releases/

查看登录用户

npm whoami --registry=http://ip:8081/repository/npm-releases/

发布到 npm 私服

npm publish --registry=http://ip:8081/repository/npm-releases/

image.png

推荐

其实我不推荐吧全部的包都放到 Nexus 上,私有部署的放到就可以, 可以在发布更改下项目name,添加group用来代理, 以elementUI演示

image.png

image.png

项目配置 .npmrc 文件

这里的 _auth 是 base64 加密的:admin:adminadmin(用户名:密码) 单个包的代理试了不成功,所以才需要加 **group**,如果有知道的,可以交流交流

@test:registry=http://ip:8081/repository/npm-group/
_auth=YWRtaW46YWRtaW5hZG1pbg==

swagger api

官方文档: https://help.sonatype.com/repomanager3/integrations/rest-and-integration-api

http://49.233.19.168:8081/swagger-ui/ http://49.233.19.168:8081/service/rest/swagger.json

举个例子,获取 @wshoto/lowcode-cli 最新版本(需要添加cookie才可请求) http://49.233.19.168:8081/service/rest/v1/search?sort=version&name=lowcode-cli&group=wshoto

tips

问题1

image.png

如果出现这个问题,可以往下面方向查询

  1. 可能用户名与密码错误
  2. 如果是注册,可能用户名已存在
  3. 清缓存 npm cache clear --force
  4. 设置权限问题
  5. 有可能与本地配置有关,找到 .npmrc 删除文件不相关的缓存即可

private-service / Verdaccio

https://verdaccio.org/

https://github.com/verdaccio/verdaccio

安装

npm install -g verdaccio

# 启动
$ verdaccio
yarn global add verdaccio

# 启动
$ verdaccio

docker

$ docker pull verdaccio/verdaccio

# 启动
$ docker run --name verdaccio-test -p 4873:4873 -d verdaccio/verdaccio

启动

image.png

用户

# 添加
$ npm adduser --registry http://localhost:4873/

# 登录
$ npm login --registry http://localhost:4873/

image.png

发布

npm publish --registry http://localhost:4873/

image.png

image.png

删除

$ npm unpublish --force react-components --registry http://localhost:4873/

# 删除v1.0.1版本
$ npm unpublish react-components@1.0.1

image.png

以上私服npm完成

Copyright (c) 2023-PRESENT | wudi