一、显示信息的命令
console.log("normal"); // 用于输出普通信息 console.info("information"); // 用于输出提示性信息 console.error("error"); // 用于输出错误信息 console.warn("warn"); // 用于输出警示信息
二、点位符:字符(%s)、整数(%d或%i)、浮点数(%f)和对象(%o);
console.log("%s","string"); //字符(%s) console.log("%d年%d月%d日",2016,8,29); //整数(%d或%i) console.log("圆周率是%f",3.1415926); //浮点数(%f) var dog = {}; dog.name = "大毛"; dog.color = "黄色"; dog.sex = "母狗"; console.log("%o",dog); //对象(%o)
三、信息分组 (console.group(),console.groupEnd())
console.group("第一组信息"); console.log("第一组第一条:我的博客"); console.log("第一组第二条:CSDN"); console.groupEnd(); console.group("第二组信息"); console.log("第二组第一条:程序爱好者QQ群"); console.log("第二组第二条:欢迎你加入"); console.groupEnd();
四、将对象以树状结构展现 (console.dir()可以显示一个对象所有的属性和方法)
var info = { name : "Alan", age : "27", grilFriend : "nothing", getName : function(){ return this.name; } } console.dir(info);
五、显示某个节点的内容 (console.dirxml()用来显示网页的某个节点(node)所包含的html/xml代码)
var node = document.getElementById("info"); node.innerHTML += "<p>追加的元素显示吗</p>"; console.dirxml(node);
六、判断变量是否是真 (console.assert()用来判断一个表达式或变量是否为真,只有表达式为false时,才输出一条相应作息,并且抛出一个异常)
var testObj = false; console.assert(testObj, '当testObj为false时才输出!');
七、计时功能 (console.time()和console.timeEnd(),用来显示代码的运行时间)
console.time("控制台计时器"); for(var i = 0; i < 10000; i++){ for(var j = 0; j < 10000; j++){} } console.timeEnd("控制台计时器");
八、性能分析performance profile (就是分析程序各个部分的运行时间,找出瓶颈所在,使用的方法是console.profile()和console.proileEnd();)
function All(){ // alert(11); for(var i = 0; i < 10; i++){ funcA(100); } funcB(1000); } function funcA(count){ for(var i = 0; i < count; i++){}; } function funcB(count){ for(var i = 0; i < count; i++){}; } console.profile("性能分析器"); All(); console.profileEnd();
详细的信息在chrome控制台里的"profile"选项里查看
九、console.count()统计代码被执行的次数
function myFunction(){ console.count("myFunction 被执行的次数"); } myFunction(); //myFunction 被执行的次数: 1 myFunction(); //myFunction 被执行的次数: 2 myFunction(); //myFunction 被执行的次数: 3
十、keys和values,要在浏览器里输入
十一、console.table表格显示方法
var mytable = [ { name: "Alan", sex : "man", age : "27" }, { name: "Wu", sex : "gril", age : "28" }, { name: "Tao", sex : "man and gril", age : "29" } ] console.table(mytable);
十二、Chrome 控制台中原生支持类jQuery的选择器,也就是说你可以用$加上熟悉的css选择器来选择DOM节。
$("body"); //选择body节点
十三、copy通过此命令可以将在控制台获取到的内容复制到剪贴板
copy(document.body); //复制body copy(document.getElementById("info")); //复制某id元素的的节点
十四、$_命令返回最近一次表达式执行的结果,$0-$4代表了最近5个你选择过的DOM节点
十五、利用控制台输出文字,图片,以%c开头,后面的文字就打印的信息,后面一个参数就是样式属性;
console.log("请在邮件中注明%c 来自:console","font-size:16px;color:red;font-weight:bold;");
这是一个数据可视化项目,基于D3.js。能够将历史数据排名转化为动态柱状图图表。
先来看看效果:
https://xoyozo.net/Demo/BarGraph
作者 Jannchie 见齐还提供了官方视频教程:
https://www.bilibili.com/video/av28087807
不过由于开源项目的不断更新,该教程的部分内容已失效,本文针对 2018-12-25 版本总结了一些常用的配置说明,仅供参考。
从 GitHub 下载源代码,在 src 目录中可以看到 4 个文件:
bargraph.html --> 运行示例页面
config.js --> 配置文件
stylesheet.css --> 样式表
visual.js --> 核心文件
作者并没有将代码封装为插件的方式,所以我们是通过修改 config.js 配置文件的方式应用到自己的项目中的。
visual.js 虽然是核心文件,但作者将部分示例中的代码也包含其中,但并不影响我们直接在自己的项目中引用。
以下是 config.js 中的主要属性:
属性 | 说明 | 参考值 |
encoding | 数据源(csv、json)等的文件编码 | GBK / UTF-8 等 |
max_number | 每个时间节点最多显示的条目数 | 10 |
showMessage | 控制是否显示顶部附加信息文字 | true / false |
auto_sort | 时间自动排序(详细含义、作用及限制见代码中注释) | true / false |
timeFormat | 时间格式,显示于图表右下角的时间 | %Y、%Y-%M-%D 等 |
reverse | 是否倒序 | true / false |
divide_by | 类型根据什么字段区分?如果是 name,则关闭类型显示 | |
divide_color_by | 颜色根据什么字段区分? 须要注意的是,如果配置成 name,则各条颜色不同(因为 name 值各异),如果配置成 type 等,那么相同 type 值的条颜色相同。 | 字段名 |
color | 指定部分或所有条的颜色,该项与 divide_color_by 设置有关。 | 以 divide_color_by = 'name' 为例,"中国"是其中的一项 name 值,那么该项将显示 #D62728 色: color: { '中国': '#D62728' } |
changeable_color | 若 true 则颜色的深浅将根据数据的增长率实时改变 | |
itemLabel | 左边文字 | |
typeLabel | 右边文字 | |
item_x | ||
interval_time | 时间点间隔时间 | 2 |
text_y | ||
text_x | ||
offset | ||
display_barInfo | 如果希望不显示,则可以设置较大的值,单位像素 | |
use_counter | ||
step | ||
format | 格式化数值 | |
left_margin | ||
right_margin | ||
top_margin | ||
bottom_margin | ||
dateLabel_x | ||
dateLabel_y | ||
allow_up | ||
enter_from_0 | ||
big_value | ||
use_semilogarithmic_coordinate | ||
long | ||
wait | 数据加载完成后开始播放前的等待时间 | 0 |
update_rate |
当我们用 ASP.NET Web API 作服务端接口时,如果客户端以 GET 方式请求的 URL 中,由于拼接错误导致“?”和“&”并列出现,会发生 400 Bad Request,那么需要在 IIS 中使用 URL 重写模块来纠正。
请求 URL 如:
http://www.abc.com/api/obj?&foo=bar
需要重写为:
http://www.abc.com/api/obj?foo=bar
打开 IIS 管理器中对应网站的 URL 重写,
添加空白规则。
匹配的 URL 不包含域名,如上述 URL 则匹配的范围是:
api/obj
不包含我们要查找的字符串 ?&,所以此处模式只能匹配所有:
.*
又因为重定向路径中需要包含这部分,所以用括号包裹,即:
(.*)
继续添加条件匹配:
条件输入:{QUERY_STRING},表示匹配查询字符串,
匹配的部分是“?”后面的字符串:
&foo=bar
那么可以填写模式:
^\&.*
因为 & 后面的部分仍然需要使用,所以用括号包裹:
^\&(.*)
“操作”中选择类型“重定向”,URL 填写:
{R:1}?{C:1}
其中 {R:1} 指代 api/obj,{C:1} 指代 foo=bar
取消“附加查询字符串”前面的勾
重定向类型在测试时可以选择 302 或 307,没问题后选择 301。
完成。
最终我们可以在网站的配置文件 web.config 中看到:
<rewrite>
<rules>
<rule name="uni-app-h5-replace-q" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{QUERY_STRING}" pattern="^\&(.*)" />
</conditions>
<action type="Redirect" url="{R:1}?{C:1}" appendQueryString="false" redirectType="Temporary" />
</rule>
</rules>
</rewrite>
我们要把这段代码复制到开发环境源代码的 web.config 中,以免发布网站时将我们设置好的配置覆盖掉。
uni-app 的 input,当 type = "text" 时,设置 confirm-type = "next" 可以提供键盘的“下一项”,通过 @confirm 触发设置下一个文本框的焦点,即可实现只操作键盘即可完成表单填写。
我们以从 a 组件跳转到 b 组件为例:
<input v-model="a" type="text" confirm-type="next" @confirm="moveNext('b')" />
uni-app 的组件只有属性和事件,不提供方法,那么我们只能设置提供了 focus 属性的组件的焦点。
<input v-model="b" :focus="focusList['b']" />
moveNext() 方法实现:
moveNext(dom){
this.focusList[dom] = true;
}
当然 focusList 必须先定义并初始化,否则设置 focus 为 true 将失效:
data() {
return {
a: '',
b: '',
focusList: {
b: false
}
}
}
几个注意点:
confirm-type = "next" 仅支持微信小程序
“下一项”按钮效果是一次性的,使用一次后再次在 a 上触摸“下一项”将不再跳转到 b,即使在 moveNext() 中先设置为 false 再设置为 true 也没用
其它组件也可以通过自带的方法实现跳转到带 focus 属性的组件(如在 picker 的 @change 中设置 this.focusList['b'] = true)
限制于 confirm-type = "next" 只当 type = "text" 时有效,因此非文本键盘(如数字键盘)就不能提供“下一项”功能
textarea 组件的右下角始终是“换行”,由其右上角的“完成”按钮来实现“下一项”功能
textarea 组件在“下一项”跳转过程中,组件可能会被 tabBar 遮挡(几率很大)
〓 系统
功能 | 命令 | --help | 示例 |
---|---|---|---|
关机 | halt | halt | |
重启 | reboot | reboot | |
系统监视器 | top | 系统时间, 运行天数, 当前登录用户数, 系统负载 总进程数, 运行中的, 睡眠的, 停止的, 未响应的 Cpu(s):us 用户, sy 系统, ni XX, id 空闲, wa 等待, hi XX, si XX Mem, 已使用, 空余, 缓冲 Swap, 已使用, 空余, 缓冲 快捷键: M 按占内存排序 P 按占Cpu排序 1 显示每个 Cpu k 杀死进程 q 退出 | top |
查看进程 | ps | aux -ef | 列出包含 java 的进程 ps aux |grep java ps -ef |grep java |
查看内存及 Swap 用量 | free | -b,-k,-m,-g 按单位显示 | free -m |
查看系统时间 | date | 显示 CST 时间 -R 显示时区 -u 显示 UTC 时间 | date |
查看硬件时间 | clock | clock | |
设置系统日期 | date -s 月/日/年 | ||
设置系统时间 | date -s 时:分:秒 | ||
将系统时间写入到硬件时间 | clock -w | ||
查看系统版本 | cat /etc/*release | ||
升级系统软件 | yum update -y |
〓 文件
功能 | 命令 | --help | 示例 |
---|---|---|---|
进入目录 | cd | cd .. # 上一层目录 cd /root # 根目录 | |
列出目录 | ls | 白色:表示普通文件 蓝色:表示目录 绿色:表示可执行文件 红色:表示压缩文件 浅蓝色:链接文件 红色闪烁:表示链接的文件有问题 黄色:表示设备文件 灰色:表示其他文件 | ls |
创建目录 | mkdir | mkdir XXX | |
删除目录 | rm | rm -rf XXX | |
删除文件 | rm | rm XXX | |
复制文件 | cp | cp XXX YYY | |
复制目录 | cp | -r 复制目录及目录内的所有项目 -v 详细显示进行的步骤 | cp -rv XXX YYY |
重命名文件 | mv | -i: 若指定目录已有同名文件,则先询问是否覆盖旧文件; -f: 在mv操作要覆盖某已有的目标文件时不给任何指示; | mv 源文件 目标文件 |
移动文件 | mv | mv 一个或多个文件 目标目录 | |
下载文件 | wget | 下载到当前目录 | wget http://XXX.tar.gz |
计算文件/目录的磁盘用量 | du | -a 不仅显示目录,同时显示文件 -h 容易阅读方式显示 --max-depth=N 可指定计算深度 | du -ah --max-depth=1 | sort -n |
查找文件 | find | find /home -name *.apk |
〓 tar
功能 | 命令 | --help | 示例 |
---|---|---|---|
tar | tar | -z 是否压缩 -c 打包 -x 解包 -v 详细地列出处理的文件 -f | 打包:tar -cvf abc.tar abc 解包:tar -xvf abc.tar 压缩打包:tar -zcvf abc.tar.gz abc 解压解包:tar -zxvf abc.tar.gz |
〓 磁盘
功能 | 命令 | --help | 示例 |
---|---|---|---|
查看所有磁盘及分区 | fdisk -l | fdisk -l | |
查看当前挂载 | df | -h 按可阅读的方式打印数值和单位 -T 显示文件系统类型 | df -hT |
管理磁盘分区 | fdisk /dev/*** | 进入后的操作说明: m 显示命令菜单 d 删除一个分区 n 创建一个分区(e 扩展分区;p 主分区) t 改变分区ID q 不保存退出 w 保存退出 | fdisk /dev/vdb |
格式化分区 | mkfs.*** /dev/***N | mkfs.xfs /dev/vdb1 | |
挂载分区 | mount /dev/***N /*** | mount /dev/vdb1 /www | |
卸载分区 | umount /dev/***N | umount /dev/vdb1 | |
开机自动挂载 | vi /etc/fstab | 配置文档格式:设备 挂载点 文件系统类型 defaults 0 0 | 打开:vi /etc/fstab 配置:/dev/vdb1 /www xfs defaults 0 0 |
〓 网络
功能 | 命令 | --help | 示例 |
---|---|---|---|
查看 IP 配置 | ifconfig | ifconfig | |
配置网卡 IP | 配置文件目录:/etc/sysconfig/network-scripts/ 配置文件格式: DEVICE=eth0 / eth0:0 / ... # 在配置多线时若使用 cp 命令复制配置文件,必须修改此项以防止冲突 HWADDR=XX:XX:XX:XX:XX:XX # 网卡地址 TYPE=Ethernet # 以太网 UUID=******** ONBOOT=yes # 开机启动 NM_CONTROLLED=yes BOOTPROTO=static # 使用静态 IP IPADDR=192.168.1.2 # IP 地址 NETMASK=255.255.255.XXX # 子网掩码 GATEWAY=192.168.1.1 # 网关 DNS1=114.114.114.114 DNS2=8.8.8.8 | vi ifcfg-XXXN(:N) | |
重启网卡 | 使配置生效 | service network restart |
〓 防火墙
功能 | 命令 | --help | 示例 |
---|---|---|---|
配置 iptables | 添加需要允许的端口的方法同 22 端口 | vi /etc/sysconfig/iptables | |
重启使配置生效 | service iptables restart |
〓 用户/权限
功能 | 命令 | --help | 示例 |
---|---|---|---|
添加用户 | useradd | -g 组名 # 加入到该组 -s /bin/false #不允用户直接登录系统 | useradd –g 组名 用户名 -s /bin/false |
修改密码 | passwd | passwd 用户 | |
查看所有用户 | cut -d : -f 1 /etc/passwd | ||
查看可以登录系统的用户 | cat /etc/passwd | grep -v /sbin/nologin | cut -d : -f 1 | ||
删除用户 | -r, --remove remove home directory and mail spool | userdel 用户 | |
添加用户组 | groupadd | groupadd 组名 | |
为组添加用户(用户必须已存在) | gpasswd | gpasswd -a 用户 组 | |
将用户移出组 | gpasswd | gpasswd -d 用户 组 | |
查看用户所属组 | groups | groups 用户 | |
查看组中有哪些用户 | groupmems | groupmems -g 组 -l | |
更改文件/目录所有者 | chown | -R 递归处理所有的文件及子目录 | chown -R 用户:组 *** |
更改文件/目录权限 | chmod | -R 以递归方式更改所有的文件及子目录 | chmod -R 777 *** |
〓 vi 编辑器
功能 | 命令 | --help | 示例 |
---|---|---|---|
打开文件 | vi | vi XXX | |
进入编辑模式 | 按 a/i/o/Insert 等 | ||
进入末行模式/命令模式 | 按 Esc后: :w 保存不退出 :q 退出(提示是否保存) :wq 保存并退出 :w XXX 另存到文件 XXX :q! 不保存退出 |
〓 计划任务
功能 | 命令 | --help | 示例 |
---|---|---|---|
设置计划任务 | crontab | 详细步骤见本页底部 | crontab -l # 查看计划任务 crontab -e # 编辑计划任务 |
〓 网站
功能 | 命令 | --help | 示例 |
---|---|---|---|
简单审查日志 | cat | grep | cat 日志文件 | grep 关键词1 | grep 关键词2 | more | |
日志分析 | goaccess | 见下文 |
〓 goaccess
功能 | 命令 | --help | 示例 |
---|---|---|---|
安装 | yum install goaccess | ||
日志格式 | NCSA Commbined Log Format | date_format %d/%b/%Y log_format %h %^[%d:%^] "%r" %s %b "%R" "%u" | |
参数 | -f | 需要解析的日志文件 | |
参数 | -e | 指定 IP 地址统计 | |
参数 | -p | 指定配置文件 | 可以将上面的日志格式内容保存到文件 ~/.goacessrc |
参数 | -H | 显示 HTTP 协议信息 | |
参数 | -M | 显示 HTTP 方法信息 | |
生成文件 | goaccess -f 日志文件 -p ~/.goaccessrc > 目标文件.htm |
〓 lnmp
功能 | 命令 | --help | 示例 |
---|---|---|---|
重启 LNMP | /root/lnmp restart | ||
重启 MySQL | /etc/init.d/mysql restart | ||
重启 PureFTPd | /root/pureftpd restart | ||
安装 LNMP | http://lnmp.org/install.html | ||
常见问题 | http://lnmp.org/faq.html | ||
状态管理命令 | http://lnmp.org/faq/lnmp-status-manager.html | ||
相关软件目录及文件位置 | http://lnmp.org/faq/lnmp-software-list.html | ||
防跨站、跨目录安全设置(仅支持 PHP 5.3.3 以上版本) | http://www.vpser.net/security/lnmp-cross-site-corss-dir-security.html | ||
查看 Nginx 版本 | nginx -V | ||
查看 MySQL 版本 | mysql -V | ||
查看 PNP 版本 | php -v | ||
查看 Apache 版本 | httpd -v | ||
查内存 | cat /proc/meminfo | ||
php.ini | vim /usr/local/php/etc/php.ini | ||
MySQL 配置文件 | vim /etc/my.cnf | ||
添加网站 | /root/vhost.sh | ||
添加 ProFTPd 用户 | /root/proftpd_vhost.sh |
〓 nginx
功能 | 命令 | --help | 示例 |
---|---|---|---|
启动/停止/重启 | service nginx 或 /etc/rc.d/init.d/nginx | service nginx start service nginx stop service nginx restart | |
伪静态 | 在 .conf 文件中配置 | rewrite ^(.*)/read-htm-(.*)\.html(.*)$ $1/read.php?$2.html? last; rewrite ^(.*)/thread-htm-(.*)\.html(.*)$ $1/thread.php?$2.html? last; rewrite ^(.*)-htm-(.*)$ $1.php?$2 last; rewrite ^(.*)/simple/([a-z0-9\_]+\.html)$ $1/simple/index.php?$2 last; rewrite ^(.*)/data/(.*)\.(htm|php)$ 404.html last; rewrite ^(.*)/attachment/(.*)\.(htm|php)$ 404.html last; rewrite ^(.*)/html/(.*)\.(htm|php)$ 404.html last; | |
防盗链 | 在 .conf 文件中配置 | HttpRefererModule | location ~* \.(gif|jpg|png|swf|flv)$ { valid_referers none blocked *.0574bbs.com *.eyuyao.com 0574bbs.com eyuyao.com; if ($invalid_referer) { rewrite ^/ http://web1.eyuyao.com/yyad/src/3122.jpg; # return 404; } } |
浏览器缓存 | 在 .conf 文件中配置 | location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } |
〓 vsftpd
功能 | 命令 | --help | 示例 |
---|---|---|---|
安装 | yum install vsftpd | ||
查看是否已安装 | rpm -q vsftpd | ||
启动/停止/重启 | service vsftpd | service vsftpd start service vsftpd stop service vsftpd restart | |
配置文件 | vi /etc/vsftpd/vsftpd.conf |
〓 MySQL
功能 | 命令 | --help | 示例 |
---|---|---|---|
登录 | mysql | mysql -u username -p | |
登出 | exit | ||
查看信息 | status; | ||
查询当前正在执行的 SQL 语句 | show processlist; | ||
删除指定时间之前的日志 | PURGE | PURGE MASTER LOGS BEFORE '2015-1-1 0:00:00'; |
〓 scp 远程文件/目录传输命令 (yum install openssh-clients) 用法
scp 会把文件权限(读取/写入/执行)带过来,但所有者为当前执行 scp 命令的用户。
scp 低版本有许多漏洞,用完最好 yum remove openssh-clients
scp 采用直接覆盖的机制,如需判断文件无差异则跳过,应改用 rsync 命令。查看 rsync 详细使用方式及与 scp 对比
功能 | 命令 | --help | 示例 |
---|---|---|---|
若远程服务器 SSH 端口非默认 | scp | -P 端口号 | |
下载远程服务器上的文件到本地 | scp | scp 远程用户@远程服务器:远程文件 本地文件 | |
下载远程服务器上的目录到本地 | scp | -P 端口 -v 显示进度 -r 递归 | scp -r 远程用户@远程服务器:远程目录 本地目录 实例:scp -r root@x.x.x.x:/a/b/ /c/d/ 结果:/c/d/b/,即将整个 b 复制到 d 下(注意与 rsync 命令的区别) |
将本地文件上传到远程服务器 | scp | scp 本地文件 远程用户@远程服务器:远程文件 | |
将本地目录上传到远程服务器 | scp | 最终目录结构参:远程->本地 | scp -r 本地目录 远程用户@远程服务器:远程目录 |
〓 rsync 远程文件/目录传输命令 (yum install rsync) 用法查看 rsync 详细使用方式及与 scp 对比
rsync 会把文件权限(读取/写入/执行)带过来,所有者也会带过来。
相比于 scp 最大的优势就是可以增量同步
功能 | 命令 | --help | 示例 |
---|---|---|---|
下载远程服务器上的目录到本地 | rsync | -a 递归 -v 详细 -p, -- perms 保持权限 -g, -- group 保持属组 -o, --owner 保持属主 -r 递归 --progress 打印 --delete 删除已不存在的文件 -u 表示仅更新较新的文件 -z 表示在传输过程中进行压缩 -e 'ssh -p 2222' 指定其它端口 | rsync 远程用户@远程服务器:远程目录 本地目录 实例:rsync -avu --progress root@x.x.x.x:/a/b/ /c/d/ 结果:/c/d/,即将 b 内的文件(夹)复制到 d 下(注意与 scp 命令的区别) |
将本地文件上传到远程服务器 | rsync | rsync 本地文件 远程用户@远程服务器:远程文件 | |
将本地目录上传到远程服务器 | rsync | 最终目录结构参:远程->本地 | rsync 本地目录 远程用户@远程服务器:远程目录 |
〓 ftp 客户端 (yum install ftp)
功能 | 命令 | --help | 示例 |
---|---|---|---|
登录 | ftp | ftp 目标服务器 | |
列出远程当前路径目录/文件 | ls | ls | |
创建远程目录 | mkdir | mkdir 目录名 | |
删除远程目录(空) | rmrmdir | mkdir 目录名 | |
进入远程目录 | cd | cd 目录名 | |
显示远程当前路径 | pwd | pwd | |
重命名远程文件 | rename | rename 原文件名 新文件名 | |
上传文件 | put | put 本地文件名 | |
下载文件 | get | get 远程文件名 | |
批量下载文件 | mget | 需要单个确认 | |
批量下载文件【lftp】 | mirror | 参数有很多 | mirror |
返回 shell(不退出) | ! | ! | |
返回 ftp(接上步) | exit ftp | exit ftp | |
结束 | bye quit | bye quit |
〓 iftop
流量监控工具 教程
〓 GoAccess
实时网站日志分析工具 官网
〓 Cacti
常见问题笔记
加硬盘
插入新硬盘
若有 RAID,则先设置,使操作系统能认到硬盘
使用 fdisk 命令对新设备进行分区
使用 mkfs 命令对新分区进行格式化
使用 mount 命令进行挂载
设置开机自动挂载(vi /etc/fstab)
更改 MySQL 数据库目录位置
停止 MySQL 服务
将原数据目录转移或复制到新位置(若是复制,则修改所有者使原来一致)
找到 my.cnf 配置文件(一般在 /etc/),修改 datadir 值为新路径
启动 MySQL 服务
502 Bad Gateway 问题排查
查看 PHP 日志,路径:/usr/local/php/var/log
一般为“server reached pm.max_children setting (10), consider raising it”连接数问题,在“/usr/local/php/etc”下的所有配置文件中查找并修改相关设置即可(如改成 1000)。
计划任务(实例:定时备份数据库并通过 FTP 同步至其它服务器)
创建可执行文件:vi dotask.sh
dotask.sh 的内容示例:
DATE_TIME=`date +%Y_%m_%d_%H%M%S`;
FILE_NAME=数据库名_backup_$DATE_TIME.sql;
cd /home/mysqlbackup/;
mysqldump -u数据库用户名 -p数据库密码 数据库名>$FILE_NAME;
tar -zcf $FILE_NAME.tar.gz $FILE_NAME;
rm $FILE_NAME;
ftp -v -n FTP地址 << END
user FTP用户名 FTP密码
bin
put 本地目录文件 目标路径文件
bye
END
文件名乱码问题可以在行末加“;”来解决赋予执行权限:chmod 777 dotask.sh (ls 命令时呈绿色)
编辑计划任务:crontab -e
crontab 书写规则:
# 分 时 日 月 周 文件路径
0 3 * * * /home/dotask.sh
30 4 * * * /home/dotask2.sh重启 crond:/etc/init.d/crond restart
netstat
netstat -an | grep xxx.xxx.xxx.xxx 可查看此 IP 的 TCP 请求及端口
制作类似下图中的拖拽排序功能:
1. 首先数据库该表中添加字段 sort,类型为 double(MySQL 中为 double(0, 0))。
2. 页面输出绑定数据(以 ASP.NET MVC 控制器为例):
public ActionResult EditSort()
{
if (!zConsole.Logined) { return RedirectToAction("", "SignIn", new { redirect = Request.Url.OriginalString }); }
db_auto2018Entities db = new db_auto2018Entities();
return View(db.dt_dealer.Where(c => c.enabled).OrderBy(c => c.sort));
}
这里可以加条件列出,即示例中 enabled == true 的数据。
3. 前台页面引用 jQuery 和 jQuery UI。
4. 使用 <ul /> 列出数据:
<ul id="sortable" class="list-group gutter list-group-lg list-group-sp">
@foreach (var d in Model)
{
<li class="list-group-item" draggable="true" data-id="@d.id">
<span class="pull-left"><i class="fa fa-sort text-muted fa m-r-sm"></i> </span>
<div class="clear">
【id=@d.id】@d.name_full
</div>
</li>
}
</ul>
5. 初始化 sortable,当拖拽结束时保存次序:
<script>
var url_SaveSort = '@Url.Action("SaveSort")';
</script>
<script>
$("#sortable").sortable({
stop: function (event, ui) {
// console.log('index: ' + $(ui.item).index())
// console.log('id: ' + $(ui.item).data('id'))
// console.log('prev_id: ' + $(ui.item).prev().data('id'))
$.post(url_SaveSort, {
id: $(ui.item).data('id'),
prev_id: $(ui.item).prev().data('id')
}, function (json) {
if (json.result.success) {
// window.location.reload();
} else {
toastr["error"](json.result.info);
}
}, 'json');
}
});
$("#sortable").disableSelection();
</script>
这里回传到服务端的参数为:当前项的 id 值、拖拽后其前面一项的 prev_id 值(若移至首项则 prev_id 为 undefined)。
不使用 $(ui.item).index() 是因为,在有筛选条件的结果集中排序时,使用该索引值配合 LINQ 的 .Skip 会引起取值错误。
6. 控制器接收并保存至数据库:
[HttpPost]
public ActionResult SaveSort(int id, int? prev_id)
{
if (!zConsole.Logined)
{
return Json(new { result = new { success = false, msg = "请登录后重试!" } }, JsonRequestBehavior.AllowGet);
}
db_auto2018Entities db = new db_auto2018Entities();
dt_dealer d = db.dt_dealer.Find(id);
// 拖拽后其前项 sort 值(若无则 null)(此处不需要加 enabled 等筛选条件)
double? prev_sort = prev_id.HasValue
? db.dt_dealer.Where(c => c.id == prev_id).Select(c => c.sort).Single()
: null as double?;
// 拖拽后其后项 sort 值(若无前项则取首项作为后项)(必须强制转化为 double?,否则无后项时会返回 0,导致逻辑错误)
double? next_sort = prev_id.HasValue
? db.dt_dealer.Where(c => c.sort > prev_sort && c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault()
: db.dt_dealer.Where(c => c.id != id).OrderBy(c => c.sort).Select(c => (double?)c.sort).FirstOrDefault();
if (prev_sort.HasValue && next_sort.HasValue)
{
d.sort = (prev_sort.Value + next_sort.Value) / 2;
}
if (prev_sort == null && next_sort.HasValue)
{
d.sort = next_sort.Value - 1;
}
if (prev_sort.HasValue && next_sort == null)
{
d.sort = prev_sort.Value + 1;
}
db.SaveChanges();
return Json(new { item = new { id = d.id }, result = new { success = true } });
}
需要注意的是,当往数据库添加新项时,必须将 sort 值设置为已存在的最大 sort 值 +1 或最小 sort 值 -1。
var d = new dt_dealer
{
name_full = "新建项",
sort = (db.dt_dealer.Max(c => (double?)c.sort) ?? 0) + 1,
};
工作这几年碰到的版本检测升级的接口也算是五花八门,啥样的都有,但肯定有的功能是有个 apk 的下载链接,能间接或直接提示你是强制还是非强制更新:
间接是指提供你后台最新版本号,让你自己与本地版本号通过比较得出是否升级;
直接就是后台接口直接返回个 Boolean 类型告诉你是强制或者非强制更新。
个人认为一个好的版本检测接口需要设计的更灵活更清晰用起来更方便,下面就我理解的接口设计如下(如思路有误,欢迎指正):
总字段如下(并不是所有字段都要返回给客户端):
1.最新版本号 :newVersion
2.最小支持版本号 : minVersion
3.apk 下载 url : apkUrl
4.更新文案 : updateDescription
5.是否有更新 : isUpdate
6.是否强制更新 : forceUpdate 可选字段:
7.apk 文件大小:apkSize
8.apk 的文件 MD5 值:md5
方案一(后端处理逻辑):
在客户端请求参数中添加当前版本号 currentVersion 传输给后台,由后台根据客户端传过来的当前版本号 currentVersion 做相应的判断后给出是否强制更新。
后端逻辑如下:
如果 currentVersion < newVersion, 则 isUpdate = true;
如果 currentVersion < minVersion, 则 forceUpdate = true;
如果 currentVersion >= minVersion, 则 forceUpdate = false;
如果有特殊需求可指定某个版本必须强制更新,如 currentVersion == XXX, 则 forceUpdate = true;
如果 currentVersion == newVersion,则 isUpdate = false.
结论:
返回客户端的字段仅需要 apk 下载 url : apkUrl
、更新文案 : updateDescription
、是否有更新 : isUpdate
、 是否强制更新 : forceUpdate
这四个字段即可。
方案二(前端处理逻辑):
逻辑和后端处理逻辑大体上一致,只是把逻辑判断移到前台,故需要后端提供最新版本号 :newVersion
、 最小支持版本号 : minVersion
、apk下载url : apkUrl
、更新文案 : updateDescription
这四个字段。
客户端逻辑如下:
如果 currentVersion < newVersion, 则有更新信息;
如果 currentVersion < minVersion, 则需要强制更新;
如果 currentVersion >= minVersion, 则不需要强制更新;
如果 currentVersion == newVersion,则没有更新信息。
总结:
细心的你可能会发现上面的可选字段 apkSize 和 md5 并没有用到,既然是可选字段也就是可用可不用,根据需要决定是否采用,这里来讲下他们的用处。
apk 文件大小 apkSize
这个用处可以说出于考虑用户体验,需要在升级弹框出来展示给用户将要更新的内容多大,让用户决定在非 WIFI 状态是否要更新,不能为了拉用户下载量或所谓的 UV 数直接让用户在不知道大小的情况下去直接下载(土豪用户绕路)
。apk 的文件 MD5 值
这个主要是出于安全考虑吧,因为文件内容固定的话对应的 md5 是一样的,我们可以通过这个 md5 值来和下载的 apk 的 md5 值进行比较去保证我们从服务器更新下载的 apk 是一个完整的未被篡改的安装包,也就是说如果我们下载的 apk 的 md5 值和服务器返回的 md5 值相等,则说明我们下载的 apk 是完整的,且没有被相关有心人处理过的 apk。
综上所述,这个版本更新的处理逻辑客户端和后端谁来做都可以,无关乎懒不懒的问题,个人感觉灵活性后端比客户端方便多了,毕竟后端可以指定 minVersion 与 newVersion 中间的任意一个版本强制更新,而客户端做起来就没有那么灵活了,个人见解,如有更好的方案,欢迎指教。
UEditor 的 ASP 版本在虚拟空间上传文件失败,提示“上传错误”或“上传失败,请重试”等,是因为其文件上传组件在创建目录时,没有网站目录外的访问权限。
例如上传文件的磁盘保存路径为:
D:\web\sitename\wwwroot\upload\20180523\123.jpg
百度编辑器的上传组件会依次判断以下目录是否存在,不存在则创建:
D:\
D:\web\
D:\web\sitename\
D:\web\sitename\wwwroot\
D:\web\sitename\wwwroot\upload\
D:\web\sitename\wwwroot\upload\20180523\
虚拟空间自动配置的网站根目录可能是:
D:\web\sitename\wwwroot\
所以,判断存在或创建以下目录没有问题:
D:\web\sitename\wwwroot\upload\
D:\web\sitename\wwwroot\upload\20180523\
但判断存在以下目录时会因为权限不足而失败:
D:\
D:\web\
D:\web\sitename\
解决方法是找到文件 asp/Uploader.Class.asp
找到 CheckOrCreatePath 这个 Function,替换为:
Private Function CheckOrCreatePath( ByVal path )
Set fs = Server.CreateObject("Scripting.FileSystemObject")
Dim parts
Dim root : root = Server.mappath("/") & "\"
parts = Split( Replace(path, root, ""), "\" )
path = root
For Each part in parts
path = path + part + "\"
If fs.FolderExists( path ) = False Then
fs.CreateFolder( path )
End If
Next
End Function
另外,如果服务端无法通过 Request.Form 来接收该值,把 <form /> 放到 <table /> 外面试试。
或者绑定 contentChange 事件来赋值(不推荐):
ue.addListener("contentChange", function() {
document.getElementsByName('xxxxxx')[0].value = ue.getContent();
});
此方法的缺点是,不是所有操作都能触发 contentChange 事件,比如剪切、上传图片等。
改进方法(推荐):在 form 提交之前赋值。
打开:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample.sln
工具 - NuGet 包管理器 - 管理解决方案的 NuGet 程序包 - 更新
选择所有的包(Microsoft.Net.Http 无法更新,暂不勾选),更新
Microsoft.Net.Http 是个老顽固,无法更新也无法卸载,原因是它的语言包 Microsoft.Net.Http.zh-Hans 无法安装。我们切换到“已安装”选项卡,搜索“Microsoft.Net.Http”,选中 Microsoft.Net.Http.zh-Hans 并卸载它,现在我们可以更新 Microsoft.Net.Http 了。
【可能】运行报错:
“/”应用程序中的服务器错误。
配置错误
说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。
分析器错误消息: 创建 system.web.webPages.razor/host 的配置节处理程序时出错: 未能加载文件或程序集“System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)
源错误:行 4: <configSections>
行 5: <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
行 6: <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
行 7: <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
行 8: </sectionGroup>
源文件: E:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample\views\web.config 行: 6展开 Senparc.Weixin.MP.Sample 引用,查看 System.Web.Razor 的属性,复制版本号。打开 \views\web.config,修改 System.Web.WebPages.Razor 的 Version(有 3 处)。
对于小型开发项目来说,我习惯将所有代码放在同一个项目(Project)中来,所以做了以下处理:(盛派官方分离这些代码是为了同时供 MVC 和 WebForms 重用)
在 Senparc.Weixin.MP.Sample 中添加文件夹 CommonService,将 Senparc.Weixin.MP.Sample.CommonService 项目中的文件夹和文件(.config 除外)拖到 CommonService 文件夹中。
卸载 Senparc.Weixin.MP.Sample.CommonService 项目并删除引用。
在解决方案的 Libraries 文件夹中,我们看到 Sample 项目了以下功能模块,这些就是 Senparc.Weixin 的源代码,如果不需要修改,可以卸载它们改为使用从 NuGet 获取,以便随时更新到最新版本:
Senparc.WebSocket
Senparc.Weixin
Senparc.Weixin.MP
Senparc.Weixin.MP.MvcExtension NuGet 中对应的包名为:Senparc.Weixin.MP.MVC
Senparc.Weixin.Open
Senparc.Weixin.Work
Senparc.Weixin.WxOpen
Senparc.Weixin.Cache.Memcached
Senparc.Weixin.Cache.Redis
卸载这些项目,并在 NuGet 中安装这些模块(搜索“Senparc.Weixin”第一页都在了,认准作者 Jeffrey Su)
单元测试项目中的引用也按需更换,当然也可以卸载这些单元测试项目(视情况保留 Senparc.Weixin.MP.Sample.Tests) 。
【可能】运行报错
HTTP Error 500.19 - Internal Server Error
无法访问请求的页面,因为该页的相关配置数据无效。
配置源:
134: </sessionState>
135: <sessionState customProvider="Memcached" mode="Custom">
136: <providers>
可以在 Global.asax 的 RegisterWeixinCache() 中修改 Memcached 配置。
或者打开 Web.config,找到 Memcached 所在的 sessionState 标签,注释掉,这样程序自动使用上方的 InProc(应用进程内)。
如果开启了 ASP.NET State Service 服务,那么建议改用 StateServer,与 Custom 一样需要序列化,方便日后改用 Memcached 或 Redis。
再次更新 NuGet,否则会提示无法加载 Enyim.Caching 的错误(报错行:MemcachedObjectCacheStrategy.RegisterServerList(memcachedConfig);)。
接下来,进入微信公众平台,在左侧 开发 - 基本配置 中设置相关参数(填写“服务器地址(URL)”如“https://weixin.xoyozo.net/weixin/”),打开 Sample 项目中的 Web.config,配置我们的公众号参数。
将项目发布到服务器上,尝试向公众号发送消息吧。
单元测试
设置单元测试中的公众号配置项:打开 Senparc.Weixin.MP.Test/CommonAPIs/CommonApiTest.cs,在 AppConfig 方法中可以看到支持两种配置方式,在 Senparc.Weixin.MP.Test/Config/test.config 配置文件中配置,或直接在 CommonApiTest.cs 文件中配置,前者格式如下,后者建议只在个人测试项目中使用。
<Config> <AppId>YourAppId</AppId> <Secret>YourSecret</Secret> <MchId>YourMchId</MchId> <TenPayKey>YourTenPayKey</TenPayKey> <TenPayCertPath>YourTenPayCertPath</TenPayCertPath> <!-- 小程序 --> <WxOpenAppId>YourOpenAppId</WxOpenAppId> <WxOpenSecret>YourWxOpenSecret</WxOpenSecret> </Config>
另外,将 _testOpenId 值改为自己的微信号在本公众号上的 openid。
微信网页授权(OAuth2)
打开 OAuth2Controller 控制器,将“Index”Action 中的盛派的网址(有 2 处)改成:
Request.Url.Scheme + "://" + Request.Url.Authority + "/Oauth2/……
未完待续……
AspNetPager 官网给出了示例:
即在引入 bootstrap.css 后添加以下样式:
.pagination a[disabled] { color: #777; cursor: not-allowed; background-color: #fff; border-color: #ddd; }
.pagination span.active { z-index: 2; color: #fff; cursor: default; background-color: #337ab7; border-color: #337ab7; }
给 AspNetPager 控件添加以下属性:
CssClass="pagination" LayoutType="Ul" PagingButtonLayoutType="UnorderedList" PagingButtonSpacing="0" CurrentPageButtonClass="active"
然而,当页数超过 30 时(ShowBoxThreshold 的默认值)会显示 PageIndexBox,可以使之不显示:
ShowPageIndexBox="Never"
或者添加以下样式修饰它:
.pagination input[type=text] { width: 50px !important; display: inline-block; text-align:center; }
.pagination input[type=button] { margin-top: -3px; margin-left: 5px; }
并增加属性:
PageIndexBoxClass="form-control" SubmitButtonClass="btn btn-primary"
另外,我还添加了以下属性来适应我的项目:
UrlPaging="true" AlwaysShow="True" AlwaysShowFirstLastPageNumber="true" ShowFirstLast="false" NumericButtonCount="3"