技术文章

Git 合并代码的不同方式 – Merge Commit、Squash and merge、Cherry-pick、Rebase and merge

前言

我们在日常开发中经常使用 Git 管理代码, 每个人在各自的分支开发代码, 开发完毕后在 Gitlab/Github 上提交 MR/PR, 最后点击 merge 按钮即将代码合并至主分支…

在稍微学习 Git 相关的知识后, 我们会发现 Git 代码合并的方法绝不仅此一种, 不同的代码合并方式之间有什么差异?各自又适用于什么样的场景?我将在这篇文章为大家展开聊聊这些话题。

不同的代码合并方式

1. Merge

在常见的 Git 工作流中, 我们会有 2 个长期存在并且不会被删除的分支: master 和 develop。而在日常开发流程中, 我们使用的是特性分支,也叫功能分支。当需要开发一个新的功能的时候,可以新建一个 feature-xxx 的分支,在里边开发新功能,开发完成后,将之并入 develop 分支中,如下图:

                        H---I---J feature-xxx
                        /       \
                E---F---G---K----L develop
                /
    A---B---C---D master

其中 L 这个提交是由 Git 自动生成的合并提交节点。

注意, 如果 git 可以通过移动指针完成合并, 那么默认情况下将不会创建提交节点, 这个优化又被称之为 fast-forward(ff) , 如需关闭该优化项, 可添加参数 --no-ff 要求 git 创建提交节点。

2. Squash Merge

在日常的 MR/PR 过程中, 我们会发现合并时有个选项叫 squash commits 。 顾名思义, Squash 意味着会将多个 commit(提交) 合并到一个。与 Merge 类似的是, 使用 Squash Merge 将会在该分支末尾追加一个提交记录, 如下拓扑结构:

                        H---I---J feature-xxx
                        / 
                E---F---G---K----L' develop (where L' == (H + I +J)
                /
    A---B---C---D master

但是, 与普通的 Merge 不同的是, Squash Merge 会丢弃原来分支 (feature-xxx) 上的所有提交记录, 并生成一个包含原来提交的所有内容的提交节点。
基于以上特性, 如果 Squash Merge 后继续在 feature-xxx 分支开发, 那么下次合并后将大概率出现冲突,这时候就需要用到 cherry-pick 。

3. Cherry-pick

根据 git-book 中的介绍, cherry-pick 提供了从另一分支中 挑选(pick) 单个或数个提交并应用到当前的开发分支中的能力。 我们以 Squash Merge 后意外地在原分支中继续开发为例, 介绍 cherry-pick 的操作流程, 如下拓扑结构:

                        H---I---J---M---N feature-xxx
                        /           `    `
                E---F---G---K----L'---M'---N' develop (where M', N' is chery pick from M, N)
                /
    A---B---C---D master

除了修复 Sqaush Merge 引来的意外冲突以外, cherry-pick 还常用于从不稳定的开发分支(不具备合并到主分支的条件)挑选个别需要紧急发布的安全修复到稳定分支中, 这种场景合并没有意义, 因为合并反而会引入更多不需要的变更。

4. Rebase

最后一种常用的, 也是最强大(复杂)的合并方式是 Rebase。顾名思义, Rebase(变基) 即变更当前分支的根节点, 我们以如下拓扑结构为例介绍 Rebase 的流程:

        E---F---G feature-xxx
        /
    A---B---C---D develop

当我们开发的基础分支已经落后于原分支时, 我们在提交代码前就应该使用 rebase :

➜ git rebase develop feature-xxx

执行以上操作后, 拓扑结构将调整为如下所示:

                E'---F'---G' feature-xxx
                /
    A---B---C---D develop

其中, E’, F’, G’ 与原来的 E, F, G 内容完全一致, 本质上是在另一个根节点后重新应用原来的提交。

值得注意的是, rebase 后的分支是必然符合 fast-forward 的优化条件的, 这意味着 rebase merge 可以不创建无意义的合并节点, 有利于保持代码分支的可读性。

交互式 Rebase

Rebase 本质上是在另一个根节点上 重放 你的代码提交记录, 因此 rebase 不仅仅具备变更根节点的能力, 还能压缩代码提交记录(squash), 修改代码提交信息(edit) 甚至可删除部分提交(drop)。我们可以通过启动一个交互式的 Rebase 会话来做到上述功能:

➜ git rebase -i HEAD~2

执行上述指令后, Git 将打开一个编辑器, 依据指引操作即可:

pick 6b2e82f 2
pick a95710b 4

## 变基 7244a00..a95710b 到 7244a00(2 个提交)
#
## 命令:
## p, pick <提交> = 使用提交
## r, reword <提交> = 使用提交,但修改提交说明
## e, edit <提交> = 使用提交,进入 shell 以便进行提交修补
## s, squash <提交> = 使用提交,但融合到前一个提交
## f, fixup <提交> = 类似于 "squash",但丢弃提交说明日志
## x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
## b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
## d, drop <提交> = 删除提交
## l, label <label> = 为当前 HEAD 打上标记
## t, reset <label> = 重置 HEAD 到该标记
## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
## .       创建一个合并提交,并使用原始的合并提交说明(如果没有指定
## .       原始提交,使用注释部分的 oneline 作为提交说明)。使用
## .       -c <提交> 可以编辑提交说明。
#
## 可以对这些行重新排序,将从上至下执行。
#
## 如果您在这里删除一行,对应的提交将会丢失。
#
## 然而,如果您删除全部内容,变基操作将会终止。
#
## 注意空提交已被注释掉

如何区分不同的合并方式?

一般情况下, 我们选择不同的合并方式应该基于同一个准则: 维护一份干净且可用的代码提交历史。为此, 我们需要区分不同的场景使用以上不同的合并方式。

1. Merge

毋庸置疑, 合并是最通用的代码合并方式。当你需要将来自一个分支的整个功能完全合并到另一个分支时, 使用 merge 可以将代码提交历史完整地保存下来, 为代码溯源(git blame)提供最有价值的技术指导。

以 Git 工作流为例, 当需要发布 develop 至稳定的环境时, 就应当将 develop 分支 merge 到 master 分支。

2. Squash Merge

如前所述, Squash Merge 会将代码提交记录压缩合并为 1个, 并且操作不当容易引发代码冲突。不过仍然有些情况是建议将提交记录进行压缩的:

以功能开发为例, 当我们开发一个功能分支时, 可能会产生很多意义不大的提交记录(例如可能 commit 后才发现有 typo, 于是又多了个修复 typo 的 commit)。

一般情况下, 是否使用 Squash Merge 是一个团队偏好问题:

  1. 如果你觉得意义不大的提交记录污染了主分支的代码历史, 那么你将代码合并到主分支前就应当合并你的代码提交历史, 而 Squash Merge 则是其中一种合并提交记录的方式。
  2. 如果你觉得所有提交都应该被追踪(例如某些团队以提交记录作为工作凭证?), 那么你的所有提交就不应该被任何人”篡改”!

一些团队可能认为使用 Squash Merge 有助于保持主分支的整洁, 但是并不能说这就是绝对正确的事情,所以这主要还是一个偏好问题。

而且, 为什么不使用 rebase 调整代码记录后再进行代码合并呢!

3. Cherry-pick

Cherry-pick 用于从某个分支挑选个别提交记录合并至指定分支, 因此 cherry-pick 常用的场景即是从开发分支中 挑选(pick) 安全修复至稳定分支(如, master)。除此之外, 在日常开发中如需从其他开发分支中摘取部分代码时, 亦可使用 cherry-pick 。

4. Rebase

Rebase 是 Git 常用命令中最强大的命令之一, 使用场景亦是最广泛的, 包括:

  1. 当你的开发分支是基于过时的分支时:

这是团队开发中最为常见的场景: 当其他人将代码合并至远程的 develop 分支后, 你的开发分支将落后于 develop 分支。

为了保证开发的功能不被其他人破坏, 本地测试时应当保证本地代码是最新的。在这种情况下, 我们可以将 develop 分支逆向合并至本地开发分支, 但是这会产生不必要的代码提交记录。使用 Rebase 即可更优雅地解决这个 “噪音” 问题。

  1. 当你希望不产生额外的代码合并记录时:

正如前言, rebase 后的分支是必然符合 fast-forward 的优化条件的, 这意味着 rebase merge 可以不创建无意义的合并节点, 有利于保持代码分支的可读性。

  1. 当你需要清理或调整某些提交记录时:

这种情况在现实开发中也是经常发生的: 例如当你在代码提交后意外发现代码中(或者 commit message 中)存在错别字, 但是这份代码又并未合并到主分支时。

我们期望维护一份干净而可用的代码提交历史,不希望某些意义不大或存在歧义的提交记录污染主分支的代码提交历史, 此时我们就应该使用可交互式的 Rebase 压缩或调整代码提交记录。

总结

Git 提供了多种合并代码的方式, 日常开发使用普通的 Merge 即可。如非团队开发约定, 尽量少用 Squash Merge 。如需压缩代码提交记录, 可于本地使用 Rebase 调整代码提交历史后, 再合并至主分支。而对于安全修复等紧急发布, 可使用 cherry-pick 摘取提交记录合并至主分支。

node部署代理服务器全流程

启动node代理服务器:

  • 安装nvm: 如何安装nvm
  • 使用nvm安装node服务,要求版本14以上
  • 安装node中间件:npm install express http-proxy-middleware
  • node代码,所有访问localhost/api/?的请求都会被转发到https://final_host_site.com/?:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

const targetUrl = 'https://final_host_site.com';

// 代理配置
const options = {
  target: targetUrl, // 目标主机
  changeOrigin: true, // 需要虚拟托管站点
  pathRewrite: {
    '^/api': '',
  },
  onProxyReq: (proxyReq, req, res) => {
    console.log(`[Proxy] ${req.method} ${req.path}`);
  },
};

// 使用中间件
app.use('/api', createProxyMiddleware(options));

// 监听端口
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

使用pm2命令管理程序,保证出问题时可以自动重启

npm install pm2 -g

pm2 start server.js --name myapp

pm2 startup

pm2 save

安装lnmp服务:

wget https://soft.lnmp.com/lnmp/lnmp2.0.tar.gz -O lnmp2.0.tar.gz && tar zxf lnmp2.0.tar.gz && cd lnmp2.0 && ./install.sh lnmp
配置nginx:
server
    {
        listen 80;
        #listen [::]:80;
        server_name www.myhost.com;
            location / {
                    proxy_pass http://localhost:3000;
            }

    }

server
    {
        listen 443 ssl http2;
        #listen [::]:443 ssl http2;
        server_name wishapi.vvip.tech ;

        ssl_certificate /usr/local/nginx/conf/ssl/www.myhost.com/fullchain.cer;
        ssl_certificate_key /usr/local/nginx/conf/ssl/www.myhost.com/www.myhost.com.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
        ssl_session_cache builtin:1000 shared:SSL:10m;
        # openssl dhparam -out /usr/local/nginx/conf/ssl/dhparam.pem 2048
        ssl_dhparam /usr/local/nginx/conf/ssl/dhparam.pem;
            location / {
                    proxy_pass http://localhost:3000;
            }
                access_log  /home/wwwlogs/www.myhost.com.logs;

    }

阿里云(国内)安装nvm

​nvm正常安装步骤:https://github.com/nvm-sh/nvm

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

直接使用nvm脚本安装的时候一直失败,应该是国内网络的问题,使用下面方案进行解决:

下载

cd /
wget https://github.com/nvm-sh/nvm/archive/refs/tags/v0.39.1.tar.gz
mkdir -p /.nvm
tar -zxvf v0.39.1.tar.gz -C /.nvm

配置

vim ~/.bashrc

复制

在文件末尾添加(注意修改nvm路径中的版本号)

export NVM_DIR="/.nvm/nvm-0.39.1"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

让配置文件生效

source ~/.bashrc

设置镜像

直接安装node和npm可能会安装失败,需要修改镜像

nvm node_mirror https://npm.taobao.org/mirrors/node/
nvm npm_mirror https://npm.taobao.org/mirrors/npm/

常见问题

  • 如果不是root用户,没有权限
sudo chmod 777 /.nvm

nvm基本使用

# 查看已安装的版本
nvm ls
# 安装指定版本的node
nvm install 16
# 切换node版本
nvm use 16

初始化一个GCP项目并用gcloud访问操作

1 简介

谷歌云GCP(Google Cloud Platform)是由Google提供的云平台,还是为用户提供了许多免费的产品,还是可以尝试一下的。对于学习或者小项目,都可以使用。

2 创建一个新项目

要使用GCP,我们需要创建一个项目,它所有的资源都是在项目之下管理的:

3 创建Service Account

在实际开发中,我们不能使用自己的账号在做操作,最好的方式是创建一个服务账号(Service Account),这应该也是所有云平台都推荐的方式。创建位置如下:

输入账号名字:

选择角色,为了方便,我直接选择Owner,会拥有所有权限,但实际应用肯定不能这样,要做好隔离:

4 创建密钥文件

对于Service Account,不是通过用户名密码来授权的,而是通过密钥文件,创建如下:

选择新建一个密钥,并格式为json。创建后,会自动下载key文件。

5 设置gcloud SDK

Key文件拿到后,我们可以设置环境变量:GOOGLE_APPLICATION_CREDENTIALS

$ export GOOGLE_APPLICATION_CREDENTIALS=/Users/larry/Software/google-cloud-sdk/pkslow-admin-for-all.json

BashCopy

激活Service Account:

$ gcloud auth activate-service-account admin-for-all@pkslow.iam.gserviceaccount.com --key-file=${GOOGLE_APPLICATION_CREDENTIALS}

BashCopy

设置SDK的项目ID:

$ gcloud config set project pkslow

BashCopy

检查一下设置是否正确:

$ gcloud auth list
               Credentialed Accounts
ACTIVE  ACCOUNT
*       admin-for-all@pkslow.iam.gserviceaccount.com

To set the active account, run:
    $ gcloud config set account `ACCOUNT`


$ gcloud config list
[core]
account = admin-for-all@pkslow.iam.gserviceaccount.com
disable_usage_reporting = True
project = pkslow

Your active configuration is: [default]

BashCopy

6 使用gcloud创建Pub/Sub

SDK设置好后,就可以使用了,我们使用它来创建Pub/Sub试试。创建主题和订阅:

$ gcloud pubsub topics create pkslow-test
Created topic [projects/pkslow/topics/pkslow-test].

$ gcloud pubsub subscriptions create pkslow-sub --topic=pkslow-test
Created subscription [projects/pkslow/subscriptions/pkslow-sub].

BashCopy

检查是否创建成功:

$ gcloud pubsub topics list
---
name: projects/pkslow/topics/pkslow-test


$ gcloud pubsub subscriptions list
---
ackDeadlineSeconds: 10
expirationPolicy:
  ttl: 2678400s
messageRetentionDuration: 604800s
name: projects/pkslow/subscriptions/pkslow-sub
pushConfig: {}
topic: projects/pkslow/topics/pkslow-test

BashCopy

在浏览器查看,发现已经成功创建了:

fastAdmin后台功能详解

FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。优点见开发文档 介绍 – FastAdmin框架文档 – FastAdmin开发文档

在这里上传几张优秀的快速入门图:

 一张图解析FastAdmin中的表格列表的功能:(仅仅为了查找方便,如有侵权联系删除)

功能描述

请根据图片上的数字索引查看对应功能说明。

1.菜单名称和描述
默认生成的CRUD是没有菜单名称和描述显示的,如果需要显示则可以修改权限管理->菜单规则,给对应菜单的添加上备注信息后即可显示,支持HTML

2.TAB过滤选项卡
在一键生成CRUD时,如果表中存在status字段且为ENUM类型,则会生成相应的TAB过滤选项卡,如果需要生成其它字段的过滤选项卡则可以在使用php think crud时使用--headingfilterfield=你的字段名称来指定字段

3.通用搜索
通用搜索的的内容是根据bootstrap-table配置的字段columns决定的,渲染的内容及格式由FastAdmin自动进行渲染,如果需要禁用或删除某一选项,可以在JS中配置operate:false来删除通用搜索中的选项。例如通常情况下我们的在JS中进行字段的配置如下:

{field: 'createtime', title: __('Create Time')},

这里默认是启用的通用搜索,针对通用搜索,有以下几个常用的配置:

operate:'=' //用于查询时的操作符,默认为=,为false表示禁用此字段的通用搜索,支持!=、LIKE、NOT LIKE、>、<、>=<=、FIND_IN_SET、IN、NOT IN、BETWEEN、NOT BETWEEN、RANGE、NOT RANGE、NULL、NOT NULL、false
searchList: //用于渲染列表的数据,支持的格式有JSON Array、JSON Object、$.getJSON、Function
addclass: //用于给input或select添加额外的class属性
type: //用于定义input文本框的类型,默认为text
data: //用于给input或select添加额外的属性

常用配置示例如下:

//时间区间搜索
{field: 'createtime', title: __('Createtime'), operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime},
//金额区间搜索
{field: 'money', title: __('Money'), operate: 'RANGE'},
//下拉列表搜索
{field: 'flag', title: __('Flag'), searchList: {"hot": __('Flag hot'), "index": __('Flag index'), "recommend": __('Flag recommend')}, operate: 'FIND_IN_SET', formatter: Table.api.formatter.label},
//动态下拉列表搜索
{field: 'type', title: __('Type'), searchList: $.getJSON("ajax/dynamicselect")},
//禁用通用搜索
{field: 'keywords', title: __('Keywords'), operate: false},

如果我们需要完全自定义我们的通用搜索栏,我们可以在配置bootstrap-table时定义searchFormTemplate选项来完全重写我们的通用搜索栏,具体请参考开发示例插件中的自定义搜索示例

4.工具栏按钮
FastAdmin在一键CRUD时会自动生成添加、编辑、删除、导入、更多按钮的HTML,这些按钮会根据用户所拥有的权限控制基是否显示或隐藏。我们可以在控制器对应的index.html视图文件中任意修改或删除对应的按钮。请特别注意这几个自动生成的按钮都通过基拥有的class属性来绑定相关的事件,例如添加按钮拥有btn-add这个class、框架所已经占用的class如下:

btn-add: 添加按钮使用
btn-edit: 编辑按钮使用
btn-del: 删除按钮使用
btn-import: 导入按钮使用
btn-more: 更多按钮使用
btn-multi: 指操作使用
btn-disabled: 添加此class后则只有在列表有选中数据时按钮才会变为可使用

如果我们想点击添加按钮后默认全屏,则可以给添加按钮加上data-area='["100%","100%"]'即可默认全屏
如果我们想自定义按钮并添加事件,我们需要在视图中添加相应的HTML代码,然后在对应的JS文件中添加按钮的执行事件,切记不可在视图中直接编写JS或jQuery代码来绑定事件

5.动态渲染统计信息
很多时候我们需要在页面额外显示服务端传回的动态数据,此时我们只需要在index.html视图中添加

<a href="javascript:;" class="btn btn-default" style="font-size:14px;color:dodgerblue;">
    <i class="fa fa-dollar"></i>
    <span class="extend">
        金额:<span id="money">0</span>
        单价:<span id="price">0</span>
    </span>
</a>

然后在控制器对应的JS中的index方法中添加以下的JS

//当表格数据加载完成时
table.on('load-success.bs.table', function (e, data) {
    //这里可以获取从服务端获取的JSON数据
    console.log(data);
    //这里我们手动设置底部的值
    $("#money").text(data.extend.money);
    $("#price").text(data.extend.price);
});

注意务必将这段代码添加在var table = $("#table");之后
其中data.extend.moneydata.extend.price就是我们在服务端动态返回的数据,如下

$result = array("total" => $total, "rows" => $list, "extend" => ['money' => 1024, 'price' => 888]);
return json($result);

通过以上配置即可动态显示服务端返回的额外数据

6.快速搜索
快速搜索在键入关键词时将实时从服务端搜索数据,如果你的数据表数据较大,建议关闭此功能,关闭的方法是使用search:false,其次快速搜索默认只会搜索主键id这个字段,如果你需要搜索其它字段,则需要在服务端你的控制器中定义$searchFields这个值,如下

protected $searchFields = 'id,name,title';

这样在快速搜索时将会搜索id,name,title这三个字段。
如果需要修改默认文本框的placeholder,可以在表格初始化前定义

$.fn.bootstrapTable.locales[Table.defaults.locale]['formatSearch'] = function(){return "自定义placeholder文本";};

7.浏览模式、显示隐藏列、导出、通用搜索
浏览模式可以切换卡片视图和表格视图两种模式,如果不需要此功能,可以设置showToggle: false
显示隐藏列可以快速切换字段列的显示和隐藏,如果不需要此功能,可以设置showColumns: false,如果想要表格中的字段列默认隐藏可以设置字段属性visible: false即可默认隐藏
导出按钮默认将导出整个表的所有行,如果需要仅导出当前分页的数据,需要设置exportDataType: 'basic',如果想导出选中的行,则可以设置为exportDataType: 'selected',如果不需要此功能,可以设置showExport: false
通用搜索指表格上方的搜索,通用搜索的表单默认是隐藏的,如果需要默认显示,需要设置searchFormVisible: true,如果不需要通用搜索功能,可以设置commonSearch: false。如果想要控制字段列不参考搜索则可以设置字段列属性为operate: false即可。

8.字段配置
默认字段的控制是根据控制器对应的JS来配置的,因此字段配置是通过JS,而在我们的视图index.html中是没有任何字段配置的,通常我们的配置如下:

columns: [
    [
        {checkbox: true},
        {field: 'id', title: __('Id')},
        {field: 'admin_id', title: __('Admin_id')},
        {field: 'category.name', title: __('分类名称'), formatter:Table.api.formatter.search},
        {field: 'category_id', title: __('Category_id'), visible: false},
        {field: 'flag', title: __('Flag'), searchList: {"hot": __('Flag hot'), "index": __('Flag index'), "recommend": __('Flag recommend')}, operate: 'FIND_IN_SET', formatter: Table.api.formatter.label},
        {field: 'genderdata', title: __('Genderdata'), searchList: {"male": __('Genderdata male'), "female": __('Genderdata female')}, formatter: Table.api.formatter.normal},
        {field: 'title', title: __('Title')},
        {field: 'image', title: __('Image'), formatter: Table.api.formatter.image},
        {field: 'images', title: __('Images'), formatter: Table.api.formatter.images},
        {field: 'createtime', title: __('Createtime'), operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime},
        {field: 'updatetime', title: __('Updatetime'), operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime, visible: false},
        {field: 'weigh', title: __('Weigh'), operate: false, visible: false},
        {field: 'switch', title: __('Switch'), searchList: {"1": __('Yes'), "0": __('No')}, formatter: Table.api.formatter.toggle},
        {field: 'status', title: __('Status'), searchList: {"normal": __('Normal'), "hidden": __('Hidden')}, formatter: Table.api.formatter.status},
    ]
]

字段配置的参数有

checkbox:true, //是否为首列复选框
field:'name' //字段名称,如果启用了关联查询这里可以使用别名,比如:category.name,请注意服务端返回的字段一一对应,如果使用了一个不存在的字段,将不会渲染任何数据
title:'名称' //字段标题,显示于头部的标题
operate:'=' //通用搜索的操作符,详见上方通用搜索介绍
visible:false //字段是否可见,为false时将默认不可见
formatter:Table.api.formatter.search //格式化显示的内容,FastAdmin内部定义了许多通用的格式化方法
events: //定义元素响应的事件
searchList: //定义通用搜索下拉列表的数据
addclass: //通用搜索文本框或下拉列表的额外class
type: //通用搜索文本框的类型
data: //通用搜索文本框或下拉列表的额外属性
buttons: //配置的按钮组

FastAdmin封装了许多常用的formatter方法,我们可以快速的调用即可。

> `Table.api.formatter.icon` 快速将字段渲染成一个按钮,仅支持Fontawesome按钮
> `Table.api.formatter.image` 快速将字段渲染成图片展示的形式
> `Table.api.formatter.images` 快速将字段渲染成多图片展示的形式,字段数据请以`,`进行分隔
> `Table.api.formatter.status` 快速将字段渲染成状态,默认`normal/hidden/deleted/locked`这四个状态
> `Table.api.formatter.url` 快速将字段渲染成URL框
> `Table.api.formatter.search` 快速将字段渲染成可搜索的链接,点击后将执行搜索
> `Table.api.formatter.addtabs` 快速将字段渲染成可添加到选项卡的链接,点击后将把链接添加到选项卡
> `Table.api.formatter.flag` 快速将字段渲染成标志,仅支持`index/hot/recommend/new`这四种标志
> `Table.api.formatter.label` 快速将字段渲染Label标签
> `Table.api.formatter.datetime` 快速时间戳数据渲染成日期时间数据
> `Table.api.formatter.operate` 操作栏固定按钮
> `Table.api.formatter.buttons` 快速生成多个按钮
> `Table.api.formatter.toggle` 快速生成切换按钮

9.复选框
如果我们需要不需要复选框则移除{checkbox: true}即可

10.分类名称(关联搜索出分类表的名称)
这里显示的分类名称是根据分类表关联查询出来的结果,如果我们启用关联查询,我们必须在当前控制器中设置属性protected $relationSearch = true;,同时我们的index方法也需要重写,请参考下方的完整代码中PHP部分。如果我们启用了关联查询,当两个表中的字段有冲突时,我们必须在字段中加上别名。请参考下方的完整代码中JS部分。

11.标志
我们可以使用formatter:Table.api.formatter.flag来渲染标志字段,默认会将数据库的值渲染以下几种颜色

{index: 'success', hot: 'warning', recommend: 'danger', 'new': 'info'}

如果我们需要扩展额外的颜色,则可以使用

custom:{aaa: 'info', bbb:'danger'}

这样当值为aaa时会显示为蓝色,bbb时显示为红色

12.图片和图片组
我们可以使用

formatter:Table.api.formatter.image
formatter:Table.api.formatter.images

以上两种方式来渲染图片或图片组
请注意如果是图片组的情况下,数据值应该是以,进行分隔的

13.开关
我们可以使用formatter:Table.api.formatter.toggle来生成开关组件
默认情况下是根据数据库值1和0来表示开和关
我们可以通过额外的配置和定义开和关,比如

yes: 'open', no: 'close'

则此时会根据数据库值是open还是close来展示开关,
开关在点击的时候默认是只允许修改数据库的status字段的,如果我们开关不是status字段,我们需要在服务端对应的控制器中定义protected $multiFields="id,name,swith";,多个字段以,进行分隔

14.状态渲染
我们可以使用formatter:Table.api.formatter.status来渲染状态
默认根据以下值进行状态的颜色渲染

{normal: 'success', hidden: 'gray', deleted: 'danger', locked: 'info'}

如果我们状态有额外的值,我们可以使用custom来进行扩展显示的颜色,如下

custom: {rejected:'danger', agreed:'success'}

状态渲染显示的文本是根据searchList配置的值进行渲染的

15.自定义按钮
按钮组的功能是根据第8项中的Table.api.formatter.buttons进行生成的,代码如下

 {
    field: 'buttons',
    width: "120px",
    title: __('按钮组'),
    table: table,
    events: Table.api.events.operate,
    buttons: [
        {
            name: 'detail',
            text: __('弹出窗口打开'),
            title: __('弹出窗口打开'),
            classname: 'btn btn-xs btn-primary btn-dialog',
            icon: 'fa fa-list',
            url: 'example/bootstraptable/detail',
            callback: function (data) {
                Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
            },
            visible: function (row) {
                //返回true时按钮显示,返回false隐藏
                return true;
            }
        },
        {
            name: 'ajax',
            text: __('发送Ajax'),
            title: __('发送Ajax'),
            classname: 'btn btn-xs btn-success btn-magic btn-ajax',
            icon: 'fa fa-magic',
            url: 'example/bootstraptable/detail',
            confirm: '确认发送',
            success: function (data, ret) {
                Layer.alert(ret.msg + ",返回数据:" + JSON.stringify(data));
                //如果需要阻止成功提示,则必须使用return false;
                //return false;
            },
            error: function (data, ret) {
                console.log(data, ret);
                Layer.alert(ret.msg);
                return false;
            }
        },
        {
            name: 'addtabs',
            text: __('新选项卡中打开'),
            title: __('新选项卡中打开'),
            classname: 'btn btn-xs btn-warning btn-addtabs',
            icon: 'fa fa-folder-o',
            url: 'example/bootstraptable/detail'
        }
    ],
    formatter: Table.api.formatter.buttons
}

按钮配置支持的参数有:

name 按钮唯一标识,其中 add/edit/del/dragsort已经被占用,如果使用将覆盖相应的按钮配置。如果需要按钮按钮显示,我们可以在HTML视图文件的 table添加 data-buttons-标识来根据权限控制显示
text 按钮的文本内容,如果不需要显示文本可忽略,支持 function和 string类型
title 鼠标移上去的标题或 弹窗/选项显示的标题,支持 function和 string类型
icon 按钮的图标,请使用 font-awesome图标库,比如  fa fa-home
classname 按钮的 class, 其中 classname中的 btn-dialog、btn-ajax、btn-addtabs、btn-click,FastAdmin已经为这几个固定的Class注册了事件,所以可以直接使用,如果想要实现其它功能,需要自己手动编写代码绑定事件才可使用。
url 按钮的链接/Ajax事件请求的URL/弹窗链接/选项卡链接,直接 function和 string类型,此链接会自动在链接后添加 ids/{ids}{ids}为当行主键ID,如果需要传递其它字段值,请在URL中使用 {字段名}占位即可
refresh 自动刷新,只针对 btn-ajax事件
confirm 确认框提示文字,配置后会在确认操作再执行对应的事件,只针对 btn-ajax/btn-dialog/btn-addtabs事件
success 事件成功的回调,只针对 btn-ajax事件
error 事件失败的回调,只针对 btn-ajax事件
callback 弹窗回传的回调,只针对 btn-dialog事件,需要在对应打开的页面中使用 Fast.api.close(data);进行回传数据
hidden 是否隐藏按钮,按钮默认显示,支持 function和 bool类型
visible 是否显示按钮,按钮默认显示,支持 function和 bool类型
disable 是否禁用按钮,按钮默认不禁用,支持 function和 bool类型
click 当 classname包含 btn-click时的点击回调事件
extend 按钮扩展信息,可以任意定制按钮的参数,比如我们想在新窗口中打开链接,则配置 extend:' target="_blank"'即可
dropdown 下拉列表分组的名称,当多个按钮需要显示为一级时,该值为显示的文字

16.操作
操作区域默认是排序、编辑、删除这三个按钮,此功能也是根据第8项中Table.api.formatter.operate来实现的。排序按钮只在表中存在weigh字段时才会出现,编辑按钮和删除按钮会根据管理员所拥有的权限进行按需显示。如果我们需要重写编辑(排序、删除)按钮的相关属性,则可以使用buttons来定义编辑(排序、删除)的相关属性。其次Table.api.formatter.operate也支持buttons属性来配置多个其它按钮,如示例图中的详情按钮,配置参数请参考第15.自定义按钮。请参考下方完整代码中JS部分。
如果希望禁用编辑、删除按钮,可以通过修改视图中表格的属性置为空即可。

<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
       data-operate-edit="" 
       data-operate-del="" 
       width="100%">
</table>

17.分页信息
分页信息显示的文字可以通过在表格初始化前定义

$.fn.bootstrapTable.locales[Table.defaults.locale]['formatSearch'] = function (pageFrom, pageTo, totalRows) {
    return '显示第 ' + pageFrom + ' 到第 ' + pageTo + ' 条记录,总共 ' + totalRows + ' 条记录';
};

分页大小或分页大小选项可以在表格初始化时传入以下参数进行配置

pageSize: 10,
pageList: [10, 25, 50, 'All'],

18.翻页信息
翻页信息会根据服务端返回的数据行数自动进行渲染,如果返回的行数不满足分页条件时,此项是不会显示的

完整代码

PHP代码

<?php
namespace app\admin\controller;
use app\common\controller\Backend;
/**
 * 测试管理
 *
 * @icon fa fa-circle-o
 * @remark 此列表是通过php think crud -t test一键生成的针对数据表的查看、添加、编辑、删除、批量修改等功能,只需在设计表时符合FastAdmin相关字段名称、备注要求,即可生成相关的表单组件
 */
class Test extends Backend
{
    protected $model = null;
    protected $relationSearch = true;
    public function _initialize()
    {
        parent::_initialize();
        $this->model = model('Test');
    }
    /**
     * 查看
     */
    public function index()
    {
        if ($this->request->isAjax())
        {
            list($where, $sort, $order, $offset, $limit) = $this->buildparams();
            $total = $this->model
                    ->with("category")
                    ->where($where)
                    ->order($sort, $order)
                    ->count();
            $list = $this->model
                    ->with("category")
                    ->where($where)
                    ->order($sort, $order)
                    ->limit($offset, $limit)
                    ->select();
            $result = array("total" => $total, "rows" => $list, "extend" => ['money' => 1024, 'price' => 888]);
            return json($result);
        }
        return $this->view->fetch();
    }
}

JS代码:

define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
 
    var Controller = {
        index: function () {
            // 初始化表格参数配置
            Table.api.init({
                extend: {
                    index_url: 'test/index',
                    add_url: 'test/add',
                    edit_url: 'test/edit',
                    del_url: 'test/del',
                    multi_url: 'test/multi',
                    table: 'test',
                }
            });
 
            var table = $("#table");
 
            //当表格数据加载完成时
            table.on('load-success.bs.table', function (e, data) {
                //这里可以获取从服务端获取的JSON数据
                console.log(data);
                //这里我们手动设置底部的值
                $("#money").text(data.extend.money);
                $("#price").text(data.extend.price);
            });
 
            // 初始化表格
            table.bootstrapTable({
                url: $.fn.bootstrapTable.defaults.extend.index_url,
                pk: 'id',
                sortName: 'weigh',
                columns: [
                    [
                        {checkbox: true},
                        {field: 'id', title: __('Id')},
                        {field: 'admin_id', title: __('Admin_id')},
                        {field: 'category.name', title: __('分类名称'), formatter:Table.api.formatter.search},
                        {field: 'category_id', title: __('Category_id'), visible: false},
                        {field: 'flag', title: __('Flag'), searchList: {"hot": __('Flag hot'), "index": __('Flag index'), "recommend": __('Flag recommend')}, operate: 'FIND_IN_SET', formatter: Table.api.formatter.label},
                        {field: 'genderdata', title: __('Genderdata'), searchList: {"male": __('Genderdata male'), "female": __('Genderdata female')}, formatter: Table.api.formatter.normal},
                        {field: 'title', title: __('Title')},
                        {field: 'image', title: __('Image'), formatter: Table.api.formatter.image},
                        {field: 'images', title: __('Images'), formatter: Table.api.formatter.images},
                        {field: 'createtime', title: __('Createtime'), operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime},
                        {field: 'updatetime', title: __('Updatetime'), operate: 'RANGE', addclass: 'datetimerange', formatter: Table.api.formatter.datetime, visible: false},
                        {field: 'weigh', title: __('Weigh'), operate: false, visible: false},
                        {field: 'switch', title: __('Switch'), searchList: {"1": __('Yes'), "0": __('No')}, formatter: Table.api.formatter.toggle},
                        {field: 'status', title: __('Status'), searchList: {"normal": __('Normal'), "hidden": __('Hidden')}, formatter: Table.api.formatter.status},
                        {
                            field: 'buttons',
                            width: "120px",
                            title: __('按钮组'),
                            table: table,
                            events: Table.api.events.operate,
                            buttons: [
                                {
                                    name: 'detail',
                                    text: __('弹出窗口打开'),
                                    title: __('弹出窗口打开'),
                                    classname: 'btn btn-xs btn-primary btn-dialog',
                                    icon: 'fa fa-list',
                                    url: 'example/bootstraptable/detail',
                                    callback: function (data) {
                                        Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
                                    },
                                    visible: function (row) {
                                        //返回true时按钮显示,返回false隐藏
                                        return true;
                                    }
                                },
                                {
                                    name: 'ajax',
                                    text: __('发送Ajax'),
                                    title: __('发送Ajax'),
                                    classname: 'btn btn-xs btn-success btn-magic btn-ajax',
                                    icon: 'fa fa-magic',
                                    url: 'example/bootstraptable/detail',
                                    confirm: '确认发送',
                                    success: function (data, ret) {
                                        Layer.alert(ret.msg + ",返回数据:" + JSON.stringify(data));
                                        //如果需要阻止成功提示,则必须使用return false;
                                        //return false;
                                    },
                                    error: function (data, ret) {
                                        console.log(data, ret);
                                        Layer.alert(ret.msg);
                                        return false;
                                    }
                                },
                                {
                                    name: 'addtabs',
                                    text: __('新选项卡中打开'),
                                    title: __('新选项卡中打开'),
                                    classname: 'btn btn-xs btn-warning btn-addtabs',
                                    icon: 'fa fa-folder-o',
                                    url: 'example/bootstraptable/detail'
                                }
                            ],
                            formatter: Table.api.formatter.buttons
                        },
                        {
                            field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate,
                            buttons: [
                                {
                                    name: 'detail',
                                    title: __('详情'),
                                    classname: 'btn btn-xs btn-primary btn-dialog',
                                    icon: 'fa fa-list',
                                    url: 'test/detail',
                                    callback: function (data) {
                                        Layer.alert("接收到回传数据:" + JSON.stringify(data), {title: "回传数据"});
                                    }
                                }],
                            formatter: Table.api.formatter.operate
                        }
                    ]
                ]
            });
 
            // 绑定TAB事件
            $('.panel-heading a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
                var field = $(this).closest("ul").data("field");
                var value = $(this).data("value");
                var options = table.bootstrapTable('getOptions');
                options.pageNumber = 1;
                options.queryParams = function (params) {
                    var filter = {};
                    if (value !== '') {
                        filter[field] = value;
                    }
                    params.filter = JSON.stringify(filter);
                    return params;
                };
                table.bootstrapTable('refresh', {});
                return false;
            });
 
            // 为表格绑定事件
            Table.api.bindevent(table);
        },
        add: function () {
            Controller.api.bindevent();
        },
        edit: function () {
            Controller.api.bindevent();
        },
        api: {
            bindevent: function () {
                Form.api.bindevent($("form[role=form]"));
            }
        }
    };
    return Controller;
});

HTML代码:

<div class="panel panel-default panel-intro">
    <div class="panel-heading">
        {:build_heading(null,FALSE)}
        <ul class="nav nav-tabs" data-field="status">
            <li class="active"><a href="#t-all" data-value="" data-toggle="tab">{:__('All')}</a></li>
            {foreach name="statusList" item="vo"}
            <li><a href="#t-{$key}" data-value="{$key}" data-toggle="tab">{$vo}</a></li>
            {/foreach}
        </ul>
    </div>
    <div class="panel-body">
        <div id="myTabContent" class="tab-content">
            <div class="tab-pane fade active in" id="one">
                <div class="widget-body no-padding">
                    <div id="toolbar" class="toolbar">
                        <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
                        <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('test/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>
                        <a href="javascript:;" class="btn btn-success btn-edit btn-disabled disabled {:$auth->check('test/edit')?'':'hide'}" title="{:__('Edit')}" ><i class="fa fa-pencil"></i> {:__('Edit')}</a>
                        <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('test/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a>
                        <a href="javascript:;" class="btn btn-danger btn-import {:$auth->check('test/import')?'':'hide'}" title="{:__('Import')}" id="btn-import-file" data-url="ajax/upload" data-mimetype="csv,xls,xlsx" data-multiple="false"><i class="fa fa-upload"></i> {:__('Import')}</a>
 
                        <div class="dropdown btn-group {:$auth->check('test/multi')?'':'hide'}">
                            <a class="btn btn-primary btn-more dropdown-toggle btn-disabled disabled" data-toggle="dropdown"><i class="fa fa-cog"></i> {:__('More')}</a>
                            <ul class="dropdown-menu text-left" role="menu">
                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=normal"><i class="fa fa-eye"></i> {:__('Set to normal')}</a></li>
                                <li><a class="btn btn-link btn-multi btn-disabled disabled" href="javascript:;" data-params="status=hidden"><i class="fa fa-eye-slash"></i> {:__('Set to hidden')}</a></li>
                            </ul>
                        </div>
                        <a class="btn btn-info btn-disabled disabled btn-selected" href="javascript:;"><i class="fa fa-leaf"></i> 获取选中项</a>
                        <a class="btn btn-success btn-singlesearch" href="javascript:;"><i class="fa fa-user"></i> 自定义搜索</a>
                        <a class="btn btn-success btn-change btn-start" data-params="action=start" data-url="example/bootstraptable/start" href="javascript:;"><i class="fa fa-play"></i> 启动</a>
                        <a class="btn btn-danger btn-change btn-pause" data-params="action=pause" data-url="example/bootstraptable/pause" href="javascript:;"><i class="fa fa-pause"></i> 暂停</a>
                        <a href="javascript:;" class="btn btn-default" style="font-size:14px;color:dodgerblue;">
                            <i class="fa fa-dollar"></i>
                            <span class="extend">
                                金额:<span id="money">0</span>
                                单价:<span id="price">0</span>
                            </span>
                        </a>
                    </div>
                    <table id="table" class="table table-striped table-bordered table-hover table-nowrap"
                           data-operate-edit="{:$auth->check('test/edit')}" 
                           data-operate-del="{:$auth->check('test/del')}" 
                           width="100%">
                    </table>
                </div>
            </div>
 
        </div>
    </div>
</div>

感谢博主的详细分析,文章转自:初识fastAdmin表格列表的功能_fastadmin table-CSDN博客

stable diffusion webui 参数详解

-h, –help:显示帮助信息并退出。
–exit:安装后立即终止。
–data-dir:指定存储所有用户数据的基本路径,默认为”./”。
–config:用于构建模型的配置文件路径,默认为 “configs/stable-diffusion/v1-inference.yaml”。
–ckpt:稳定扩散模型的检查点路径;如果指定,该检查点将被添加到检查点列表并加载。
–ckpt-dir:稳定扩散检查点的目录路径。
–no-download-sd-model:即使没有找到模型,也不下载SD1.5模型。
–vae-dir:变分自编码器模型的路径。
–gfpgan-dir:GFPGAN目录。
–gfpgan-model:GFPGAN模型文件名。
–codeformer-models-path:Codeformer模型文件的目录路径。
–gfpgan-models-path:GFPGAN模型文件的目录路径。
–esrgan-models-path:ESRGAN模型文件的目录路径。
–bsrgan-models-path:BSRGAN模型文件的目录路径。
–realesrgan-models-path:RealESRGAN模型文件的目录路径。
–scunet-models-path:ScuNET模型文件的目录路径。
–swinir-models-path:SwinIR和SwinIR v2模型文件的目录路径。
–ldsr-models-path:LDSR模型文件的目录路径。
–lora-dir:Lora网络的目录路径。
–clip-models-path:CLIP模型文件的目录路径。
–embeddings-dir:用于文本逆向的嵌入目录,默认为 “embeddings”。
–textual-inversion-templates-dir:文本逆向模板的目录。
–hypernetwork-dir:超网络目录。
–localizations-dir:本地化目录。
–styles-file:用于样式的文件名,默认为 “styles.csv”。
–ui-config-file:用于UI配置的文件名,默认为 “ui-config.json”。
–no-progressbar-hiding:不隐藏Gradio UI中的进度条(默认隐藏,因为在浏览器中使用硬件加速会降低机器学习速度)。
–max-batch-count:UI的最大批次计数值,默认为16。
–ui-settings-file:用于UI设置的文件名,默认为 “config.json”。
–allow-code:允许从Web UI执行自定义脚本。
–share:使用Gradio的share=True,并使UI通过其网站访问(对我来说不起作用,但您可能会更幸运)。
–listen:使用0.0.0.0作为服务器名称启动Gradio,允许响应网络请求。
–port:使用给定的服务器端口启动Gradio,需要根/管理员权限才能使用1024以下的端口,默认为7860(如果可用)。 34. –hide-ui-dir-config:从Web UI中隐藏目录配置。
–freeze-settings:禁用编辑设置。
–enable-insecure-extension-access:无论其他选项如何,都启用扩展选项卡。
–gradio-debug:使用–debug选项启动Gradio。
–gradio-auth:设置Gradio身份验证,如 “username:password”;或逗号分隔多个,如 “u1:p1,u2:p2,u3:p3″。
–gradio-auth-path:设置Gradio身份验证文件路径,例如 “/path/to/auth/file”,与–gradio-auth的格式相同。
–disable-console-progressbars:不在控制台输出进度条。
–enable-console-prompts:在使用txt2img和img2img生成时,在控制台打印提示。
–api:使用API启动Web UI。
–api-auth:设置API身份验证,如 “username:password”;或逗号分隔多个,如 “u1:p1,u2:p2,u3:p3″。
–api-log:启用所有API请求的日志记录。
–nowebui:仅启动API,不启动UI。
–ui-debug-mode:不加载模型以快速启动UI。
–device-id:选择要使用的默认CUDA设备(可能需要在此之前设置CUDA_VISIBLE_DEVICES=0,1等环境变量)。
–administrator:管理员权限。
–cors-allow-origins:以逗号分隔的列表形式允许的CORS来源(无空格)。
–cors-allow-origins-regex:以单个正则表达式的形式允许的CORS来源。
–tls-keyfile:部分启用TLS,需要–tls-certfile才能完全生效。
–tls-certfile:部分启用TLS,需要–tls-keyfile才能完全生效。
–server-name:设置服务器主机名。
–gradio-queue:使用Gradio队列;实验性选项;破坏重新启动UI按钮。
–skip-version-check:不检查torch和xformers的版本。
–no-hashing:禁用检查点的sha256哈希,以提高加载性能。
性能类参数
–xformers:启用xformers以加速跨注意层。
–reinstall-xformers:强制重新安装xformers。在升级后使用,但升级后请移除,否则将一直重装xformers。
–force-enable-xformers:无论检查代码是否认为可以运行,都强制启用xformers的跨注意层;如果运行失败,请勿提交错误报告。
–opt-split-attention:强制启用Doggettx的跨注意层优化。默认情况下,对于启用CUDA的系统,此选项已开启。
–opt-split-attention-invokeai:强制启用InvokeAI的跨注意层优化。默认情况下,当CUDA不可用时,此选项已开启。
–opt-split-attention-v1:启用旧版本的分割注意力优化,该版本不会消耗所有可用的显存。
–opt-sub-quad-attention:启用内存高效的子二次交叉注意力层优化。
–sub-quad-q-chunk-size:子二次交叉注意力层优化使用的查询块大小。
–sub-quad-kv-chunk-size:子二次交叉注意力层优化使用的kv块大小。
–sub-quad-chunk-threshold:子二次交叉注意力层优化使用的显存使用率阈值。
–opt-channelslast:为4d张量启用备选布局,仅在具有Tensor核心的Nvidia显卡(16xx及更高版本)上可能导致更快的推理。
–disable-opt-split-attention:强制禁用跨注意层优化。
–disable-nan-check:不检查生成的图像/潜在空间是否包含nan值;在持续集成中运行时无需检查点。
–use-cpu:对指定模块使用CPU作为torch设备。
–no-half:不将模型切换为16位浮点数。
–precision:以此精度进行评估。
–no-half-vae:不将VAE模型切换为16位浮点数。
–upcast-sampling:向上采样。与 –no-half 无效。通常产生与 –no-half 类似的结果,但在使用较少内存的情况下性能更好。
–medvram:启用稳定扩散模型优化,牺牲一点速度以减少显存使用。
–lowvram:启用稳定扩散模型优化,牺牲大量速度以极低的显存使用。
–lowram:将稳定扩散检查点权重加载到显存而非RAM。
–always-batch-cond-uncond:禁用使用 –medvram 或 –lowvram 时为节省内存而启用的条件/无条件批处理。
通用类参数
–autolaunch:在启动时使用系统的默认浏览器打开WebUI URL。
–theme:在WebUI中使用指定的主题(“light”或“dark”)。如果未指定,则使用浏览器的默认主题。
–use-textbox-seed:在UI中使用文本框输入种子(没有上/下箭头,但可以输入长种子)。
–disable-safe-unpickle:禁用对PyTorch模型的恶意代码检查。
–ngrok:用于ngrok的自动令牌,是gradio –share的替代方案。
–ngrok-region:ngrok应该在其中启动的区域。

stable diffusion的api参数-附带插件使用

stable diffusion 的 webui中api的使用,包括lora,controlnet、roop、vae等模块。 欢迎留言加群详细交流

{
    "prompt": "(masterpiece, best quality,Refined, beautiful),haruno sakura",
    "negative_prompt": " ((((big hands, un-detailed skin, semi-realistic)))), (((ugly mouth, ugly eyes,The disabled eyes, blurred eyes, missing teeth, crooked teeth, close up, cropped, out of frame))), worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, gross proportions, malformed limbs, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck,Deforming fingers, incomplete fingers, extra fingers,Ugly face",
    "override_settings": {
        "sd_model_checkpoint": "MXCFSuper_Counterfeit/mxcfSuperCounterfeit_v10.safetensors [56c0585d87]",
        "sd_vae": "YOZORA.vae.pt"
    },
    "clip_skip": 2,
    "denoising_strength": 0.5,
    "seed": -1,
    "batch_size": 1,
    "n_iter": 1,
    "steps": 25,
    "cfg_scale": 7,
    "restore_faces": False,
    "sampler_index": "DPM++ SDE Karras",
    "resize_mode": 1, # "mask_blur": 10, # "tiling": False, # "eta": 0, # "script_args": [], # "inpainting_fill": 1, # "inpaint_full_res": True, # "inpaint_full_res_padding": 32, # "inpainting_mask_invert": 1,
    "alwayson_scripts": {
        "other plugin": {
            "args": [arg1, arg2
            ]
        } "ControlNet": {
            "args": [
                {
                    "enabled": True,
                    "input_image": "",
                    "mask": None,
                    "module": "canny",
                    "model": "control_sd15_canny [fef5e48e]",
                    "weight": 1,
                    "invert_image": False,
                    "resize_mode": 1,
                    "rgbbgr_mode": False,
                    "lowvram": False,
                    "processor_res": 512,
                    "threshold_a": 100,
                    "threshold_b": 200,
                    "starting_control_step": 0,
                    "ending_control_step": 1, # "guidance": 1, # "guidance_start": 0, # "guidance_end": 1,
                    "guessmode": False
                }
            ]
        }
    }
}

参数解释:

模型:MXCFSuper_Counterfeit

VAE:none

Clip skip:2 

Prompt:

Negative Prompt:””

Resize mode:Just resize

Sampling method:DPM++ SDE Karras

Sampling steps:25

Restore faces:flase

Width:所传图片宽度

Height:所传图片高度

Batch count:1

Batch size:1

CFG Scale:5

Denoising strength:0.5

Seed:-1

ControlNet:Enable

Control Type:Canny

Preprocessor:Canny

Model:control_sd15_canny

Control Weight:1

Starting Control Step:0

Ending Control Step:1

Preprocessor Resolution:512

Canny Low Threshold:100

Canny Hight Threshold:200

Control Mode:Balanced

Resize Mode:Crop and Resize

Script:none

腾讯云安装stable diffusion webui

安装项目中的提示操作:GitHub – AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI

遇到的问题:

  1. github中clone项目的时候慢:
  2. 安装torch的时候源慢:
    • 安装的时候等一会,一般是前面阶段比较慢,到后面速度就会上来。
    • 可以在launch.py中将对应的源替换掉,使用清华的源。
  3. 一些缺失的包:
    • ERROR: Cannot activate python venv
    • python3 -m venv venv/
      1. 安装TCMalloc
        • apt install libgoogle-perftools-dev
  4. 服务器重启后找不到显卡驱动,报错 torch is not able to use gpu
    • apt update 更新的时候 内核的header没安装成功,重启了服务器,造成显卡驱动没有正常运行 重新安装了一下 kernel header
    • sudo apt install linux-headers-$(uname -r)
  5. 无法使用公网访问:
    • Could not create share link. Please check your internet connection or our status page: https://status.gradio.app
      • 没解决,并且sd默认的公网服务并不稳定。  使用nginx反向代理7860端口解决
      • 换海外服务器
  6. ERROR: Cannot activate python venv, aborting…
    • python3 -c ‘import venv’ #should not return any errors
    • python3 -m venv venv/ #should populate the directory
  7. nginx做反向代理,需要增加socket支持:
    • location / {
    • proxy_pass http://127.0.0.1:7860;
    • proxy_http_version 1.1;
    • proxy_set_header Upgrade $http_upgrade;
    • proxy_set_header Connection “Upgrade”;
    • proxy_read_timeout 300s;
    • proxy_connect_timeout 300s;
    • }
  8. 运行: ./webui.sh –share –enable-insecure-extension-access

使用cloudflare代理flask启用https服务

问题1:使用cloudflare的dns回源服务器的时候,出现了http和https不断反复重定向

问题2:  flask只能启用http服务,需要启用https

  • 使用lnmp vhost add 添加域名,配置ssl证书
  • pip install gunicorn
  • 新建文件 gunicorn_start.sh(解决问题2),运行sh gunicorn_start.sh
#!/bin/bash
 
# 定义Flask应用程序的名称和入口文件名
APP_NAME=app
APP_ENTRYPOINT=app:app
 
# 定义SSL证书和私钥文件的路径
CERT_FILE=/path/to/cert.pem
KEY_FILE=/path/to/key.pem
 
# 启动Gunicorn
gunicorn $APP_ENTRYPOINT \
  --bind 0.0.0.0:443 \
  --certfile $CERT_FILE \
  --keyfile $KEY_FILE \
  --workers 4 \
  --worker-class gthread \
  --threads 2 \
  --timeout 120 \
  --log-level=info \
  --access-logfile=- \
  --error-logfile=-
  •  nginx  配置
server
    {
        listen 443 ssl http2;
        #listen [::]:443 ssl http2;
        server_name your-site ;
        index index.html index.htm index.php default.html default.htm default.php;
        root  /home/wwwroot/your-site;
 
        ssl_certificate /usr/local/nginx/conf/ssl/your-site/fullchain.cer;
        ssl_certificate_key /usr/local/nginx/conf/ssl/your-site/your-site.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
        ssl_session_cache builtin:1000 shared:SSL:10m;
        # openssl dhparam -out /usr/local/nginx/conf/ssl/dhparam.pem 2048
        ssl_dhparam /usr/local/nginx/conf/ssl/dhparam.pem;
 
 
        # Deny access to PHP files in specific directory
        #location ~ /(wp-content|uploads|wp-includes|images)/.*\.php$ { deny all; }
 
        location / {
                proxy_pass https://127.0.0.1:5000;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        access_log  /home/wwwlogs/your-site.log;
 
    }
  • cloudflare找到对应的域名,点击左侧的 “SSL/TLS”。 将“Your SSL/TLS encryption mode ” 配置成FULL或者FULL(Strict) (否则不支持https,解决问题1)

github连接超时(不能push,不能pull)

git pull 或者git push的时候,提示:

kex_exchange_identification: Connection closed by remote host
Connection closed by 127.0.0.1 port 22
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
  1. 使用命令:ssh -T git@github.com
    1. 返回结果:Hi developer! You’ve successfully authenticated, but GitHub does not provide shell access.
    2. 表明没有问题
  2. 使用代理:
#只对github.com
git config --global http.https://github.com.proxy socks5://127.0.0.1:1080

#取消代理
git config --global --unset http.https://github.com.proxy

还是同样的问题。

3. 将ssh的22端口改为使用443

在等待git pull命令超时之后报出了类似: ssh: connect to host github.com port 22: Connection timed out的错误!

原因是: ssh 阻塞了22端口!

解决方法: 修改 ssh 的配置文件

关于修改配置,存在两种解决方法:

/etc/ssh/ssh_config 中修改全局配置
在用户主目录下.ssh/中添加配置文件
这里选择的后者:

cd ~/.ssh/
vi config

# 在config中添加下面内容
Host github.com  
User git  
Hostname ssh.github.com 
PreferredAuthentications publickey  
IdentityFile ~/.ssh/id_rsa 
Port 443
即: 使用https的443端口进行访问!

python获取print的输入内容

因为print是输出到sys.out里面, 所以要替换标准的输出:


#coding: utf-8

class TextArea(object):

    def __init__(self):
        self.buffer = []

    def write(self, *args, **kwargs):
        self.buffer.append(args)

import sys

stdout = sys.stdout
sys.stdout = TextArea()

# print to TextArea
print("testA")
print("testB")
print("testC")

text_area, sys.stdout = sys.stdout, stdout

# print to console
print(text_area.buffer)
输出内容:
[('testA',), ('\n',), ('testB',), ('\n',), ('testC',), ('\n',)]

如果是json数据,可以增加一步处理:

data_get_json_ori = text_area.buffer[0]  # json
data_get_json = json.loads(data_get_json_ori)

data_get= {}
host = ''
for key, value in data_get_json.items():
data_get[key] = value

go调用python3

因为python的底层c写的,只需要让go可以调用c就能调用python了

import "github.com/DataDog/go-python3"
/*
#cgo CFLAGS: "-I/usr/include/python3.8"
#cgo LDFLAGS: "-L/usr/local/lib/python3.8" "-lpython3.8"
#define PY_SSIZE_T_CLEAN
#include <Python.h>
*/
pycodeGo := `
from you_get import common
video_url = "` + video_url + `"
res = common.any_download(url=video_url,json_output=True)
output['title'] = res['title']
output['url'] = res['streams']['18']['url']
print(output)
`

defer python3.Py_Finalize()
python3.Py_Initialize()
pyResult := python3.PyRun_SimpleString(pycodeGo)

github上有2个python的项目,一个python2,一个python3

python2:https://github.com/sbinet/go-python

python3:https://github.com/DataDog/go-python3

ubuntu18安装Python3.7

  1. ubuntu18系统自带了python2.7和python3.6;
  2. 不要卸载ubuntu自带的python版本;
  3. ubuntu下不同版本的python可以共存,可直接安装python3.7。

1.升级包索引和软件

sudo apt update
sudo apt upgrade -y

2.安装编译所需包

sudo apt install build-essential zlib1g-dev libbz2-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev wget

3.下载Python3.7

官网下载Python-3.7.4.tgz文件

wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz

4.解压Python安装包

tar -xzvf Python-3.7.4.tgz

5.编译和安装

cd Python-3.7.4
./configure --prefix=/usr/local/src/python37  # 配置安装位置
sudo make
sudo make install

6.建立软连接

sudo ln -s /usr/local/src/python37/bin/python3.7 /usr/bin/python3.7
sudo ln -s /usr/local/src/python37/bin/pip3.7 /usr/bin/pip3.7

接下来你就可以使用python3.7,而不会影响系统自带的python2.7和python3.6

如果你想用python3.7替换掉系统默认的python3

sudo rm -rf /usr/bin/python3
sudo ln -s /usr/local/src/python37/bin/python3.7 /usr/bin/python3

默认ubuntu系统中没有pip3,直接建立软连接即可。
sudo ln -s /usr/local/src/python37/bin/pip3.7 /usr/bin/pip3

输入python3默认就是你安装好的python3.7了

vim常用配置

set nocompatible " 关闭 vi 兼容模式

syntax on " 自动语法高亮

colorscheme desert" 设定配色方案

set number " 显示行号

set cursorline " 突出显示当前行

set ruler " 打开状态栏标尺

set shiftwidth=4 " 设定 << 和 >> 命令移动时的宽度为 4

set softtabstop=4 " 使得按退格键时可以一次删掉 4 个空格

set tabstop=4 " 设定 tab 长度为 4

set nobackup " 覆盖文件时不备份

set autochdir " 自动切换当前目录为当前文件所在的目录

filetype plugin indent on " 开启插件

set backupcopy=yes " 设置备份时的行为为覆盖

set ignorecase smartcase " 搜索时忽略大小写,但在有一个或以上大写字母时仍保持对大小写敏感

set nowrapscan " 禁止在搜索到文件两端时重新搜索

set incsearch " 输入搜索内容时就显示搜索结果

set hlsearch " 搜索时高亮显示被找到的文本

set noerrorbells " 关闭错误信息响铃

set novisualbell " 关闭使用可视响铃代替呼叫

set t_vb= " 置空错误铃声的终端代码

" set showmatch " 插入括号时,短暂地跳转到匹配的对应括号

" set matchtime=2 " 短暂跳转到匹配括号的时间

set magic " 设置魔术

set hidden " 允许在有未保存的修改时切换缓冲区,此时的修改由 vim 负责保存

set guioptions-=T " 隐藏工具栏

set guioptions-=m " 隐藏菜单栏

set smartindent " 开启新行时使用智能自动缩进

set backspace=indent,eol,start

" 不设定在插入状态无法用退格键和 Delete 键删除回车符

set cmdheight=1 " 设定命令行的行数为 1

set laststatus=2 " 显示状态栏 (默认值为 1, 无法显示状态栏)

set statusline=\ %<%F[%1*%M%*%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %c:%l/%L%)\

" 设置在状态行显示的信息

set foldenable " 开始折叠

set foldmethod=syntax " 设置语法折叠

set foldcolumn=0 " 设置折叠区域的宽度

setlocal foldlevel=1 " 设置折叠层数为

" set foldclose=all " 设置为自动关闭折叠

" 用空格键来开关折叠

" nnoremap @=((foldclosed(line(‘.’)) < 0) ? ‘zc’ : ‘zo’)

" 配置多语言环境

if has("multi_byte")

" UTF-8 编码

set encoding=utf-8

set termencoding=utf-8

set formatoptions+=mM

set fencs=utf-8,gbk
endif

" 选中状态下 Ctrl+c 复制

vmap <C-c>"+y

" 确认vim编辑器是否支持clipboard特性
" vim –version| grep “clipboard"
" 支持这特性的检索结果应该包含 +clipboard,而不支持则会是 -clipboard

更改pip源

  1. 临时使用:

在使用pip的时候加参数-i

例如:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gevent

常用的pip源如下:

pypi 清华大学源:https://pypi.tuna.tsinghua.edu.cn/simple
pypi 豆瓣源 :http://pypi.douban.com/simple/
pypi 腾讯源:http://mirrors.cloud.tencent.com/pypi/simple
pypi 阿里源:https://mirrors.aliyun.com/pypi/

  1. 永久修改:

linux下 :

mkdir ~/.pip
vi ~/.pip/pip.conf
添加内容如下:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
windows下,直接在user目录中创建一个pip目录,如:C:\Users\xx\pip,新建文件pip.ini,内容如下

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple

在crontab中执行scrapy(解决不执行,不爬取数据的问题)

在crontab中执行scrapy会遇到命令不执行,或者执行了但是没有爬取数据的问题,这里做一下总结

  1. 先说这里遇到的问题和解决方案:
    1. spider不执行:在crontab中需要先cd到项目目录,然后调用命令,否则找不到爬虫
    2. 执行scrapy的时候需要调用/usr/local/bin/scrapy crawl spider_name,否则找不到scrapy命令
  2. 如何使用crontab做爬取:这里有2种方式,一种是直接在crontab中执行scrapy crawl spider_name,将每个爬虫写一遍
    1. 0 3 * * * cd /project_path/spider && /usr/bin/python3 startup.py >> /tmp/spider.log
    2. 另一种是增加一个startup.py,调用subprocess,将需要调用的爬虫做整合然后循环调用:
# 顺序执行所有爬虫
import subprocess
from datetime import datetime
import time


def crawl_work():
    date_start = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

    crawl_name_list = ['spider_1', 'spider_2']
    record_time_list = {}
    for crawl_name in crawl_name_list:
        start_time = time.time()
        record_time_list[crawl_name] = {}
        record_time_list[crawl_name]['start_date'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        subprocess.Popen('/usr/local/bin/scrapy crawl ' + crawl_name, shell=True).wait()
        end_time = time.time()
        record_time_list[crawl_name]['end_date'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        record_time_list[crawl_name]['time_last'] = int((end_time - start_time) / 60)  # 分钟,向下取整
    print('time_record-date_start: ', date_start)
    for crawl_name, record_time in record_time_list.items():
        print('time_record-' + crawl_name + ': ', record_time)
    date_end = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    print('time_record-date_end: ', date_end)


if __name__ == '__main__':
    crawl_work()

python append 覆盖数据

问题:使用append为list添加object的时候,有时候会出现后面的object将前面的几个给覆盖的情况

原因:当在list中append一个对象的时候,并没有创建新的内存地址,而是将该对象的地址引用了过来,这就导致当该对象变化的时候,前面append进来的对象也会跟着变化。

例子代码:

list = []
item  = {}
for i in range(3,3):
    item[1] = i
    list.append({'item':item})
print(list)


结果:[{'item': {1: 2}}, {'item': {1: 2}}, {'item': {1: 2}}]


解决方式:使用深度拷贝  deepcopy ,复制出一个新的对象并使用。 

例子代码:

import copy

list = []
item  = {}
for i in range(0,3):
    item[1] = i
    list.append(copy.deepcopy({'item':item}))
print(list)

结果:[{'item': {1: 0}}, {'item': {1: 1}}, {'item': {1: 2}}]

scrapy提供接口服务

将scrapy发布为服务并提供接口服务,以便其他项目调用:

  • pip install scrapyd
  • pip install scrapyd-client
  • 修改scrapy.cfg:
  • 启动服务:scrapyd

[settings]
default = compass.settings

[deploy]
url = http://localhost:6800/
project = compass[settings]

  • 部署服务:scrapyd-deploy
  • 启动服务:scrapyd
  • 调用服务:curl http://localhost:6800/schedule.json -d project=your_project_name -d spider=your_spider_name
  • 不清楚项目名和爬虫名可以用下面命令查询:
    • 列出项目:curl http://localhost:6800/listprojects.json
    • 列出爬虫:curl http://localhost:6800/listspiders.json?project=compass
  • 其他命令:
    • 调度爬虫
      • curl http://localhost:6800/schedule.json -d project=your_project_name -d spider=your_spider_name
    • 包含参数
      • curl http://localhost:6800/schedule.json -d project=your_project_name -d spider=your_spider_name -d setting=DOWNLOAD_DELAY=2 -d arg1=val1
    • 取消运行
      • curl http://localhost:6800/cancel.json -d project=your_project_name -d job=2bffadcb3218k9abbd23ccf016aa82f02
    • 列出版本
      • curl http://localhost:6800/listversions.json?project=your_project_name
    • 列出job
      • curl http://localhost:6800/listjobs.json?project=your_project_name
    • 删除版本
      • curl http://localhost:6800/delversion.json -d project=your_project_name -d version==15419782769
    • 删除项目
      • curl http://localhost:6800/delproject.json -d project=your_project_name

MCC(移动国家码)和 MNC(移动网络码)

MCC:Mobile Country Code,移动国家码,MCC的资源由国际电联(ITU)统一分配和管理,唯一识别移动用户所属的国家,共3位,中国为460;

MCC常用值

代码(MCC)ISO 3166-1国家
202GR希腊
204NL荷兰
206BE比利时
208FR法国
212MC摩纳哥
213AD安道尔
214ES西班牙
216匈牙利
218BA波斯尼亚和黑塞哥维那
219HR克罗地亚
220RS塞尔维亚 (共和国)
222IT意大利
225VA梵蒂冈城国
226RO罗马尼亚
228CH瑞士
230CZ捷克共和国
231SK斯洛伐克
232AT奥地利
234GB联合王国
235GB联合王国
238DK丹麦
240SE瑞典
242NO挪威
244FI芬兰
246LT立陶宛
247LV拉脱维亚
248EE爱沙尼亚
250RU俄罗斯联邦
255UA乌克兰
257BY白俄罗斯
259MD摩尔多瓦
260PL波兰
262DE德国
266GI直布罗陀 ( 英国 )
268PT葡萄牙
270LU卢森堡
272IE爱尔兰
274IS冰岛
276AL阿尔巴尼亚
278MT马耳他
280CY塞浦路斯
282通用电器格鲁吉亚
283调幅亚美尼亚
284BG保加利亚
286TR土耳其
288FO法罗群岛 ( 丹麦 )
290GL格陵兰 ( 丹麦 )
292SM圣马力诺
293SI斯洛文尼亚
294MK马其顿共和国
295列支敦士登
297ME黑山 (共和国)
302CA加拿大
308PM圣皮埃尔和密克隆群岛 ( 法国 )
310美国美国
311美国美国
312美国美国
313美国美国
314美国美国
315美国美国
316美国美国
330PR波多黎各 ( 美国 )
332VI美属维尔京群岛 ( 美国 )
334MX墨西哥
338JM牙买加
340GP瓜德罗普岛 ( 法国 )
340MQ马提尼克岛 ( 法国 )
342BB巴巴多斯
344AG安提瓜和巴布达
346KY开曼群岛 ( 英国 )
348VG英属维尔京群岛 ( 英国 )
350BM百慕达 ( 英国 )
352GD格林纳达
354MS蒙特塞拉特 ( 英国 )
356KN圣基茨和尼维斯
358LC圣卢西亚
360VC圣文森特和格林纳丁斯
362CW库拉索岛 ( 荷兰 )
363AW阿鲁巴 ( 荷兰 )
364BS巴哈马
365AI安圭拉
366DM多米尼加
368CU古巴
370DO多米尼加共和国
372HT海地
374TT特里尼达和多巴哥
376TC特克斯和凯科斯群岛 ( 英国 )
400AZ阿塞拜疆共和国
401KZ哈萨克斯坦
402BT不丹
404IN印度
405IN印度
406IN印度
410PK巴基斯坦
412AF阿富汗
413LK斯里兰卡
414MM缅甸
415LB黎巴嫩
416JO约旦
417SY叙利亚
418IQ伊拉克
419KW科威特
420SA沙特阿拉伯
421YE也门
422OM阿曼
424AE阿拉伯联合大公国
425IL以色列
425PS巴勒斯坦
426BH巴林
427QA卡塔尔
428MN蒙古
429NP尼泊尔
430AE阿拉伯联合大公国
431AE阿拉伯联合大公国
432IR伊朗
434UZ乌兹别克斯坦
436TJ塔吉克斯坦
437KG吉尔吉斯共和国
438TM土库曼斯坦
440JP日本
441JP日本
450KR韩国,南非,
452VN越南
454HK香港 ( 中国 )
455MO澳门 ( 中国 )
456KH柬埔寨
457LA老挝
460CN中国
461CN中国
466TW台湾
467KP韩国,北
470BD孟加拉国
472MV马尔代夫
502MY马来西亚
505AU澳大利亚
510ID印尼
514TL东帝汶
515PH菲律宾
520TH泰国
525SG新加坡
528BN文莱
530NZ新西兰
534MP北马里亚纳群岛 ( 美国 )
535GU关岛 ( 美国 )
536NR瑙鲁
537PG巴布亚新几内亚
539TO汤加
540SB索罗门群岛
541VU瓦努阿图
542FJ
543WF瓦利斯群岛和富图纳群岛 ( 法国 )
544AS美属萨摩亚 ( 美国 )
545KI基里巴斯
546NC新喀里多尼亚 ( 法国 )
547PF法属波利尼西亚 ( 法国 )
548CK库克群岛 ( 新西兰 )
549WS萨摩亚
550调频密克罗尼西亚联邦
551MH马绍尔群岛
552PW帕劳
555NU纽埃
602EG埃及
603DZ阿尔及利亚
604MA摩洛哥
605TN突尼斯
606LY利比亚
607GM冈比亚
608SN塞内加尔
609MR毛里塔尼亚
610ML马里
611GN几内亚
612CI科特迪瓦
613BF布基纳法索
614NE尼日尔
615TG多哥共和国
616BJ贝宁
617MU毛里求斯
618LR利比里亚
619SL塞拉利昂
620GH加纳
621NG尼日利亚
622TD乍得
623CF中非共和国
624CM喀麦隆
625CV佛得角
626ST圣多美及普林西比
627GQ赤道几内亚
628GA加蓬共和国
629CG刚果民主共和国
630光盘刚果民主共和国
631AO安哥拉
632GW几内亚比绍
633SC塞舌尔
634SD苏丹
635RW卢旺达共和国
636ET埃塞俄比亚
637SO索马里
638DJ吉布提
639KE肯尼亚
640TZ坦桑尼亚
641UG乌干达
642BI布隆迪
643MZ莫桑比克
645ZM赞比亚
646MG马达加斯加
647可再生能源留尼汪 ( 法国 )
648ZW津巴布韦
649NA纳米比亚
650MW马拉维
651LS莱索托
652BW博茨瓦纳
653SZ斯威士兰
654KM科摩罗
655ZA南非
657ER厄立特里亚
702BZ伯利兹
704GT危地马拉
706SV萨尔瓦多
708HN洪都拉斯
710NI尼加拉瓜
712CR哥斯达黎加
714PA巴拿马
716PE秘鲁
722AR阿根廷共和国
724BR巴西
730CL智利
732CO哥伦比亚
734VE委内瑞拉
736BO玻利维亚
738GY圭亚那
740EC厄瓜多尔
742GF法属圭亚那 ( 法国 )
744PY巴拉圭
746SR苏里南
748UY乌拉圭
750FK福克兰群岛 (英国)

MNC常用值

00中国移动
01中国联通
02中国移动
03中国电信
04中国移动
06中国联通
07中国移动
08中国移动
09中国联通
10中国联通
11中国电信
12中国电信
13中国移动

mac安装redis

首先查看是否安装了homebrew

brew --version

如果此命令失败,那么先去安装homebrew吧。

安装

从终端运行来安装redis:

brew install redis

在前台启动和停止 Redis

从命令行运行以下命令来测试是否成功安装了redis:

redis-server

如果成功,将看到 Redis 的启动日志,并且 Redis 将在前台运行。

输入Ctrl-C停止redis

使用 launchd 启动和停止 Redis

作为在前台运行 Redis 的替代方案,还可以使用launchd在后台启动进程:

brew services start redis

这将启动 Redis 并在登录时重新启动它。可以launchd通过运行以下命令检查托管 Redis 的状态:

brew services info redis

如果服务正在运行,将看到如下输出:

redis (homebrew.mxcl.redis)
Running: ✔
Loaded: ✔
User: yourname
PID: 67836

要停止服务,请运行:

brew services stop redis

连接到 Redis

Redis 运行后,可以运行以下命令对其进行测试redis-cli

redis-cli

这将打开 Redis REPL。尝试运行一些命令:

127.0.0.1:6379> lpush demos redis-macOS-demo
OK
127.0.0.1:6379> rpop demos
"redis-macOS-demo"

mac查看硬盘使用情况

mac有很多方法查看硬盘使用情况

  1. 使用自带的硬盘检测工具 disk Utility
  2. 使用自带的shell命令
    1. du -sh . | sort -rn | head -n 10
  3. 使用一个形象可视化的shell命令ncdu(推荐)
    1. 安装:在命令行中输入:brew install ncdu
    2. 使用:在命令行中输入:ncdu+空格+路径
      1. 举例,查看当前路径硬盘使用情况,就是(ncdu+空格+点): ncdu .
      2. 举例,查看所有硬盘使用情况,就是(ncdu+空格+”/”): ncdu /

快捷键如下所示:

? — 使用帮助
up, k — 向上移动光标
down, j – 向下移动光标
right/enter — 打开选定的目录
left, <, h — 打开父目录
n — 按文件名排序(升序/降序)
s — 按文件大小排序(升序/降序)
C – 按项目数排序(升序/降序)
d – 删除选定的文件或目录
t — 排序时将目录放在文件前面
g – 以图形方式显示百分比

如果发现硬盘占用不多,但是总会提示可用空间不足, 或者在硬盘工具disk Utility中显示没有空间,可以参考这篇文章:如何处理mac空间不足的问题

mac空间不足

先说结果:是由于时间机器(time machine)的自动备份造成的。

在Mac的「关于本机」中显示我的电脑还有90多G的可用空间,但是使用硬盘工具(disk Utility)时发现空间都被Macintosh – Data占用了。

用了各种工具shell命令一起用,发现根本解决不了问题,期间还发现了mac firmlink的骚操作。

使用硬盘工具,显示如下信息:

「可用100.41GB(73.22GB可清除)」

这就引起了我的注意,因为如果这73.22GB清除掉,就是我正常可用的空间了,也和「关于本机」中的数据相符。

具体方法为:

在「系统偏好设置」中,找到「时间机器」,将「自动备份」取消勾选。

  1. 启动「终端」,输入以下命令

sudo tmutil listlocalsnapshots /
输入完成后,会要求你输入管理员密码,输入后回车。

  1. 这时候就会出现一堆写着Time Machine的字符出现。
  1. 输入

tmutil deletelocalsnapshots XXXX-XX-XX-XXXXXX
这里的「XXXX-XX-XX-XXXXXX」是Time Machine后面的那串数字,如 2021-08-19-103846。

  1. 等待一会,出现「Deleted local snapshot」后代表删除成功。

操作结束后,回到「磁盘工具」可以发现可用空间变多了

但是仍然显示有73.22GB可清除。此时只需重启电脑。

PHP使用curl

php封装好的5种不同方式的curl请求函数

<?php
//get请求
function geturl($url){
        $headerArray =array("Content-type:application/json;","Accept:application/json");
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); 
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch,CURLOPT_HTTPHEADER,$headerArray);
        $output = curl_exec($ch);
        curl_close($ch);
        $output = json_decode($output,true);
        return $output;
}

//post请求
function posturl($url,$data){
        $data  = json_encode($data);    
        $headerArray =array("Content-type:application/json;charset='utf-8'","Accept:application/json");
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,FALSE);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        curl_setopt($curl,CURLOPT_HTTPHEADER,$headerArray);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return json_decode($output,true);
}


//put请求
function puturl($url,$data){
    $data = json_encode($data);
    $ch = curl_init(); //初始化CURL句柄 
    curl_setopt($ch, CURLOPT_URL, $url); //设置请求的URL
    curl_setopt ($ch, CURLOPT_HTTPHEADER, array('Content-type:application/json'));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); //设为TRUE把curl_exec()结果转化为字串,而不是直接输出 
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST,"PUT"); //设置请求方式
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);//设置提交的字符串
    $output = curl_exec($ch);
    curl_close($ch);
    return json_decode($output,true);
}

//del请求
function delurl($url,$data){
    $data  = json_encode($data);
    $ch = curl_init();
    curl_setopt ($ch,CURLOPT_URL,$put_url);
    curl_setopt ($ch, CURLOPT_HTTPHEADER, array('Content-type:application/json'));
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "DELETE");   
    curl_setopt($ch, CURLOPT_POSTFIELDS,$data);
    $output = curl_exec($ch);
    curl_close($ch);
    $output = json_decode($output,true);
}

//patch请求
function patchurl($url,$data){
    $data  = json_encode($data);
    $ch = curl_init();
    curl_setopt ($ch,CURLOPT_URL,$url);
    curl_setopt ($ch, CURLOPT_HTTPHEADER, array('Content-type:application/json'));
    curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "PATCH");  
    curl_setopt($ch, CURLOPT_POSTFIELDS,$data);     
    $output = curl_exec($ch);
    curl_close($ch);
    $output = json_decode($output);
    return $output;
}
?>

CentOS7 手动部署lnmp环境

步骤一:准备编译环境

  1. 关闭防火墙。
    1. 运行systemctl status firewalld命令,查看当前防火墙的状态。查看防火墙状态
      • 如果防火墙的状态参数是inactive,则防火墙为关闭状态。
      • 如果防火墙的状态参数是active,则防火墙为开启状态。
    2. 关闭防火墙。如果防火墙为关闭状态可以忽略此步骤。
      • 如果您想临时关闭防火墙,需要运行以下命令:systemctl stop firewalld说明 临时关闭防火墙后,如果Linux实例重启,则防火墙将会自动开启。
      • 如果您想永久关闭防火墙,需要依次运行以下命令:
        1. 关闭防火墙。systemctl stop firewalld
        2. 实例开机时,禁止启动防火墙服务。systemctl disable firewalld
  2. 关闭SELinux。
    1. 运行getenforce命令查看SELinux的当前状态。
      • 如果SELinux状态参数是Disabled,则SELinux为关闭状态。
      • 如果SELinux状态参数是Enforcing,则SELinux为开启状态。
    2. 关闭SELinux。如果SELinux为关闭状态可以忽略此步骤。

步骤二:安装Nginx

  1. 运行以下命令安装Nginx。yum -y install nginx
  2. 运行以下命令查看Nginx版本。nginx -v返回结果如下所示,表示Nginx安装成功。nginx version: nginx/1.20.1

步骤三:安装MySQL

  1. 运行以下命令更新YUM源。rpm -Uvh http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
  2. 运行以下命令安装MySQL。说明 如果您使用的操作系统内核版本为el8,可能会提示报错信息No match for argument。您需要先运行命令yum module disable mysql禁用默认的MySQL模块,再安装MySQL。yum -y install mysql-community-server
  3. 运行以下命令查看MySQL版本号。mysql -V返回结果如下所示,表示MySQL安装成功。mysql Ver 14.14 Distrib 5.7.36, for Linux (x86_64) using EditLine wrapper
  4. 运行以下命令启动MySQL。systemctl start mysqld
  5. 依次运行以下命令设置开机启动MySQL。systemctl enable mysqldsystemctl daemon-reload

步骤四:安装PHP

  1. 更新YUM源。
    1. 运行以下命令添加epel源。yum install \https://repo.ius.io/ius-release-el7.rpm \https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    2. 运行以下命令添加Webtatic源。rpm -ivh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
  2. 运行以下命令安装PHP。yum -y install php71w-devel php71w.x86_64 php71w-cli.x86_64 php71w-common.x86_64 php71w-gd.x86_64 php71w-ldap.x86_64 php71w-mbstring.x86_64 php71w-mcrypt.x86_64 php71w-pdo.x86_64 php71w-mysqlnd php71w-fpm php71w-opcache php71w-pecl-redis php71w-pecl-mongodb
  3. 运行以下命令查看PHP版本。php -v返回结果如下所示,表示安装成功。PHP 7.0.33 (cli) (built: Dec 6 2018 22:30:44) ( NTS )Copyright (c) 1997-2017 The PHP GroupZend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies with Zend OPcache v7.0.33, Copyright (c) 1999-2017, by Zend Technologies

步骤五:配置Nginx

  1. 运行以下命令备份Nginx配置文件。cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
  2. 修改Nginx配置文件,添加Nginx对PHP的支持。说明 若不添加此配置信息,后续您使用浏览器访问PHP页面时,页面将无法显示。
    1. 运行以下命令打开Nginx配置文件。vim /etc/nginx/nginx.conf
    2. 按i进入编辑模式。
    3. server大括号内,修改或添加下列配置信息。除下面提及的需要添加或修改的配置信息外,其他配置保持默认值即可。
      • 添加或修改location /配置信息。 location / { index index.php index.html index.htm; }
      • 添加或修改location ~ .php$配置信息。 #添加下列信息,配置Nginx通过fastcgi方式处理您的PHP请求。 location ~ .php$ { root /usr/share/nginx/html; #将/usr/share/nginx/html替换为您的网站根目录,本文使用/usr/share/nginx/html作为网站根目录。 fastcgi_pass 127.0.0.1:9000; #Nginx通过本机的9000端口将PHP请求转发给PHP-FPM进行处理。 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; #Nginx调用fastcgi接口处理PHP请求。 }
      添加或修改配置信息后,文件内容如下图所示:nginx配置文件
    4. 按下Esc键后,输入:wq并回车以保存关闭配置文件。
  3. 运行以下命令启动Nginx服务。systemctl start nginx
  4. 运行以下命令设置Nginx服务开机自启动。systemctl enable nginx

步骤六:配置MySQL

  1. 运行以下命令查看/var/log/mysqld.log文件,获取并记录root用户的初始密码。grep 'temporary password' /var/log/mysqld.log命令行返回结果如下,其中ARQTRy3+n8*W为MySQL的初始密码。在下一步重置root用户密码时,会使用该初始密码。2021-11-10T07:01:26.595215Z 1 [Note] A temporary password is generated for root@localhost: ARQTRy3+n8*W
  2. 运行以下命令配置MySQL的安全性。mysql_secure_installation
    1. 输入MySQL的初始密码。说明 在输入密码时,系统为了最大限度的保证数据安全,命令行将不做任何回显。您只需要输入正确的密码信息,然后按Enter键即可。Securing the MySQL server deployment. Enter password for user root: #输入上一步获取的root用户初始密码
    2. 为MySQL设置新密码。The existing password for the user account root has expired. Please set a new password. New password: #输入新密码。长度为8至30个字符,必须同时包含大小写英文字母、数字和特殊符号。特殊符号包含()` ~!@#$%^&*-+=|{}[]:;‘<>,.?/ Re-enter new password: #确认新密码。The 'validate_password' plugin is installed on the server.The subsequent steps will run with the existing configurationof the plugin.Using existing password for root. Estimated strength of the password: 100 #返回结果包含您设置的密码强度。Change the password for root ? ((Press y|Y for Yes, any other key for No) :Y #您需要输入Y以确认使用新密码。 #新密码设置完成后,需要再次验证新密码。New password:#再次输入新密码。 Re-enter new password:#再次确认新密码。 Estimated strength of the password: 100Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) :Y #您需要输入Y,再次确认使用新密码。
    3. 输入Y删除匿名用户。Remove anonymous users? (Press y|Y for Yes, any other key for No) :YSuccess.
    4. 输入Y禁止使用root用户远程登录MySQL。Disallow root login remotely? (Press y|Y for Yes, any other key for No) :YSuccess.
    5. 输入Y删除test库以及用户对test库的访问权限。Remove test database and access to it? (Press y|Y for Yes, any other key for No) :Y - Dropping test database...Success. - Removing privileges on test database...Success.
    6. 输入Y重新加载授权表。Reload privilege tables now? (Press y|Y for Yes, any other key for No) :YSuccess. All done!

步骤七:配置PHP

  1. 新建并编辑phpinfo.php文件,用于展示PHP信息。
    1. 运行以下命令新建phpinfo.php文件。vim <网站根目录>/phpinfo.php<网站根目录>是您在nginx.conf配置文件中location ~ .php$大括号内,配置的root参数值,如下图所示。网站根目录本文配置的网站根目录为/usr/share/nginx/html,因此需要运行以下命令新建phpinfo.php文件:vim /usr/share/nginx/html/phpinfo.php
    2. 按i进入编辑模式。
    3. 输入下列内容,函数phpinfo()​会展示PHP的所有配置信息。<?php echo phpinfo(); ?>
    4. 按Esc键后,输入:wq并回车,保存关闭配置文件。
  2. 运行以下命令启动PHP-FPM。systemctl start php-fpm
  3. 运行以下命令设置PHP-FPM开机自启动。systemctl enable php-fpm

步骤八:测试访问LNMP配置信息页面

  1. 在本地Windows主机或其他具有公网访问能力的Windows主机中,打开浏览器。
  2. 在浏览器的地址栏输入http://<ECS实例公网IP地址>/phpinfo.php进行访问。访问结果如下图所示,表示LNMP环境部署成功。php结果

后续步骤

测试访问LNMP配置信息页面后,建议您运行以下命令将phpinfo.php文件删除,消除数据泄露风险。

rm -rf <网站根目录>/phpinfo.php

其中,<网站根目录>需要替换为您在nginx.conf中配置的网站根目录。本文配置的网站根目录为/usr/share/nginx/html,因此需要运行以下命令:

rm -rf /usr/share/nginx/html/phpinfo.php

常见问题

如何使用其他版本的Nginx服务器?

  1. 使用浏览器访问Nginx开源社区获取对应的Nginx版本的下载链接。请根据您的个人需求,选择对应的Nginx版本。本章节以Nginx 1.8.1为例。
  2. 远程连接需要部署LNMP环境的ECS实例。
  3. 运行wget命令下载Nginx 1.8.1。您可以通过Nginx开源社区直接获取对应版本的安装包URL,然后通过wget URL的方式将Nginx安装包下载至ECS实例。例如,Nginx 1.8.1的下载命令如下:wget http://nginx.org/download/nginx-1.8.1.tar.gz
  4. 运行以下命令,安装Nginx相关依赖。yum install -y gcc-c++yum install -y pcre pcre-develyum install -y zlib zlib-develyum install -y openssl openssl-devel
  5. 运行以下命令,解压Nginx 1.8.1安装包,然后进入Nginx所在的文件夹。tar zxvf nginx-1.8.1.tar.gzcd nginx-1.8.1
  6. 依次运行以下命令,编译源码。./configure \ --user=nobody \ --group=nobody \ --prefix=/usr/local/nginx \ --with-http_stub_status_module \ --with-http_gzip_static_module \ --with-http_realip_module \ --with-http_sub_module \ --with-http_ssl_modulemake && make install
  7. 运行以下命令,进入Nginx的sbin目录,然后启动Nginx。cd /usr/local/nginx/sbin/./nginx
  8. 在本地主机中,使用浏览器访问ECS实例公网IP。出现如下图所示的页面,表示Nginx已成功安装并启动。nginx

vim查找与替换

 VIM中常用的替换模式总结。

1,简单替换表达式

替换命令可以在全文中用一个单词替换另一个单词:

:%s/four/4/g

“%” 范围前缀表示在所有行中执行替换。最后的 “g” 标记表示替换行中的所有匹配点。如果仅仅对当前行进行操作,那么只要去掉%即可

  如果你有一个象 “thirtyfour” 这样的单词,上面的命令会出错。这种情况下,这个单词会被替换成”thirty4″。要解决这个问题,用 “\<” 来指定匹配单词开头:

       :%s/\<four/4/g

显然,这样在处理 “fourty” 的时候还是会出错。用 “\>” 来解决这个问题:

       :%s/\<four\>/4/g

如果你在编码,你可能只想替换注释中的 “four”,而保留代码中的。由于这很难指定,可以在替换命令中加一个 “c” 标记,这样,Vim 会在每次替换前提示你:

       :%s/\<four\>/4/gc

2,删除多余的空格

要删除这些每行后面多余的空格,可以执行如下命令:

       :%s/\s\+$//

命令前面指明范围是 “%”,所以这会作用于整个文件。”substitute” 命令的匹配模式是

“\s\+$”。这表示行末($)前的一个或者多个(\+)空格(\s)。替换命令的 “to” 部分是空的:”//”。这样就会删除那些匹配的空白字符。

3,匹配重复性模式

星号项 “*” 规定在它前面的项可以重复任意次。因此:

       /a*

匹 配 “a”,”aa”,”aaa”,等等。但也匹配 “” (空字串),因为零次也包含在内。星号 “*” 仅仅应用于那个紧邻在它前面的项。因此 “ab*” 匹配 “a”,”ab”,”abb”,”abbb”,等等。如要多次重复整个字符串,那么该字符串必须被组成一个项。组成一项的方法就是在它前面加 “\(“,后面加 “\)”。因此这个命令:

       /\(ab\)*

匹配: “ab”,”abab”,”ababab”,等等。而且也匹配 “”。

要避免匹配空字串,使用 “\+”。这表示前面一项可以被匹配一次或多次。

       /ab\+

匹配 “ab”,”abb”,”abbb”,等等。它不匹配 后面没有跟随 “b” 的 “a”。

要匹配一个可选项,用 “\=”。 例如:

       /folders\=

匹配 “folder” 和 “folders”。

4,指定重复次数

要匹配某一项的特定次数重复,使用 “\{n,m}” 这样的形式。其中 “n” 和 “m” 都是数字。在它前面的那个项将被重复 “n” 到 “m” 次 (|inclusive| 包含 “n” 和 “m”)。例如:

       /ab\{3,5}

匹配 “abbb”,”abbbb” 以及 “abbbbb”。

  当 “n” 省略时,被默认为零。当 “m” 省略时,被默认为无限大。当 “,m” 省略时,就表示重复正好 “n” 次。例如:

       模式 匹配次数

       \{,4} 0,1,2,3 或 4

       \{3,} 3,4,5,等等

       \{0,1} 0 或 1,同 \=

       \{0,} 0 或 更多,同 *

       \{1,} 1 或 更多,同 \+

       \{3} 3

5,多选一匹配

在一个查找模式中,”或” 运算符是 “\|”。例如:

       /foo\|bar

这个命令匹配了 “foo” 或 “bar”。更多的抉择可以连在后面:

       /one\|two\|three

匹配 “one”,”two” 或 “three”。

  如要匹配其多次重复,那么整个抉择结构须置于 “\(” 和 “\)” 之间:

       /\(foo\|bar\)\+

这个命令匹配 “foo”,”foobar”,”foofoo”,”barfoobar”,等等。

  再举个例子:

       /end\(if\|while\|for\)

这个命令匹配 “endif”,”endwhile” 和 “endfor”。

Centos 配置eth0 提示Device does not seem to be present

一.故障现象:[root@c1node01 ~]# service network restartShutting down loopback insterface:                                                       [   OK  ]Bringing up loopback insterface:                                                            [   OK  ]Bringing up interface eth0:  Device eth0 does not seem to be present,delaying initialization.                    [FAILED]解决办法:[root@c1node01 ~]# rm -rf /etc/udev/rules.d/70-persistent-net.rules[root@c1node01 ~]# reboot ………………[root@c1node01 ~]# service network restartShutting down loopback insterface:                                                         [   OK   ]Bringing up loopback insterface:                                                              [   OK   ]Bringing up interface eth0:                                                                     [   OK   ][root@c1node01 ~]# 二.另一种方法造成这样的原因,是因为在虚拟机(Vmware)中移动了Centos系统对应的文件,导致重新配置时,网卡的MAC地址变了,输入ifconfig -a,找不到eth0······· 安装完一个centos虚拟机,又拷贝一份,开机后网卡无法正常启动,报错:Device eth0 does not seem to be present, 
delaying initialization

解决:# mv /etc/sysconfig/network-scripts/ifcfg-eth0 
sysconfig/network-scripts/ifcfg-eth1

vim 
sysconfig/network-scripts/ifcfg-eth1

修改DEVICE=”eth0″ 
为DEVICE=”eth1″

然后重启启动网卡尝试下 三.比第一种更深入一点

故障前的操作:

DELL刀片装的是CentOS6.3的操作系统,网卡识别的是em1和em2,由于工作需要做了槽位调整,并启动了刀片

故障现象:

启动后网络不通,通过iDRAC登录后route查看缺省路由正常;

重启网络服务:

[root@nodeA ~]# service network restart Shutting down loopback insterface: [ OK ] Bringing up loopback insterface: [ OK ] Bringing up interface em1: Device em1 does not seem to be present,delaying initialization. [FAILED]

分析问题和解决:

之前在别的文章中我们提过70-persistent-net.rules文件,所以看了一下:

[root@nodeA ~]# vi /etc/udev/rules.d/70-persistent-net.rules

# This file was automatically generated by the /lib/udev/write_net_rules # program, run by the persistent-net-generator.rules rules file. # # You can modify it, as long as you keep each rule on a single # line, and change only the value of the NAME= key.

# PCI device 0x14e4:0x163a (bnx2) SUBSYSTEM==”net”, ACTION==”add”, DRIVERS==”?*”, ATTR{address}==”24:b6:fd:ab:76:1e”, ATTR{type}==”1″, KERNEL==”eth*”, NAME=”eth1″

# PCI device 0x14e4:0x163a (bnx2) SUBSYSTEM==”net”, ACTION==”add”, DRIVERS==”?*”, ATTR{address}==”24:b6:fd:ab:76:1c”, ATTR{type}==”1″, KERNEL==”eth*”, NAME=”eth0″

发现NAME的名称不正确,依次将上述红色字体中的eth0改为em1,eth1改为em2;

*切记:网卡编号由MAC地址大小决定,MAC越小网卡编号越小;

如下:

…………

# PCI device 0x14e4:0x163a (bnx2)

SUBSYSTEM==”net”, ACTION==”add”, DRIVERS==”?*”, ATTR{address}==”24:b6:fd:ab:76:1e”, ATTR{type}==”1″, KERNEL==”eth*”, NAME=”em2″

# PCI device 0x14e4:0x163a (bnx2) SUBSYSTEM==”net”, ACTION==”add”, DRIVERS==”?*”, ATTR{address}==”24:b6:fd:ab:76:1c”, ATTR{type}==”1″, KERNEL==”eth*”, NAME=”em1″

保存退出~!

[root@nodeA ~]#reboot

从Git仓库中恢复已删除的分支、文件或丢失的commit

在使用Git的过程中,有时可能会有一些误操作比如:执行checkout -f 或 reset -hard 或 branch -d删除一个分支结果造成本地(远程)的分支或某些commit丢失可以通过reflog来进行恢复,前提是丢失的分支或commit信息没有被git gc清除
一般情况下,gc对那些无用的object会保留很长时间后才清除的reflog是git提供的一个内部工具,用于记录对git仓库进行的各种操作可以使用git reflog show或git log -g命令来看到所有的操作日志
恢复的过程很简单:
1. 通过git log -g命令来找到我们需要恢复的信息对应的commit_id,可以通过提交的时间和日期来辨别。一个好的办法是运行:1、git log –since=”2 weeks ago” — myfile 可以2个星期期间的myfile历史;2、git log –branches=”develop” 可以查看develop的commit
2. 通过git branch recover_branch[新分支] commit_id 来建立一个新的分支这样,我们就把丢失的东西给恢复到了recover_branch分支上了。
Q:如果是不小心执行了git reset,还有办法取消吗?A:git reflog 查看操作历史,找到之前 HEAD 的 hash 值,然后 git reset –hard 到那个 hash 即可。
Q:怎样找回历史版本中删除的文件?A:先确定需要恢复的文件要恢复成哪一个历史版本(commit),假设那个版本号是: commit_id,那么git checkout [commit_id] — <path_to_file>就可以恢复。

git常用命令

删除分支     本地:git branch -d branch_name     远程:git push origin —delete branch_name 

https方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受https带来的极速

设置记住密码(默认15分钟):

git config --global credential.helper cache

如果想自己设置时间,可以这样做:

git config credential.helper 'cache --timeout=3600'

这样就设置一个小时之后失效

长期存储密码:

git config --global credential.helper store

增加远程地址的时候带上密码也是可以的。(推荐)

http://yourname:password@git.oschina.net/name/project.git

补充:使用客户端也可以存储密码的。

如果你正在使用ssh而且想体验https带来的高速,那么你可以这样做: 切换到项目目录下 :

cd projectfile/

移除远程ssh方式的仓库地址

git remote rm origin

增加https远程仓库地址

git remote add origin http://yourname:password@git.oschina.net/name/project.git

Linux常用命令

linux下快速跳转到所需目录

    $ vi my_path

cd 要切换到的目录 

    $ . my_path

替换内容

    sed -i ‘s/原来内容/新内容/g’ 文件地址 这样这个文件从头到尾,全部内容就更换了。

    如果替换的内容有空格之类的,加上双引号””屏蔽掉,或者用转义字符\

    find -name ‘要查找的文件名’ | xargs perl -pi -e ‘s|被替换的字符串|替换后的字符串|g’
      空格、制表符      sed ‘s/[[:space:]][[:space:]]*/,/g’

替换文件内容

    sed -i ‘s///g’ db_*

基于http方式跨平台的文件共享

    python -m SimpleHTTPServer 8000
 查找:  find / -name supervisor*

下面的命令可以查看 ‘cache’ 认证助手的所有配置: 
 LC_ALL=zh_CN
SCP:     scp  -P 60066 -r /file  dev@:/home/dest/        scp -r root@123.56.90.178:/usr/local/app/mysql .

php curl 获取数据不完整

curl获取数据的时候,结果的字符串长度比较大。 相同的结果每次获取的数据都不全,并且长度也不一样。

试着把 HEADER信息修改为except: 但还是不行(这个可以解决的问题是数据量太大导致获取结果为空的情况)。

去掉

CURLOPT_RETURNTRANSFER = true
可以打印出完整数据

解决方案:

修改数据源服务器的nginx缓存配置

fastcgi_buffers 由原来的 8128k修改到81M

以下引自https://segmentfault.com/a/1190000007513677

Nginx的buffer机制,对于来自 FastCGI Server 的 Response,Nginx 将其缓冲到内存中,然后依次发送到客户端浏览器。缓冲区的大小由 fastcgi_buffers 和 fastcgi_buffer_size 两个值控制。
比如如下配置:
fastcgi_buffers 8 4K;
fastcgi_buffer_size 4K;
fastcgi_buffers 控制 nginx 最多创建 8 个大小为 4K 的缓冲区,而 fastcgi_buffer_size 则是处理 Response 时第一个缓冲区的大小,不包含在前者中。所以总计能创建的最大内存缓冲区大小是 84K+4K = 36k。而这些缓冲区是根据实际的 Response 大小动态生成的,并不是一次性创建的。比如一个 8K 的页面,Nginx 会创建 24K 共 2 个 buffers。
当 Response 小于等于 36k 时,所有数据当然全部在内存中处理。如果 Response 大于 36k 呢?fastcgi_temp 的作用就在于此。多出来的数据会被临时写入到文件中,放在这个目录下面。
内存中缓冲了 36Kb,剩下的会写入的文件中。而实际的情况是,运行 Nginx Process 的用户并没有 fastcgi_temp 目录的写权限,于是剩下的数据就丢失掉了。
————————————————
版权声明:本文为CSDN博主「冰峰雪岭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/glovenone/article/details/72650133