Tom's Blog

写一些有意思的东西。

0%

在开始前,强烈建议手动同步电脑时钟。虽然电脑时钟的积累性误差通常只有几秒,但这几秒可以产生很大的影响。如何时钟同步?Windows 10用户参照这里;MacOS用户参照这里(MacOS强制同步时间需要命令行操作,为了简便,也可以只把时间同步服务器设置为亚洲的)。

首先,在Chrome中安装本插件

首先的首先,确保你的电脑上安装了Google Chrome(或Microsoft Edge Chromium)。本插件同时适配Windows和MacOS等平台,但本教程以Windows系统为例,MacOS操作基本一致。

OK,让我们开始吧,解压enroll.zip压缩文件,你将得到一个文件夹。如图:

打开你的Chrome,在地址栏输入如下地址:

1
chrome://extensions

进入如下界面后,按顺序按下这几个按钮(你的电脑可能会以不同的语言显示,翻译一下就可以了):

在弹出的文件选择窗口中,选择你刚刚解压得到的文件夹(注意,是文件夹!!)。如图:

之后,不出意外,你将会看到本插件被成功安装:

很好,插件安装成功,现在我们开始选课!

这里插播一个提醒,通过开发者模式安装插件之后,每次开启Chrome,都会看到这样的提醒:

请忽略它,关掉即可……

接下来,开始选课

打开Albert,我们就正常操作,一路登录到选课的页面。

一切正常的话,你将在屏幕右边看到一个小窗,解释一下窗口上显示信息的内容:

这时候在网上搜索一下北京时间,如果你发现悬浮窗中显示的当前时间和北京时间有明显的差距(比如说5秒),那么请回到文章开头,进行时钟校准。如果时钟是不准的,脚本将不会准时执行任务,所以势必对抢课造成影响。

接下来,啥都不要点,在设置选课时间输入框中,输入你想脚本运行的时间点。格式如下:

1
1970-01-01 00:00:00.000

最后的三个小数点呢,代表着毫秒,你可以把时间设置得比开放时间提前500毫秒左右,这样可以抵消一点网络延迟。当然,不能设置得太早,不然选不到课别怪我哦。

然后,我们就可以按下Set按钮了。一切顺利的话,选课时间就会变成你设置的时间,脚本运行状态中的T也会变成true。

接下来,我们按下Albert的Validate/Enroll/Edit按钮:

进入Shopping Cart界面,进入以后,可以看到脚本运行状态中S变成1。这时候,勾选好你想抢的课,就不需要做额外的操作了!如图:

这里的注意事项非常重要……

注意其一,一定要提前勾选好你要选的课;否则,你懂的哈哈哈……

注意其二,理论上脚本执行完之后,悬浮窗会变绿,但还请人工检查一下抢课结果。

注意其三,脚本不能完全取代真人,所以,在脚本运行时,请守在一旁,做好人工接管的准备!如果你看到了一些应该被按下的按钮没有被如期按下(如果脚本正常运行,你是根本看不到任何需要你去按的按钮的,它们还未出现就已经被点掉了;所以,脚本出BUG的情况是很容易分辨的),这个时候需要尽快手工接管。概率极低,但还请做好准备。

注意其四,尽量确保电脑系统时钟精确!

注意其五,提前2到3分钟按下Set按钮,效果会更好。(注:这主要是担心JavaScript中setTimeout()函数的积累性误差,所以可以尽量减小这个等待的time来减小误差。)

注意其六,确保网络通畅,如果网络很慢……那么这个脚本也没用……

最后,祝您好运!

这几天在使用WAMP Server环境时,遇到了离奇的PhpMyAdmin无法使用的问题。为了解决这个问题,我做了各方面的尝试,花了将近两天的时间,最终”灵光一现“,在一个很不起眼的地方找到了问题的根源。虽然我遇到的问题具有很强的特殊性,遇到的概率很小,但是整个探索的过程十分的波折而有趣,特此记录。另外本文内容的先后顺序严格遵循时间顺序,因此可能会有些凌乱,还请见谅!如果只想看结论,可以直接跳到文末。

问题描述

在Chrome中访问http://localhost,可以正常访问,如下图:

http://localhost

但是访问http://localhost/phpmyadmin时,会加载很长时间,然后显示一个加载不完全的界面,如下图:

http://localhost/phpmyadmin

尝试使用正确的账户密码登录,在按下Go按钮之后,又会加载很长时间,然后提示”The connection was reset.“,如下图:

http://localhost/phpmyadmin/index.php

排查WAMP Server的问题

一开始,我的直觉是,一定是WAMP Server的某个地方出了问题,重装应该可以解决这个问题。于是,我重新安装了WAMP Server,但是问题依然存在。

排查浏览器的问题

在网上搜索了我所遇到的情况后,我在WAMP Server官方论坛找到了一个帖子。帖子中描述的情况是:“ERR_CONNECTION_RESET”错误,和PhpMyAdmin登录界面上出现的如下错误信息:

There is mismatch between HTTPS indicated on the server and client. This can lead to non working phpMyAdmin or a security risk. Please fix your server configuration to indicate HTTPS properly.

这和我的情况完全符合。

我浏览了这个帖子,得到的一个解释是:

Try using a browser other than Chrome, see if it works with that!

Hi,

> The error message is “There is mismatch between HTTPS indicated on the server and client.

PhpMyAdmin from Wampserver is not launched as HTTPS : ‘http://localhost/phpmyadmin/'

It seems that more and more Chrome (Google) wants to behave like a web boss and wants to force the use of https, in the same way that it hijacked the tld “.dev”

Supposedly for our safety, but ultimately to be the Internet dictator.

翻译一下,就是说Google在HTTPS上十分的执着且霸道,因此Chrome会阻断一些HTTP连接。虽然感觉这个解释并不靠谱,但是我仍然抱着试一试的态度,试用了不同的浏览器:

  • Edge Chromium - 问题依然存在
  • Internet Explorer - 问题依然存在
  • Mozilla Firefox - 问题依然存在

新版Edge用的是Chromium引擎,和Chrome是近邻,它出问题不能说明什么。但是IE和Firefox也有同样的问题,足以证明这个问题并非浏览器导致。那么浏览器的嫌疑被排除了。

排查Xdebug的问题

帖子中另一种说法认为这个问题是PHP的Xdebug组件导致的。我尝试禁用了php_opcache和php_xdebug两个组件,问题依然存在。更何况,之前使用WAMP Server时,这两个组件都是默认开启的,但我并未遇到PhpMyAdmin访问问题。所以排除Xdebug的嫌疑。

一个不完美的解决方案

帖子的最后,问题通过更改Apache端口的方式解决了。我尝试将Apache的监听端口从80改为了8080,发现PhpMyAdmin可以正常访问了。但是这个解决方式是不完美的。80端口上的问题依旧存在,更改端口只是避开了问题罢了,并没有真正解决它。

为了找到问题根源,我又在网上搜索了很久,可惜并没有找到更多的有实质性帮助的信息。我于是开始了一个人的离线琢磨。

在WAMP Server中的测试

我试着在WAMP Server的www目录中新建了一个halo文件夹,在其中新建了index.htmlindex.php两个文件。其中index.html的内容是一行文字,而index.php则是echo()了一行文字。经测试,两个文件都能正常访问。但是当我进一步把index.php的内容修改为:

1
2
<?php
phpinfo();

这时浏览器就无法访问index.php了,症状为一直加载不出来,最后显示”ERR_CONNECTION_RESET“,和PhpMyAdmin那边的状况如出一辙。难道是PHP解释器出了问题?显然不是,因为一开始index.php是能够被正常解释的。

排查Apache配置的问题

虽然说WAMP Server已经重新安装过了,但是Apache错综复杂的配置文件给了我一种问题就在其中的错觉。既然80端口不能正常访问,而8080端口没有任何问题,会不会是配置文件导致的呢?我搜索了各个配置文件,发现唯一提到了80端口的地方,就是Apache自带的localhost虚拟主机的那段配置。我试着注释了这段配置,但是问题依然存在,所以Apache的配置文件应该也没有问题。

在开发者工具中的发现

鉴于无头苍蝇式的尝试都没有结果,我意识到,我很可能遇到了一个极其特殊的案例。如果继续随意地修改配置信息,不但解决不了眼前的问题,还很可能不小心把系统整崩。

我于是开始重新整理排查问题的思路。既然问题表现在网页上,那为何不仔细看看网页上发生了什么呢?在PhpMyAdmin登录界面按下F12打开开发者工具——出现了满屏的错误信息!

开发者工具中的错误信息

仔细看了看这些错误信息,发现关键原因在于部分JS和CSS文件请求失败,这也解释了为什么网页的排版很不正常。而请求的失败信息,正是之前出现过的“ERR_CONNECTION_RESET”。当我换成8080端口访问时,则没有任何错误信息出现。

在Apache日志中的发现

除了查看开发者工具之外,我也很自然地想到了检查Apache的日志。

access.log

这一行代表的请求在Chrome的开发者工具中是失败的,原因为“ERR_CONNECTION_RESET“,但是从Apache的角度来看,服务器返回的状态码为200(OK),也就是说这个请求是成功的。也就是说,虽然服务器成功响应了这个请求,但是这个请求并没有被浏览器接收到。换句话说,如果服务器的日志和浏览器的开发者工具没有骗我,这个请求就是在半路上被拦截了。那么是谁拦截了这个请求呢?为什么还是有一小部分请求是正常的呢?难不成这个拦截还是有一定规律的?

在Flask中的测试

为了进一步排除Apache作为服务器的嫌疑,我想到可以用别的HTTP服务器来代替Apache,再测试80端口的连通性。Python Flask可以作为一个简易的Web服务器,于是我编写了如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask import Flask

app = Flask(__name__,
static_url_path='',
static_folder='www')


@app.route('/', methods=['GET'])
def test_page():
return "It works!"


@app.route('/phpmyadmin/js/config.js', methods=['GET'])
def test_page_2():
return "Okay, though it's not a file!"


if __name__ == '__main__':
app.run(
host='127.0.0.1',
port=80
)

由于www文件夹被作为了Flask的静态资源文件夹,我在其中创建了含有一行文字的index.html,是用浏览器访问,一切正常!

从之前开发者工具的输出中,可以看到/phpmyadmin/js/config.js?v=4.9.2这个请求是失败的。因为问号之后的内容是参数,因此我在Flask中定义了/phpmyadmin/js/config.js这么一个路径。脚本运行后,浏览器可以正常访问此路径,如图:

http://localhost/phpmyadmin/js/config.js

也就是说,如果问题是半路上的”拦截“导致的,在这个实验中所谓的”拦截“并没有发生,或者说这个”拦截“的规则并不是基于URL路径的(因为之前被拦截的路径能够正常访问了)。但是这个实验并不能很好的缩小问题位置的范围,Apache依然有嫌疑(毕竟切换到Flask问题就消失了)。

在PhpStudy中的测试

由于刚刚的实验并没有为Apache洗清嫌疑,我想到了用其他的WAMP/WNMP框架来测试(自己逐个安装组件也可以,但是太麻烦了)。我尝试了PhpStudy中的WAMP,安装好PhpMyAdmin,在Chrome中访问,问题依旧。秉着严谨的态度,我又切换到WNMP,在Chrome中访问,问题依旧。

通过这个实验,我可以确信,问题并不出在WAMP Server上,因为在PhpStudy中我也遇到了相同的问题。况且WAMP Servre和PhpStudy都是主打随装随用的,理论上无需配置即可正常工作;它们两者都有着不小的用户群体,两个软件同时出现一个BUG的概率,很低很低。

在之前的实验中,我已经排除了浏览器的嫌疑;现在又排除了HTTP服务器的嫌疑。总结一下,现在可以高度确信的是:问题出在了服务器-浏览器之间,也就是部分请求被半路拦截了。

排查防火墙的问题

由于防火墙一直是Windows的传统艺能,仿佛条件反射一般,遇到拦截有关的问题都应该首先尝试关闭防火墙或检查防火墙设置。我尝试关闭了防火墙,又尝试了重置防火墙设置,但都不能解决问题。所以本次问题与防火墙无关。

排查winsock和DNS缓存的问题

虽然我并不是很懂winsock到底是什么,但是确实很多问题确实可以通过重置winsock或刷新DNS缓存来解决。于是我尝试运行了一下命令:

1
2
netsh winsock reset
ipconfig /flushdns

重启电脑,问题依然存在,说明本次问题与winsock和DNS缓存无关。

在Lighttpd中的测试、在手机上的发现

虽然通过之前实验,我已经可以确定问题出在服务器-浏览器之间,但是我依然不死心地想要尝试其他的HTTP服务器。这次我选用的是轻量级的Lighttpd,也就是OpenWrt使用的那款。

启动Lighttpd后,我尝试使用浏览器访问http://localhost,得到的是Lighttpd的测试页面,如图:

http://localhost

加载十分的缓慢,而且右侧的图片没有加载出来。查看开发者工具,还是熟悉的“ERR_CONNECTION_RESET”错误。

因为Lighttpd默认监听地址为0.0.0.0,我又关闭了防火墙,我便顺手尝试了一下用手机访问电脑,结果一切正常,图片也能显示出来,如图:

通过手机访问

这就很奇怪:电脑本地访问有问题,用手机远程访问反而没有问题了。基本可以确定,这个问题不光是在服务器-浏览器之间,而且更偏向浏览器一些。或者,会不会是电脑上的Loopback Adapter出了问题呢?

排查Loopback Adapter的问题

检查了电脑上现有的网络适配器,我发现有几个可疑的对象,一是网络类软件经常使用的NpLoopbackAdapter,二是VMWare创建的给虚拟机上网用的几个适配器。抱着“宁可错杀三千。不可放过一个”的想法,我卸载了NpLoopbackAdapter和VMWare。这样就没有可疑的适配器了,然而就算重启后,问题依然存在。这说明NpLoopbackAdapter和VMWare的适配器都是OK的。

排查系统的问题

很早之前,我遇到过一个某个网站无法访问的玄学问题,各种方法试遍都不能解决,最后不得不重装了系统。那会不会这次问题的原因是系统文件损坏呢?运行如下命令检查系统文件:

1
sfc /scannow

扫描结果显示:系统文件没有损坏。虽然不排除存在暗病的可能性,但是可以确定的是系统出问题的概率很低,剩下的唯一一种可能性,就是有某个软件拦截了请求。

在安全模式中的发现

为了验证这个问题是某个软件拦截请求导致的,我想到了安全模式。安全模式下除系统必需组件外的服务都不会自启动,可以实现一个较为纯净的环境。在安全模式下,我启动了Lighttpd并用浏览器访问之,结果如下图:

通过安全模式访问

可以看到,在安全模式下,右侧图像能够被正确地加载,拦截的问题消失了。这个实验既证明了我的问题是由软件拦截请求导致的,也证明了系统文件并未损坏。

排查软件的问题

回到了正常模式,问题依旧。我尝试了挨个禁用服务、挨个停止进程,把能停止的进程都停止了,问题也没有解决。看来这个罪魁祸首藏得很深。

在Lighttpd的Web根目录中的发现

走投无路的我在浏览Lighttpd的Web根目录时,忽然有了一个新的发现:

Lighttpd的Web根目录

这里的light_button.png是左侧的小图像(可以显示),light_logo.png是右侧的大图像(无法显示)。值得注意的是,小图像的大小为3KB,而大图像的大小为35KB。我突发奇想,会不会是大文件就会请求失败呢?(后记:这个问题其实观察Apache的日志也能回答。)

我立刻写了如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from random import randint


def generate_file(filename: str, size: int) -> None:
with open(filename, 'w') as fs:
for i in range(size):
fs.write(chr(randint(33, 126)))


if __name__ == '__main__':
generate_file('2KB.txt', 2 * 1024)
generate_file('4KB.txt', 4 * 1024)
generate_file('8KB.txt', 8 * 1024)
generate_file('34KB.txt', 34 * 1024)

用这段代码,我生成了几个不同大小的TXT。将其复制到Lighttpd的Web根目录,用浏览器访问,结果如下:

  • 2KB - 请求成功
  • 4KB - 请求成功
  • 8KB - 请求成功
  • 34KB - 请求失败

这里的结果验证了刚才的猜想,即过大的文件就会请求失败。也就是说,这次拦截请求的软件,是基于响应大小来拦截的。

找到问题根源

刚刚的结论,其实已经很能说明问题了。经过短时间的痛苦思考,我想到了我的下载软件,Free Download Manager(也就是贫民版IDM)。我检查了它的设置,发现了这个:

FDM设置

不知道为什么,它们都被勾上了,我试着取消了所有的勾选,发现问题居然就解决了。Lighttpd的测试页中的图片可以正常显示了,切换回WAMP Server,PhpMyAdmin也可以正常访问了。

这时候去解释之前遇到的情况,发现一切都解释得通了:在勾选了所有的浏览器后,FDM会尝试从所有的浏览器捕捉下载,然后那些较大的JS和CSS文件也被不幸捕捉了,然而FDM却没有很好的处理这些请求,于是浏览器在超时之后得出了“ERR_CONNECTION_RESET”的结论。

为什么phpinfo()无法显示?因为它的响应太大了,会被FDM拦截。

为什么所有浏览器都有问题?经实测,这个问题有且仅有在Edge被勾选的情况下发生。大胆推测FDM可能有BUG,在尝试捕捉Edge的下载时便会错误地拦截所有浏览器的请求……

为什么换端口就可以?可能FDM只捕捉80端口的请求。

所以,最终的解决方案,就是在FDM中取消勾选Edge

困扰了我将近两天的问题,就这样解决了。

ADDI、ADDIU:加立即数,区别在于是否检测溢出。

Format: ADDI rt, rs, immediate
To add a constant to a 32-bit integer. If overflow occurs, then trap.

Format: ADDIU rt, rs, immediate
To add a constant to a 32-bit integer

ADD、ADDU:加寄存器,区别在于是否检测溢出。

Format: ADD rd, rs, rt
To add 32-bit integers. If an overflow occurs, then trap.

Format: ADDU rd, rs, rt
To add 32-bit integers

可知,在忽略溢出的前提下,ADDI与ADDIU等价,ADD与ADDU等价。原因如下:

分析

1、忽略溢出,addi与addiu等价

addi是加立即数指令,支持溢出检测,具体表现为:遇到溢出时,溢出错误标志变为高电平,传送到控制器cu中,导致此时的寄存器写使能信号regwr无效,最终结果为不将运算结果写入目的寄存器。

addiu是加立即数指令,不受溢出限制,具体表现为:遇到溢出时,对溢出的结果进行32bit求模,将求模结果写入目的寄存器中,因而不受溢出限制。

在忽略溢出的前提下,addi和addiu都能计算出溢出后的结果(进行32bit求模),并且将结果写入目的寄存器中,因此二者是等价的。

2、忽略溢出,add与addu等价

Add是将rs与rt中的值相加,结果存储在rd寄存器中。检测溢出。如果不溢出,将结果存储在rd寄存器中,如果溢出,抛出溢出异常,不改变rd中的值。

Addu是将rs与rt中的值相加,结果存储在rd寄存器中。不检测溢出,如果溢出,不会抛出异常。
add是有符号数加法,addu是”无符号数”加法,但是对于cpu来说,都是一样的,不管有没有符号位,都是从最低位加,进位,一直到最高位。

在这里插入图片描述

在忽略溢出的条件下,无论是否溢出,二者都将计算结果存入rd中,因此二者是等价的。

所以我的结论是:

对于CPU来说,有符号或者无符号都不重要。他们的运算都是一样的,不管有没有符号位,都是从最低位加,进位,一直到最高位,区别在于是否具有溢出检测。

所以,ADDADDU都可以适用于有符号/无符号数的加法,其区别只是是否具有溢出检测

附:四个指令的官方定义

MIPS32 Architecture For Programmers这本书中,对于这四个指令的定义如下:

ADDI

在这里插入图片描述

ADDIU

在这里插入图片描述

ADD

在这里插入图片描述

ADDU

在这里插入图片描述

转载自:https://blog.csdn.net/sinat_42483341/article/details/89511856

今天在理解一段代码时遇到了一个问题。这段代码的大致功能是读取一个MIDI文件中音符信息,包括音高和开始、结束时间。刚开始时,我是默认了这段代码的时间单位都是秒,毕竟在pretty_midi中音符的开始、结束时间都是用秒来表示的。想当然的,我觉得MIDI文件中也是以秒为单位。

为了测试一下这个结论,我把一个MIDI文件导入了FL Studio。这个MIDI是由pretty_midi输出的,其中所有的音符都从第0秒开始,第3秒结束。

MIDI导入FL Studio

FL中是以节拍为单位的,这些音符都持续了6个拍子。调整播放位置,使其与音符结尾对齐。将时间显示单位改成M:S:CS,这时问题出现了——显示时间并不是预期的3秒。

130 BPM和“不正确”的时间

注意到BPM是默认的130,便尝试将BPM调整到120,发现显示时间变成了预期的3秒。

120 BPM和“正确”的时间

觉得很是奇怪,既然MIDI中的时间单位是绝对的秒,那为什么改变FL中的BPM会影响音符的持续时间呢?既然FL中的音符时间可以由BPM决定,那难道MIDI中的时间单位不是秒而是节拍?如果MIDI中的时间单位是节拍,那pretty_midi中的start=0end=3的单位是什么?

pretty_midi官方文档

查阅了pretty_midi官方文档,发现pretty_midi里的时间单位确实是秒。但是MIDI中的时间单位呢?

Mido官方文档

Mido的文档中可以看到,MIDI文件确实是以节拍做单位的。那么我猜测在pretty_midi的代码中,应该有从秒到节拍的转换。这也就意味着需要一个默认的BPM值。

Mido官方文档

Mido的文档中可以看到,MIDI的文件默认BPM就是120,那么相信pretty_midi也是遵循了这个规则的,而且这个速度信息应当会被写入MIDI文件。

查询了pretty_midi的官方文档,果不其然。

pretty_midi官方文档

那么在FL中,FL的BPM设定所做的事情就是强行以这个速度来播放MIDI中的音符,而忽略了MIDI中自带的速度信息。这也就不难解释为什么当FL的BPM设置为120时,音符持续时间就和pretty_midi中的时间一致了。

Nginx的块配置

Nginx在逻辑上将提供不同内容的配置划分为块,这些块以层次结构的形式存在(http->server->location)。客户端发出请求时,Nginx收到之后,会有一个确定应该使用哪些配置块来处理请求的过程。本文主要介绍 server 块背后的处理过程。

server块是Nginx配置的子集,它定义用于处理已定义类型请求的虚拟服务器(虚拟机)。管理员通常会配置多个server块,并根据请求的域名,端口和IP地址决定哪个块应该处理哪个连接。

Nginx如何决定哪个server块来处理请求

由于Nginx允许管理员定义多个server块作为单独的虚拟Web服务器实例,因此需要一个算法来确定将使用哪些server块来匹配请求。

Nginx在此过程中关注的主要server块指令是listen指令和server_name指令。

解析“listen”指令以找到可能的匹配

首先,Nginx查看请求的IP地址和端口,并与每个服务器的 listen 指令相匹配,构建可能解析请求的服务器块列表。

listen指令通常定义 server 块将响应的IP地址和端口。默认情况下,任何不包含listen指令的 server 块默认 listen 在0.0.0.0:80(或者0.0.0.0:8080如果Nginx由普通的非root用户运行),这样的配置块响应80端口上任何接口的请求,但是这个默认值在server选择过程中没有太大的权重。

listen指令可以设置为:

  • IP地址/端口组合。
  • 只有IP地址,它将监听默认端口80。
  • 只有端口,它将监听该端口上的每个接口。
  • Unix套接字的路径。

最后的选项通常在不同的服务器之间传递请求时起到作用。

在尝试确定向哪个服务器块发送请求时,Nginx将首先尝试listen使用以下规则根据指令的特异性来决定:

  • Nginx用默认的缺省值来替换所有不完整的lesten指令(完整:IP+port的组合)的缺省值,因此每一个server块的listen指令都可以看作是IP地址和端口的组合。 这种转换的例子有:
    • 没有listen指令的块使用该值0.0.0.0:80
    • 设置为111.111.111.111没有端口的IP地址的块变为111.111.111.111:80
    • 设置为8888没有IP地址的端口的块变为0.0.0.0:8888
  • 接下来Nginx会尝试去收集一个server块的列表,这个列表是基于具体的IP和端口最佳匹配。也就是说如果匹配的server块有具体的IP地址,它就不会匹配用0.0.0.0作为默认的IP地址的server块。无论什么情况,在Nginx选择server块的过程中,端口必须准确匹配。
  • 如果只有一个最具体的匹配,那么该server块将用于提供请求。如果有多个server 块具有相同层次的具体匹配,那么Nginx需继续评估server_name指令 。

需要特别注意的是,只有 listen 指令在同一层次上有多个匹配的 server 块时,Nginx才会继续评估server_name指令。举个例子,如果域名example.com被解析到IP为192.168.1.10,端口为80的主机上,当客户端请求example.com时,在本例中,第一个server模块总是会提供服务,尽管server_name指令在第二个server模块中。

1
2
3
4
server{
listen 192.168.1.10;
....
}
1
2
3
4
5
server{
listen 80;
server_name example.com;
....
}

多个server模块在具体的匹配中处于同一级别的情况下,Nginx下一步才会检查server_name指令。

解析server_name指令选择一个匹配

接下来,为了进一步评估具有相同特定listen指令的请求,Nginx会检查请求的“host”标头,此值包含客户端实际尝试访问的域或IP地址。

Nginx在候选的每一个server模块中,查看其server_name指令,尝试去找到最佳的匹配。Nginx通过下面的公式来进行评估:

  • Nginx首先找到server_name与请求的Host头信息精准匹配的server模块,如果找到了这个server模块,它将会被用于服务客户端的请求。若有多个特定的匹配项被找到,第一个会被用于提供服务。
  • 如果没有找到精准的匹配项,Nginx接下来将尝试去找server_name与前置通配符(在配置中名称的开头用*表示)匹配的server模块。只要找到一个,这个server模块将被用于为客户端提供服务。如果找到了多个匹配,最长匹配结果的server模块将会被用于提供服务。
  • 如果使用前置通配符没有找到匹配时,Nginx接下来将尝试去找server_name与后置通配符(在配置中名称的结尾用*表示)匹配的server模块。只要找到一个,这个server模块将被用于为客户端提供服务。如果找到了多个匹配,最长匹配结果的server模块将会被用于提供服务。
  • 如果使用后置通配符没有找到匹配时,Nginx接下来将会评估用正则表达式(在名称前用~表示)定义server_name的server模块。带有与Host头匹配的正则表达式的第一个server_name将被用于提供服务。
  • 如果没有找到用正则表达式定义server_name的相匹配的server模块时,Nginx接下来会使用默认IP和端口的server模块。

每一个IP地址/端口组合都有一个默认的server模块,当用上面的方法不能确定一个操作的过程时将使用默认的server模块。对于IP地址/端口的组合来说,这将是配置中的第一个模块或者是包含default_server选项作为listen指令的一部分的server模块(这将复写first-found算法)。每一个IP地址/端口组合只能有一个default_server声明。

实例

如果已定义的server_name与Host头的值精准匹配时,这个server模块将被选择来处理请求。

在这个例子中,如果请求的Host头的值被设置为 host1.example.com,第二个server模块将被选中:

1
2
3
4
5
server{
listen 80;
server_name *.example.com;
...
}
1
2
3
4
5
server{
listen 80;
server_name host1.example.com;
...
}

如果精准的匹配没有被找到时,Nginx将会检查是否有一个具有适合前置通配符的server_name。以通配符开始的最长的server_name的server模块将会被选择来完成响应。

在这个例子中,如果请求的Host头是 www.example.org,第二个server模块将被选中:

1
2
3
4
5
server{
listen 80;
server_name www.example.*;
...
}
1
2
3
4
5
server{
listen 80;
server_name *.example.org;
...
}
1
2
3
4
server{
listen 80;
server_name *.org;
}

server_name以通配符开始的模块没有找到,Nginx将查看在表达式后面有通配符的匹配项是否存在。此时,以通配符结尾的最长的匹配项将被用于服务客户端的请求。

在这个例子中,如果请求的Host头被设置为 www.example.com,第三个模块将被选中:

1
2
3
4
5
server{
listen 80;
server_name host1.example.com;
...
}
1
2
3
4
server{
listen 80;
server_name example.com;
}
1
2
3
4
server{
listen 80;
server_name www.example.*;
}

如果通配符匹配项没有找到,Nginx将会去匹配用了正则表达式的server_name。第一个匹配上的server模块将会被选中来响应请求。

在这个例子中,如果请求的Host头设置为 www.example.com,那么第二个server模块将被选中来完成响应。

1
2
3
4
5
server{
listen 80;
server_name example.com;
...
}
1
2
3
4
5
server{
listen 80;
server_name ~^(wwwhost1).*\.example\.com$;
...
}
1
2
3
4
5
server{
listen 80;
server_name ~^(subdomainsetwwwhost1).*\.example\.com$;
...
}

如果上述步骤都不能满足请求,则该请求将被传递到默认的server模块以获取匹配的IP地址和端口。

转载自:https://www.nginx.cn/5014.html

计算机系统有一系列的“周期”概念,区别、联系地理解这些概念至关重要。以下对时钟周期、振荡周期、机器周期、CPU周期、状态周期、指令周期、总线周期、任务周期进行简单介绍。

周期

在电子技术中,脉冲信号是一个按一定电压幅度,一定时间间隔连续发出的脉冲信号。脉冲信号之间的时间间隔称为周期;而将在单位时间(如1秒)内所产生的脉冲个数称为频率。频率是描述周期性循环信号(包括脉冲信号)在单位时间内所出现的脉冲数量多少的计量名称;频率的标准计量单位是Hz(赫)。电脑中的系统时钟就是一个典型的频率相当精确和稳定的脉冲信号发生器。

时钟周期

时钟周期,一般也称振荡周期(如果晶振的输出没有经过分频就直接作为cpu的工作时钟,则时钟周期就等于振荡周期),即CPU的晶振的工作频率的倒数,是计算机中最基本的、最小的时间单位。通常成为节拍脉冲或者T周期。对于单片机时钟周期,时钟周期是单片机的基本时间单位,两个振荡周期(始终周期)组成一个状态周期。

振荡周期(oscillating period)

在衰减振荡中,两个相邻同方向峰值之间的时间称为振荡周期Tp,振荡频率2π/Tp。在相同衰减比下,振荡周期越短或振荡频率越高,则回复时间越短,因此振荡周期(频率)反映系统响应快慢的指标。

机器周期

机器周期,一般也叫CPU周期。在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶段(如,取指令、存储器读、存储器写等),每一阶段完成一项工作(称为一个基本操作)。完成一个基本操作所需要的时间称为机器周期。一般情况下,一个机器周期由若干个S周期(状态周期)组成。

CPU周期

又称机器周期,CPU周期定义为从内存读取一条指令字的最短时间。一个指令周期常由若干CPU周期构成。

状态周期

在8051单片机中把一个时钟周期定义为一个节拍(用P表示),二个节拍定义为一个状态周期(用S表示)。

8051系列单片机的一个机器周期由6个S周期(状态周期)组成。一个机器周期包含6个状态周期(S1-S6),而一个状态周期又包含两个时钟振荡周期(简称时钟周期)。例:8051单片机的机器周期由6个状态周期组成,也就是说一个机器周期=6个状态周期=12个时钟周期。

指令周期

指令周期是执行一条指令所需要的时间,即CPU从内存取出一条指令并执行这条指令的时间总和。一般由若干个机器周期组成,从取指令、分析指令到执行完所需的全部时间。指令不同,所需的机器周期数也不同。对于一些简单的的单字节指令,在取指令周期中,指令取出到指令寄存器后,立即译码执行,不再需要其它的机器周期。对于一些比较复杂的指令,例如转移指令、乘法指令,则需要两个或者两个以上的机器周期。通常含一个机器周期的指令称为单周期指令,包含两个机器周期的指令称为双周期指令。

总线周期

总线周期通常指的使通过总线完成一次内存读写操作或完成一次输入输出设备的读写操作所必须的时间。由于存储器和I/O端口是挂接在总线上的,CPU对存储器和I/O接口的访问,是通过总线实现的。通常把CPU通过总线对微处理器外部(存储器或I/O接口)进行一次访问所需时间称为一个总线周期。一个总线周期一般包含4个时钟周期,这4个时钟周期分别称4个状态即T1状态、T2状态、T3状态和T4状态。

任务周期

周期任务是指计算机系统按一定周期达到并请求运行,每次请求称为任务的一个任务实例,任务实例所属任务的起始时刻称为该任务实例的到达时刻,任务实例被置为就绪态的时刻称为该任务实例的释放时刻。

转载自:https://blog.csdn.net/yangtalent1206/article/details/5853017

很久之前就有自己建站的想法,但是一直在犹豫。这次终于下定决心买了一个顶级域名,又用学生优惠在阿里云买了一台轻量应用服务器。虽说国内的主机会有备案的要求,多一些步骤,但是毕竟价格优惠+访问速度快+不赶时间,所以还是选择了国内的机房。

第一次,我照着教程手动编译安装了LAMP,花了一晚上编译和安装,但不幸在配置HTTPS的时候把httpd搞坏了,自己又没有能力修理,无奈只好重置了系统。第二次,我试用了一下LNMP一键包,才发现它十分强大,不仅支持无人值守安装,就连更新组件,添加虚拟主机和部署HTTPS都是傻瓜式的,只需一行命令,很适合Linux小白。所以如果对于Linux不是特别的熟悉,建议使用LNMP一键包来减少工作量。下面我会罗列出几个遇到的问题和分别的解决方案(本文有些参数可能因机而异,请灵活变通)。

修改服务器主机名和SSH端口

阿里云的与主机安装CentOS后默认的主机名是一串随机字符,这使得强迫症(这是种病,得改)的人看了很不爽,遂修改之(这里new_hostname改成你想要的主机名)。

1
hostnamectl set-hostname new_hostname

另外,为了安全,最好修改一下SSH的端口。运行以下命令编辑配置文件:

1
nano /etc/ssh/sshd_config

找到#Port 22字样,在下面添加一行Port 2222(这里2222替换为你想要的端口),保存退出。运行以下命令 重启SSH服务即可生效:

1
systemctl restart sshd.service

如果无法访问,那可能是被阿里云的防火墙阻挡了,请前往控制面板打开相应端口。

切换到阿里云软件源

首先,运行以下命令备份原配置:

1
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

然后,下载阿里云的配置:

1
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

最后,重新生成缓存:

1
2
yum clean all
yum makecache

WordPress不能安装、不能下载主题和插件

这种情况我之前也遇到过,原因都是web目录的权限设置不当——没有把属主和属组设置为www(也有时候是daemon),导致web应用没有写入的权限。我们可以利用chmodchown命令解决这个问题。我使用了LNMP一键包,因此web根目录是/home/wwwroot/default,Nginx使用的用户和用户组是www:www,那么需要运行以下命令:

1
2
chmod -R 755 /home/wwwroot/default
chown -R www:www /home/wwwroot/default

另外值得一提的是,虽然权限设置错误时,WordPress会遇到各种问题,但PhpMyAdmin却依旧能正常运行,我猜测这是因为PhpMyAdmin的功能是操作数据库,并不需要写入目录,所以不受影响。

卸载阿里云盾和云监控

卸载阿里云盾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
wget http://update.aegis.aliyun.com/download/uninstall.sh && chmod +x uninstall.sh && ./uninstall.sh
wget http://update.aegis.aliyun.com/download/quartz_uninstall.sh && chmod +x quartz_uninstall.sh && ./quartz_uninstall.sh
pkill aliyun-service
rm -fr /etc/init.d/agentwatch /usr/sbin/aliyun-service
rm -rf /usr/local/aegis*
iptables -I INPUT -s 140.205.201.0/28 -j DROP
iptables -I INPUT -s 140.205.201.16/29 -j DROP
iptables -I INPUT -s 140.205.201.32/28 -j DROP
iptables -I INPUT -s 140.205.225.192/29 -j DROP
iptables -I INPUT -s 140.205.225.200/30 -j DROP
iptables -I INPUT -s 140.205.225.184/29 -j DROP
iptables -I INPUT -s 140.205.225.183/32 -j DROP
iptables -I INPUT -s 140.205.225.206/32 -j DROP
iptables -I INPUT -s 140.205.225.205/32 -j DROP
iptables -I INPUT -s 140.205.225.195/32 -j DROP
iptables -I INPUT -s 140.205.225.204/32 -j DROP

卸载云监控(不建议,卸载后将无法在管理面板查看内存和硬盘信息):

1
2
3
/usr/local/cloudmonitor/wrapper/bin/cloudmonitor.sh stop
/usr/local/cloudmonitor/wrapper/bin/cloudmonitor.sh remove && \
rm -rf /usr/local/cloudmonitor

exec()被禁用

如果在安装插件时,出现exec()函数被禁用的提示,只需要编辑php.ini,找到disable_functions,去掉exec,重启Nginx即可。

更新失败。错误信息:此响应不是合法的JSON响应。

一切妥当,想要写一篇文章测试一下,却发现无法保存草稿,也无法提交。WordPress报错:“更新失败。错误信息:此响应不是合法的JSON响应。”

根据这篇文章的解释,我需要配置Nginx的伪静态。但是在LNMP的脚本中,伪静态只能在添加虚拟主机(vhost)时设置,由于备案还未完成,我的WordPress暂时使用了默认主机(default server)。所以稍微研究了一下Nginx的配置文件规则。

稍稍阅读Nginx的配置文件,发现虚拟主机和默认主机的配置其实是非常的相似,都是server {...}的格式。我的理解,就是虚拟主机就是默认主机之外的web服务器,可以设置不同的web根目录、不同的域名过滤条件、不同的端口、不同的SSL证书之类的;因为都在同一个Nginx里面,是虚拟出来的,所以叫虚拟主机。

参考这篇文章,可以发现只要在默认主机的配置里面加一个include来引用LNMP自带的WordPress规则就可以实现默认主机的伪静态。我顺便看了一下rewrite/wordpress.conf这个规则,发现里面只有短短几行:

1
2
3
4
5
6
location / {
try_files $uri $uri/ /index.php?$args;
}

# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;

所以我就干脆把这几行直接复制到了Nginx配置文件中,重启Nginx,发现WordPress不报错了。问题解决!

那么伪静态是什么?知乎上已经解释得很白话了,这里就不再赘述。

添加HTTPS支持、设置自动跳转

过了大概两个礼拜,网站终于通过了ICP备案,可以通过域名访问了,我开始配置SSL证书。SSL证书的话我选择的是免费的Let’s Encrypt证书,支持自动签发,LNMP支持全自动配置。但是这有一个前提,就是你的服务器的80端口能够通过域名访问(这是为了证明服务器和域名确实是你的)。因为备案通过前服务器不能用域名访问,所以部署HTTPS也得在备案后进行。

备案通过后我就开始部署HTTPS。首先利用LNMP脚本创建虚拟主机:

1
lnmp vhost add

在接下来的向导中,domain一栏我写了www.tomzhu.sitemore domain name一栏我写了tomzhu.site。Nginx能够通过HTTP Header中的信息判断出请求的域名,并以此选择匹配的主机来响应。我这样填写,是因为我希望带有www和不带www的请求都可以访问WordPress。接下来的几项按照截图填写即可:

添加虚拟主机

这里的Rewrite Rules其实就是include了之前我们复制到主配置文件的那些rewrite/wordpress.conf的规则。这次我们让LNMP自动引用规则就行了,不需要自己去include或者复制了。最后我们看到LNMP已经准备好了,按下任意按键就会开始自动申请SSL证书并部署。

很快虚拟主机就部署好了,我们这个时候需要把/home/wwwroot/default里的所有文件(注意,需要排除.user.ini这个文件)复制到/home/wwwroot/www.tomzhu.site中。

另外,我还希望HTTP能够自动跳转到HTTPS,请求域名不带www能够自动加上www。所以我对Nginx主配置文件(nginx.conf)和虚拟主机配置文件(vhost/www.tomzhu.site.conf)进行了如下修改。

在 Nginx主配置文件(nginx.conf) 中找到唯一的server {}片段,在最后加上一句:

1
rewrite ^(.*)$ https://www.tomzhu.site/$1 permanent;

如果一个请求不匹配任何虚拟主机的条件(比如用IP访问),它就会被默认主机处理。这个语句意思就是直接跳转到https://www.tomzhu.site/

再来到虚拟主机配置文件(vhost/www.tomzhu.site.conf),找到第一个server {}片段,从listen 80可以看出这是HTTP协议的虚拟主机,在最后加上一句:

1
rewrite ^(.*)$ https://www.tomzhu.site/$1 permanent;

与上面的情况相似,这个语句能够把所有HTTP请求重定向到HTTPS上,也就是所说的强制HTTPS。

最后,找到第二个server {}片段,从listen 443 ssl可以看出这是HTTPS协议的虚拟主机(Nginx如何判断一个主机是HTTP还是HTTPS?请参照官方文档),在最后加上一句:

1
2
3
if ($host != 'www.tomzhu.site' ){ 
rewrite ^/(.*)$ https://www.tomzhu.site/$1 permanent;
}

这个语句检查请求的域名,如果不带www,就会重定向到带有www的域名上来,也就是自动添加www。

保险起见,最后重启一下Nginx。 最后一步,打开WordPress->设置->常规,设置一下URL。

关于Nginx中各个主机的优先级,可以阅读这篇文章