文件自动同步服务器

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env node

const shell = require('shelljs')
const chokidar = require('chokidar')
const home = require('user-home')
const path = require('path')

const WORKSPACE_PATH = path.resolve(home, 'Workspace');

const expectPath = path.join(__dirname, './expect.exp')
const to = 'root@144.xxx.xxx.92:/home/backup'

chokidar
.watch('./index.js', {
ignored: (path) => path.includes('node_modules') || path.includes('.git')
})
.on('all', (event, path) => {
console.log(event, path);
shell.exec(`expect ${expectPath} ${path} ${to}`)
});

expect.exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#expect 不能写错
#!/usr/bin/expect

set from [lindex $argv 0]
set to [lindex $argv 1]

set timeout 30

spawn bash -c "scp $from $to"

# 特殊符号需要转移 回车符号不需要
# \ 需转义为 \\\
# } 需转义为 \}
# [ 需转义为 \[
# $ 需转义为 \\\$
# ` 需转义为 \`
# " 需转义为 \\\"
# ( 需转义为 \(


expect {
"*password:" { send -- "j\(S7?xx#ky6\[0n=R\r" }
}

# 必须要加这一句,在执行完毕后把持交互状态,把控制台,这时候就可以进行你想要进行的操作了。如果没有这一句,在登陆完成之后就会退出,而不是留在远程终端上。
interact

性能优化

性能优化开篇

Html 数量控制 -> 压缩合并(30k为标准,请求变少) -> 开启CDN, gzip,brotli压缩方式 -> 服务端开启keep-alive http2 -> 测速 -> 缓存

离线缓存

localStorage 5M 低端机型,不能占用过高

script 放在底部,不会影响dom的解析,但是会影响dom的渲染

1
2
3
4
5
6
<body>
<h1>标题</h1>
<script>
alert(0)
</script>
</body>

同样css不会影响dom的解析,但是影响dom的渲染

1
2
3
4
5
6
7
8
9
10
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css">
</head>

<body>
<h1>标题</h1>
</body>

css 加载会阻塞js的执行,因为不知道js中是否使用了css, 但是不会影响dom的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css">
</head>

<body>
<h1>标题</h1>
<script>
alert(0)
</script>
</body>

css的加载不会影响dom ready,但是如果css,下面有js脚本,则不会执行dom ready,因为不知道是否使用了css样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.addEventlister('DomContentloaded',()=>{console.log('ready')})
</script>
<link rel="stylesheet" href="./css">
</head>

<body>
<h1>标题</h1>
<!-- <script>
alert(0)
</script> -->
</body>

渲染中的性能优化

CLI

命令行工具

基于文本查看,处理,操作计算机上面文件的程序

开发环境

webpack 打包 编译 图片压缩

命令

vue [options]

主命令 + 子命令 + 参数

init generate a new project from a template
list list available official templates
build prototype a new project
create (for v3 warning only)
help [cmd] display help for [cmd]

通过用户的配置拉取远程的模板,来生成模板

vue list 查看模板的种类

Available official templates:
★ browserify - A full-featured Browserify + vueify setup with hot-reload, linting & unit testing.
★ browserify-simple - A simple Browserify + vueify setup for quick prototyping.
★ pwa - PWA template for vue-cli based on the webpack template
★ simple - The simplest possible Vue setup in a single HTML file
★ webpack - A full-featured Webpack + vue-loader setup with hot reload, linting, testing & css extraction.
★ webpack-simple - A simple Webpack + vue-loader setup for quick prototyping.

borwserify 是有另一种打包规范,用于处理使用CMD打包规范,引用的模块

初始化

vue help init 查看初始化命令

Usage: vue-init [project-name]

Options:
-c, –clone use git clone
–offline use cached template
-h, –help output usage information
Examples:

# create a new project with an official template
$ vue init webpack my-project

# create a new project straight from a github template
$ vue init username/repo my-project

流程:

  • vue 输入命令 命令内部初始化

  • 获取用户配置:交互

  • 解析配置 基础配置+用户配置=配置

  • 生成项目文件 通过配置来拉取远程官方模板 + 自己写的模板
    自己写的模板必须包含 :meta.js / json
    prompts=>收集弹框信息
    helpers=>模板引擎的扩展
    complete=>钩子函数
    metalsmith=>站点生成器 通过配置和模板生成文件

    还必须包含模板文件:template

  • 完善工作

  • commander 完整的 node.js 命令行解决方案

  • Inquirer 弹窗交互

  • chalk 命令行美化工具

  • ora 命令行加载动画

  • execa 持续集成 提交到主分支

  • lerna 处理多包相互依赖

目录
  • docs 文档

  • scripts 脚本

  • packages => lerna

  • lib 核心逻辑

  • package.json
    bin 放置用户自定义命令

    为什么没有全局安装的命令可以使用 npm run 来执行,但是不能直接调用?

    如果一个命令想要全局执行,需要添加到全局的环境变量 PATH 中

    在执 npm run 或者 yarn 的时候,会自动在node_modules中查找需要执行的文件,通过npm link 软连接,添加到全局的环境变量PATH中,在执行完成后再删除

    全局安装的命令,会安装在 /usr/local/node12.18.4/lib/node_modules/,执行的时候会自动link,和scripts中写的命令同理

    bin 文件夹下面的命令需要手动link,在入口文件改变时需要重新link

    所以写一个命令的步骤: 1)创建bin文件夹,添加文件 2) 文件头部添加 #!/usr/bin/env node 表示可执行文件 3) link 到全局

vue-cli

vue-list 请求模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env node

const logger = require('../lib/logger')
const request = require('request')
const chalk = require('chalk')

// 监听ctrl + c退出事件
process.on('exit', () => {
console.log()
})

//请求模板使用到的包
// 如果没有报错,拼接包的名字 返回上面的列表
request({
url: 'https://api.github.com/users/vuejs-templates/repos',
headers: {
'User-Agent': 'vue-cli'
}
}, (err, res, body) => {
if (err) logger.fatal(err)
const requestBody = JSON.parse(body)
if (Array.isArray(requestBody)) {
console.log(' Available official templates:')
console.log()
requestBody.forEach(repo => {
console.log(
' ' + chalk.yellow('★') +
' ' + chalk.blue(repo.name) +
' - ' + repo.description)
})
} else {
console.error(requestBody.message)
}
})

vue-init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env node

const download = require('download-git-repo')
const program = require('commander')
const exists = require('fs').existsSync
const path = require('path')
const ora = require('ora')
const home = require('user-home')
const tildify = require('tildify')
const chalk = require('chalk')
const inquirer = require('inquirer')
const rm = require('rimraf').sync
const logger = require('../lib/logger')
const generate = require('../lib/generate')
const checkVersion = require('../lib/check-version')
const warnings = require('../lib/warnings')
const localPath = require('../lib/local-path')

const isLocalPath = localPath.isLocalPath
const getTemplatePath = localPath.getTemplatePath

program
.usage('<template-name> [project-name]')
.option('-c, --clone', 'use git clone')
.option('--offline', 'use cached template')

/**
* Help.
*/

program.on('--help', () => {
console.log(' Examples:')
console.log()
console.log(chalk.gray(' # create a new project with an official template'))
console.log(' $ vue init webpack my-project')
console.log()
console.log(chalk.gray(' # create a new project straight from a github template'))
console.log(' $ vue init username/repo my-project')
console.log()
})

/**
* Help.
*/

function help () {
program.parse(process.argv)
if (program.args.length < 1) return program.help()
}
help()

/**
* Settings.
*/

let template = program.args[0]
const hasSlash = template.indexOf('/') > -1
const rawName = program.args[1]
const inPlace = !rawName || rawName === '.'
const name = inPlace ? path.relative('../', process.cwd()) : rawName
const to = path.resolve(rawName || '.')
const clone = program.clone || false

const tmp = path.join(home, '.vue-templates', template.replace(/[\/:]/g, '-'))
if (program.offline) {
console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`)
template = tmp
}

/**
* Padding.
*/

console.log()
process.on('exit', () => {
console.log()
})

if (inPlace || exists(to)) {
inquirer.prompt([{
type: 'confirm',
message: inPlace
? 'Generate project in current directory?'
: 'Target directory exists. Continue?',
name: 'ok'
}]).then(answers => {
if (answers.ok) {
run()
}
}).catch(logger.fatal)
} else {
run()
}

/**
* Check, download and generate the project.
*/

function run () {
// check if template is local
if (isLocalPath(template)) {
const templatePath = getTemplatePath(template)
if (exists(templatePath)) {
generate(name, templatePath, to, err => {
if (err) logger.fatal(err)
console.log()
logger.success('Generated "%s".', name)
})
} else {
logger.fatal('Local template "%s" not found.', template)
}
} else {
checkVersion(() => {
if (!hasSlash) {
// use official templates
const officialTemplate = 'vuejs-templates/' + template
if (template.indexOf('#') !== -1) {
downloadAndGenerate(officialTemplate)
} else {
if (template.indexOf('-2.0') !== -1) {
warnings.v2SuffixTemplatesDeprecated(template, inPlace ? '' : name)
return
}

// warnings.v2BranchIsNowDefault(template, inPlace ? '' : name)
downloadAndGenerate(officialTemplate)
}
} else {
downloadAndGenerate(template)
}
})
}
}

/**
* Download a generate from a template repo.
*
* @param {String} template
*/

function downloadAndGenerate (template) {
const spinner = ora('downloading template')
spinner.start()
// Remove if local template exists
if (exists(tmp)) rm(tmp)
download(template, tmp, { clone }, err => {
spinner.stop()
if (err) logger.fatal('Failed to download repo ' + template + ': ' + err.message.trim())
generate(name, tmp, to, err => {
if (err) logger.fatal(err)
console.log()
logger.success('Generated "%s".', name)
})
})
}

O(03).数组中重复的数字

LeetCode

注意

暴力解法

使用 map 或 set

1
2
3
4
5
6
7
var findRepeatNumber = function (nums) {
var map = new map();
for (let n of nums) {
if (map[n]) return n;
map[n] = true
}
};

原地替换

  • 题目描述 (长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内 ), 说明每个元素都应该与自己的下标相等,都有自己的位置

  • 通过循环把每个元素放回自己的位置,如果发现被相同的元素占用,表示重复

1
2
3
4
5
6
7
8
9
10
11
var findRepeatNumber = function (nums) {
var index = 0;
while (nums[index] !== undefined) {
var temp = nums[index];
if (temp === index) { index += 1; continue };
if (nums[temp] === temp) return temp;
nums[index] = nums[temp];
nums[temp] = temp;
}
return -1;
};

复杂度分析

  • 时间复杂度:

  • 空间复杂度:

BFF 实践

BFF

BFF,即 Backend For Frontend(服务于前端的后端),也就是服务器设计 API 时会考虑前端的使用,并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。BFF 只是一种逻辑分层,而非一种技术,虽然 BFF 是一个新名词,但它的理念由来已久。

通常一个页面的请求包含了多个不同的请求,用于页面组件的渲染

同时为了保障 Android,iOS,以及 Web 端的不同需求,需要为不同的平台写不同的 API 接口,而每当值发生一些变化时,需要 Android,iOS,Web 做出修改。

有了 BFF 这一层时,我们就不需要考虑系统后端的迁移。后端发生的变化都可以在 BFF 层做一些响应的修改

BFF 场景

多端应用

我们在设计 API 时会考虑到不同设备的需求,也就是为不同的设备提供不同的 API,虽然它们可能是实现相同的功能,但因为不同设备的特殊性,它们对服务端的 API 访问也各有其特点,需要区别处理。

服务聚合

随着微服务的兴起,原本在同一个进程内运行的业务流程被拆分到了不同的服务中。这在增加业务灵活性的同时,也让前端的调用变得更复杂。BFF 的出现为前端应用提供了一个对业务服务调用的聚合点,它屏蔽了复杂的服务调用链,让前端可以聚焦在所需要的数据上,而不用关注底层提供这些数据的服务。

实战中的玩法

访问控制

例如,服务中的权限控制,将所有服务中的权限控制集中在 BFF 层,使下层服务更加纯粹和独立。

应用缓存

项目中时常存在一些需要缓存的临时数据,此时 BFF 作为业务的汇聚点,距离用户请求最近,遂将该缓存操作放在 BFF 层。

第三方入口

在业务中需要与第三交互时,将该交互放在 BFF 层,这样可以只暴露必要信息给第三方,从而便于控制第三方的访问。

初始化项目

项目目录结构划分

package.json 生命周期 并行执行

安装webpack

1
yarn add -D webpack-cli webpack

package.json 文件添加

1
2
3
4
"scripts": {
"test": "echo test",
"pretest": "echo pretest"
},

执行 yarn test

1
2
3
4
5
6
yarn run v1.22.10
$ echo pretest
pretest
$ echo test
test
Done in 0.05s.

并行执行,不能保证顺序

1
2
3
4
5
"scripts": {
"test1": "echo test1",
"test2": "echo test2",
"test": "yarn test1 & yarn test2"
}
1
2
3
4
5
6
7
8
yarn test
yarn run v1.22.10
$ yarn test1 & yarn test2
$ echo test2
$ echo test1
test2
test1
Done in 0.25s.
scripy

使用scripty拆分复杂命令

1
yarn add -D scripty

package.json

1
2
3
4
5
"scripts": {
"test:one": "scripty",
"test:two": "scripty",
"test": "scripty"
}

按照命令建立文件夹

执行 yarn test test文件夹下面的所有命令

package.json 定义公共参数

package.json

1
2
3
"config":{
"port":9999
}

通过变量在命令配置中使用

script/test/one.sh

1
echo $npm_package_config_port

执行 yarn test 打印端口

1
2
3
4
5
6
7
8
9
yarn run v1.22.10
$ scripty
scripty > Executing "/home/supreme/Workspace/mvc/scripts/test/one.sh":

9999
scripty > Executing "/home/supreme/Workspace/mvc/scripts/test/two.sh":

2
Done in 0.13s.
jscpd 代码重复率检查
1
yarn add -D jscpd

添加配置文件.jscpd.json

1
2
3
4
{
"threshold": 0,
"reporters": ["html", "console"]
}

scripts

1
jscpd --min-lines 1 --output ./doc/jscpd --pattern "src/**/*.js"
webpack配置

建立配置文件夹 config,通过内置方法拿到配置参数

1
process.env.NODE_ENV

安装 yargs,以对象的形式更方便的获取命令参数

安装 webpack-merge 合并公共配置和定制配置

art-template

使用art-template作为后端模板,用于服务端渲染html
配置和swig 模板类似

1
yarn add art-template koa-art-template
打包思路

通过webpack打包前端代码

html-webpack-plugin 处理前端模板,放到指定位置

因为模板中插入js代码所以需要通过编写插件处理

后端模板通过 gulp 处理

处理模块化规范,删除无用的代码

webpack.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const glob = require('glob')
const { argv } = require('yargs')
const path = require('path')
const files = glob.sync("./src/web/views/**/*.js")
const htmls = glob.sync("./src/web/views/**/*.art")
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const CustomInjectPlugin = require('./src/service/config/CustomInjectPlugin');
const mode = argv.mode;
if (!files.length) return;

const entry = files.reduce((entry, path) => {
const match = path.match(/(\w+)\/(\w+)\.js$/);
entry[match[1]] = match.input;
return entry;
}, {});

const htmlPlugin = htmls.map(path => {
const match = path.match(/(\w+)\/(\w+)\.art$/);
return new HtmlWebpackPlugin({
filename: `${match[1]}.art`,
template: match.input,
hash: true,
chunks: ['runtime', match[1]],
inject: false
})
})
module.exports = {
entry,
mode,
output: {
filename: '[name]_[contentHash].js',
path: __dirname + '/dist'
}
plugins: [
new CleanWebpackPlugin(),
...htmlPlugin,
new CustomInjectPlugin()
],
optimization: {
runtimeChunk: {
name: 'runtime'
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
}
}
]
},
resolve: {
alias: {
"@": path.resolve(__dirname, 'src/web')
}
}
}

CustomInjectPlugin.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const HtmlWebpackPlugin = require('html-webpack-plugin');
const pluginName = 'CustomInjectPlugin';

class CustomInjectPlugin {
js = ''
apply(compiler) {
compiler.hooks.compilation.tap('pluginName', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(
pluginName,
(data, cb) => {
const { assets: { js } } = data;
this.js = js.map(src => src.replace('@', './')).join('');
cb(null, data)
}
)
// Static Plugin interface |compilation |HOOK NAME | register listener
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
pluginName, // <-- Set a meaningful name here for stacktraces
(data, cb) => {
const { html } = data;
data.html = html.replace(/!script!/, this.js);
this.js = ''
cb(null, data)
}
)
})
}
}

module.exports = CustomInjectPlugin;

gulpfile.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
const { series, src, dest } = require('gulp');
var plugins = require('gulp-load-plugins')();
const replace = require('@rollup/plugin-replace');

const jspath = './src/service/**/*.js';

// 开发环境 监听文件变化, 处理模块化规范
function es6(cb) {
plugins.watch(jspath, { ignoreInitial: false },
function () {
return src(jspath)
.pipe(plugins.babel({
plugins: [
"@babel/plugin-transform-modules-commonjs",
"@babel/plugin-transform-runtime"
]
}))
.pipe(dest('dist'))
}
)
.pipe(dest('build'));
return cb()
}

function es6dev() {
return src(jspath)
.pipe(plugins.babel({
ignore: ['./src/service/config/index.js'],
plugins: [
//处理模块化规范
"@babel/plugin-transform-modules-commonjs",
"@babel/plugin-transform-runtime"
]
}))
.pipe(dest('dist'))
}

function codeClean(cb) {
return src(jspath)
// transform the files here.
.pipe(plugins.rollup({
// any option supported by Rollup can be set here.
input: './src/service/config/index.js',
output: {
format: 'cjs'
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
}))
.pipe(dest('dist'))
}

exports.dev = series(es6);
exports.default = series(es6dev, codeClean);

sonarQube

最后更新

2024-12-11

安装数据库

安装 postgresql[文档]

1
apt install postgresql

创建 schema

1
2
3
4
5
6
7
# 登录数据库
sudo -u postgres psql

# 创建数据库,必须使用 UTF-8 编码
CREATE DATABASE sonarqube_db ENCODING 'UTF8';
# 链接到新的数据库
\c sonarqube_db # 进入数据库

创建用户 sonarqube

1
CREATE USER sonarqube WITH PASSWORD 'YourSecurePassword';

创建 scheme,并赋予 sonarqube 用户所有权限

1
CREATE SCHEMA sonarqube_schema AUTHORIZATION sonarqube;

由于没有使用默认的 schema,必须要设置 search_path

1
ALTER USER sonarqube SET search_path to sonarqube_schema;

配置 java 环境

安装 java 环境,[文档]

安装依赖包

1
apt install -y wget apt-transport-https gpg

下载 Eclipse Adoptium GPG 密钥

1
wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /etc/apt/trusted.gpg.d/adoptium.gpg > /dev/null

配置库信息

1
echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list

安装

1
2
apt update
apt-get install temurin-21-jdk

安装前环境准备

确认信息

一个进程可能拥有的最大内存映射区域数(vm.max_map_count)大于等于 524288。
打开的文件描述符的最大数目(fs.file-max)大于或等于 131072。
运行 SonarQube Server 的用户至少可以打开 131072 个文件描述符
运行 SonarQube Server 的用户至少可以打开 8192 个线程

使用以下命令查看信息

1
2
3
4
5
6
7
sysctl vm.max_map_count

sysctl fs.file-max

ulimit -n

ulimit -u

修改配置

1
2
3
4
5
6
# 创建一个新的配置文件
/etc/sysctl.d/99-sonarqube.conf

# 添加
vm.max_map_count=524288
fs.file-max=131072
1
2
3
4
5
6
7
# 创建一个新的配置文件
/etc/security/limits.d/99-sonarqube.conf

# 添加
sonarqube   -   nofile   262144

sonarqube   -   nproc    16384

在 Linux 内核上启用 seccomp

1
2
3
4
5
6
grep SECCOMP /boot/config-$(uname -r)

# 如果您的内核有seccomp,将看到以下内容
# CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
# CONFIG_SECCOMP_FILTER=y
# CONFIG_SECCOMP=y

安装 sonar

下载,并解压,路径中不能有 .开头的文件夹

1
unzip sonarqube-25.3.0.104237.zip

编辑数据库链接信息

1
2
3
4
5
vi <sonarqubeHome>/conf/sonar.properties

sonar.jdbc.username=sonarqube
sonar.jdbc.password=mypassword
sonar.jdbc.url=jdbc:postgresql://localhost:5432/sonarqube_db?currentSchema=sonarqube_schema

配置 Elasticsearch 存储路径

1
2
3
4
vi <sonarqubeHome>/conf/sonar.properties

sonar.path.data=/var/sonarqube/data
sonar.path.temp=/var/sonarqube/temp

启动服务

1
2
3
4
<sonarqubeHome>/bin/linux-x86-64/sonar.sh start

# http://localhost:9000
# admin/admin

FAQ

检查日志
1
cat <sonarqubeHome>/logs/sonar.log
Startup error: ‘can not run elasticsearch as root’

修改 sonar 安装目录权限

1
2
sudo chown -R sonar:sonar /opt/sonarqube-25.3.0.104237/
sudo chown -R sonar:sonar /var/sonarqube
Process exited with exit value [ElasticSearch]: 143

143 错误 99% 与前端启动相关,检查 web.log

CI CD

代码 -> 构建 -> 集成 -> 测试 -> 发布 -> 部署

|———持续集成——| CI

|———–持续发布————| CD

|—————持续部署—————| CD

CI CD

  • 持续集成(CI)

  • 持续交付和持续部署(CD)

  • 现代软件开发的需求加上部署到不同基础设施的复杂性使得创建应用程序成为一个繁琐的过程。当应用程序出现规模性增长,开发团队人员变得更分散时,快速且不断地生产和发布软件的流程将会变得更加困难。

  • 为了解决这些问题,开发团队开始探索新的策略来使他们的构建、测试和发布流程自动化,以帮助其更快地部署新的生产。这就是持续交付和持续集成发展的由来。

流程

自动构建

在软件开发过程中,构建流程会将开发人员生成的代码转换为可执行的可用软件。

对于Go或者C语言等编译语言,此阶段需要通过编译器运行源代码以生成独立的二进制文件。

对于JavaScript或PHP等解释性语言,没有编译的步骤,但是代码依旧需要在特定的时间内冻结、绑定依赖项、打包以便于分发。这些过程通常称为“构建”或“发布”的工件。

虽然开发人员可以手动构建,但这样操作有诸多不利。首先,从主动开发到创建构建的转变中引入了上下文转换,使得开发人员不得不停止生产效率更高的工作来专注于构建过程。其次,每个开发人员都在制作自己的工件,这可能导致构建过程不一致。

为了解决这些顾虑,许多开发团队配置了自动构建流水线。这些系统监视源代码存储库,并在检测到更改时自动启动预配置的构建过程。这一配置无需牵涉过多的人力在其中并且确保了每个构建过程一致。

进程,线程,协程

程序执行

  • 写在硬盘上的静态程序文件
  • cpu从磁盘上找到程序文件
  • .exe 文件中包含操作cpu的指令
  • 把指令放到内存中
  • 在内存中的指令就可以叫做cpu的进程

进程

对于cpu来讲,有一个时间线的概念,时间线上的每一个点对应着一个操作指令

操作系统把时间线分割为不同的时间片,时间片是执行程序的小单位

按照 1,2,1,2的顺序,来回切换任务的分配,可以让cpu调度进程,看起来在同时执行多个操作

进程比较重,每个任务分配一个进程,频繁切换的时候损耗比较大,所以有了线程的概念

进程之间内存相互独立,进程之间相互通信需要内核转发(系统调用)。也就是进程之间的通信需要经过操作系统,损耗比较大

进程主要占据的内存代码(进程的实现代码),数据(需要处理的数据),文件(和硬盘文件关联的文件句柄,需要通过系统内核,来完成文件的操作)

执行的时候需要写寄存器,在进程切换的时候需要保存寄存器中信息的状态,把寄存器中的数据写到内存中。

栈 函数调用栈

线程

线程比较轻,线程依附与进程,是多个线程对应一个进程的关系,

线程共享进程的内存(代码,数据,文件),但是有自己独立的寄存器和栈,所以线程比较轻

线程共享数据存在的问题,可能读写同时操作,需要线程锁

IO 密集型

输入输出的速度远小于CPU的速度。

多任务 -> 多线程 -> 时间驱动 -> 协程

CPU密集型

多进程 -> 多线程

网络配置问题

可以使用type +命令来查看命令所在目录

查看网络配置基本信息

centenOS 6 之前使用 ifconfg 命令, 之后使用ip 命令, 也可以安装 ifconfig命令包

重启网卡

如果可以直接操作服务其 可以使用 ifup ,ifdown

如果提示系统没有命令可以使用 apt install ifupdown安装

如果是远程不能使用这个两个命令重启网卡,使用ifdown后会断开网络连接

可以在确保网络配置修改成功之后,使用 systemctl restar network

如果命令不可用可以使用 service network-manager restart

如果是 Kali Linux(Debian),则需要service networking restart

如果是Centos 8 需要nmcli c reload

查找占用端口

可以使用 ssnetstat 命令 后面加 -anp

如果没有 netstat 命令,可以通过 apt install net-tools 安装

最后使用 kill 命令杀掉端口

工程化常用命令

ps

查看进程

1
ps aux

kill pkill

1
kill -9 pid

pkill 后面可以直接写进程的名字

如果是一个服务使用 systemctl stop

w who

看谁正在连接系统

lsof 查看端口占用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#列出所有打开的文件:
lsof
备注: 如果不加任何参数,就会打开所有被打开的文件,建议加上一下参数来具体定位

# 查看谁正在使用某个文件
lsof /filepath/file

#递归查看某个目录的文件信息
lsof +D /filepath/filepath2/
备注: 使用了+D,对应目录下的所有子目录和文件都会被列出

# 比使用+D选项,遍历查看某个目录的所有文件信息 的方法
lsof | grep ‘/filepath/filepath2/’

# 列出某个用户打开的文件信息
lsof -u username
备注: -u 选项,u其实是user的缩写

# 列出某个程序所打开的文件信息
lsof -c mysql
备注: -c 选项将会列出所有以mysql开头的程序的文件,其实你也可以写成lsof | grep mysql,但是第一种方法明显比第二种方法要少打几个字符了

# 列出多个程序多打开的文件信息
lsof -c mysql -c apache

# 列出某个用户以及某个程序所打开的文件信息
lsof -u test -c mysql

# 列出除了某个用户外的被打开的文件信息
lsof -u ^root
备注:^这个符号在用户名之前,将会把是root用户打开的进程不让显示

# 通过某个进程号显示该进行打开的文件
lsof -p 1

# 列出多个进程号对应的文件信息
lsof -p 123,456,789

# 列出除了某个进程号,其他进程号所打开的文件信息
lsof -p ^1

# 列出所有的网络连接
lsof -i

# 列出所有tcp 网络连接信息
lsof -i tcp

# 列出所有udp网络连接信息
lsof -i udp

# 列出谁在使用某个端口
lsof -i :3306

# 列出谁在使用某个特定的udp端口
lsof -i udp:55

# 特定的tcp端口
lsof -i tcp:80

# 列出某个用户的所有活跃的网络端口
lsof -a -u test -i

# 列出所有网络文件系统
lsof -N

#域名socket文件
lsof -u

#某个用户组所打开的文件信息
lsof -g 5555

# 根据文件描述列出对应的文件信息
lsof -d description(like 2)

# 根据文件描述范围列出文件信息
lsof -d 2-3

免密登陆

  • 生成密钥对

    -t 指定密钥类型,默认是 rsa ,可以省略。
    -C 设置注释文字,比如邮箱。
    -f 指定密钥文件存储文件名。

    如果想免密登录,密钥不要填写密码,否则必须验证密钥密码才能登录

    1
    2
    3
    4
    ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

    # 或者创建使用 ed25519 加密算法的密钥
    ssh-keygen -t ed25519 -C "your_email@example.com"
  • 上传共钥到服务器对应账号的 home 目录下.ssh 文件夹下面,公钥的权限为 600

    linux 执行以下命令

    1
    ssh-copy-id -i mykey_rsa.pub you_user_name@xxx.xxx.xxx.xxx

    window 需要手动在服务器上创建文件,

    1
    2
    3
    4
    5
    6
    7
    touch  ~/.ssh/authorized_keys

    # 或直接写入文件
    echo "your_public_key_here" >> ~/.ssh/authorized_keys

    # 修改权限
    chmod 600 authorized_keys
  • 指定私钥登陆,私钥的权限为 600

    1
    ssh -i 私钥 user@xxx.xxx.xxx.xxx
  • 通过配置文件免密登陆,在本地服务器把私钥复制到 home 下的.ssh 文件夹下面

    创建名称为 config 的文件,配置单一服务器免密登陆

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    Host tencent
    HostName 124.222.139.87
    User ubuntu
    IdentityFile ./.ssh/id_ed25519
    IdentitiesOnly yes


    # Host 别名
    #  HostName IP
    #  Port 端口
    #  User 用户名
    #  IdentitiesOnly yes
    # IdentityFile ~/.ssh/user_rsa (私钥路径)
    # Protocal 2 (协议版本号)
    # Compression yes
    # ServerAliveInterval 60 (防止被踢配置,长时间没有操作会被踢掉,每隔60秒发一个信号)
    # ServerAliveCountMax 20 (最大连接数)
    # LogLevel INFO

    通配符配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Host app-produce
    HostName 192.168.1.10
    Port 22
    User appuser
    IdentityFile ~/.ssh/id_ed25519

    Host \*\_produce
    User commonuser
    IdentityFile ~/.ssh/id_ed25519
    Port 22

wget

wget 是一个从网络上自动下载文件的自由工具,支持通过 HTTP、HTTPS、FTP 三个最常见的 TCP/IP 协议 下载,并可以使用 HTTP 代理。”wget” 这个名称来源于 “World Wide Web” 与 “get” 的结合。

wget 可以在用户退出系统的之后在后台执行。这意味这你可以登录系统,启动一个 wget 下载任务,然后退出系统,wget 将在后台执行直到任务完成,相对于其它大部分浏览器在下载大量数据时需要用户一直的参与.

1
wget (选项) (参数)

其中选项如下:

-a<日志文件>:在指定的日志文件中记录资料的执行过程;

-A<后缀名>:指定要下载文件的后缀名,多个后缀名之间使用逗号进行分隔;

-b:进行后台的方式运行 wget;

-B<连接地址>:设置参考的连接地址的基地地址;

-c:继续执行上次终端的任务;

-C<标志>:设置服务器数据块功能标志 on 为激活,off 为关闭,默认值为 on;

-d:调试模式运行指令;

-D<域名列表>:设置顺着的域名列表,域名之间用“,”分隔;

-e<指令>:作为文件“.wgetrc”中的一部分执行指定的指令;

-h:显示指令帮助信息;

-i<文件>:从指定文件获取要下载的 URL 地址;

-l<目录列表>:设置顺着的目录列表,多个目录用“,”分隔;

-L:仅顺着关联的连接;

-r:递归下载方式;

-nc:文件存在时,下载文件不覆盖原有文件;

-nv:下载时只显示更新和出错信息,不显示指令的详细执行过程;

-q:不显示指令执行过程;

-nh:不查询主机名称;

-v:显示详细执行过程;

-V:显示版本信息;

–passive-ftp:使用被动模式 PASV 连接 FTP 服务器;

–follow-ftp:从 HTML 文件中下载 FTP 连接文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
wget http://test.com/testfile.zip ->下载指定文件到当前文件夹
wget -O wordpress.zip http://test.com/download ->指定保存名字
wget --limit-rate=300k http://www.linuxde.net/testfile.zip ->限制下载速度
wget -c http://www.linuxde.net/testfile.zip ->断点续传
wget -b http://www.linuxde.net/testfile.zip ->后台下载

# 设置使用指定浏览器下载(伪装下载)
wget --user-agent="Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16" http://www.linuxde.net/testfile.zip

wget --spider url ->测试下载
wget --tries=40 URL ->设置重试次数为40
wget -i filelist.txt ->从filelist.txt获取下载地址

# 镜像网站
# --miror开户镜像下载。
# -p下载所有为了html页面显示正常的文件。
# --convert-links下载后,转换成本地的链接。
# -P ./LOCAL保存所有文件和目录到本地指定目录
wget --mirror -p --convert-links -P ./LOCAL URL

wget --reject=gif ur ->下载一个网站,但你不希望下载图片,可以使用这条命令
wget -o download.log URL ->把下载信息存入日志文件
wget -Q5m -i filelist.txt ->限制总下载文件大小
wget -r -A.pdf url ->下载指定格式文件

# FTP下载
wget ftp-url
wget --ftp-user=USERNAME --ftp-password=PASSWORD url
  • Copyrights © 2015-2025 SunZhiqi

此时无声胜有声!

支付宝
微信