Cesium之Viewer

Viewer用于构建应用程序的基本构件。它将所有标准Cesium小部件合成为一个可重用的包。

new Cesium.Viewer(container, options)
参数 类型 说明
container Element|String 用于包含viewer组件的DOM对象或者容器ID
options Object 选项
var viewer = new Cesium.Viewer('cesiumContainer', {
    //Start in Columbus Viewer
    sceneMode : Cesium.SceneMode.SCENE3D, //COLUMBUS_VIEW|MORPHING|SCENE2D|SCENE3D
    animation:true, 
    baseLayerPicker:true,
    fullscreenButton:true,
    geocoder:true,
    homeButton:true,
    infoBox:true,
    selectionIndicator:true,
    timeline:true,
    vrButton:true,
});

成员

Scene new Cesium.Scene(options)

虚拟场景中所有3D 图形对象和状态的容器。

var scene = viewer.scene;

var scene = new Cesium.Scene({
  canvas : canvas,
  contextOptions : {
    allowTextureFilterAnisotropic : false
}});

Camera:new Cesium.Camera(scene)

方法

flyto

flyTo(target, options)  Promise.<Boolean>

飞向提供的实体、实体或数据源。如果数据源仍在加载过程中, 或者可视化仍在加载, 则此方法等待数据在执行该飞行之前准备就绪。

参数 选项
target Entity | Array. | EntityCollection | DataSource | ImageryLayer | Cesium3DTileset | Promise.<(Entity|Array.|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset)>
options Object

zoomTo

zoomTo(target, offset)  Promise.<Boolean>
参数 选项
target Entity | Array. | EntityCollection | DataSource | ImageryLayer | Cesium3DTileset | Promise.<(Entity|Array.|EntityCollection|DataSource|ImageryLayer|Cesium3DTileset)>
offset HeadingPitchRange
var viewer = new Cesium.Viewer('cesiumContainer');

var park = viewer.entities.add({
    name : '故宫',
    position : Cesium.Cartesian3.fromDegrees(116.3907222800, 39.9122117400),
    point : {
        pixelSize : 5,
        color : Cesium.Color.RED,
        outlineColor : Cesium.Color.WHITE,
        outlineWidth : 2
    },
    label : {
        text : '故宫',
        font : '14pt monospace',
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        outlineWidth : 2,
        verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
        pixelOffset : new Cesium.Cartesian2(0, -9)
    }
});
viewer.zoomTo(park);

运行上面代码,效果如下:

增加下面的代码:

park.description = '\
<img\
  width="50%"\
  style="float:left; margin: 0 1em 1em 0;"\
  src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1529492753687&di=35d9d1a8f2834c7457353091a80dd969&imgtype=0&src=http%3A%2F%2Fpic23.photophoto.cn%2F20120419%2F0033033921468983_b.jpg"/>\
<p>\
  北京是中国的首都.\
</p>\
<p>\
  链接: \
  <a style="color: WHITE"\
    target="_blank"\
    href="http://www.baidu.com">百度</a>\
</p>';

双击故宫会弹出简介层:

capistrano简介

用ruby写的用于远程自动推送部署工具。官网 github

cap install

自动生成部署文件,结构如下:

├── Capfile
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
├── lib
│   └── capistrano
│   └── tasks
└── log
└── capistrano.log

production.rb、staging.rb对应不同环境的服务器及配置。

cap production deploy

服务器推送后的目录如下:

├── current -> /tmp/stock/releases/20180102061709
├── releases
│   ├── 20171229041438
│   └── 20180102061709
├── repo
├── revisions.log
└── shared

ubuntu无法开机自启动nginx

阿里云到期了涨价,只好申请了降配续费。阿里自动重启服务器后,发现nginx没有开机自启动。

guoj@iZ2ze8wsk0m120dsi7m0juZ:~$ sudo runlevel
N 5

配好好一切后,重启服务器,奇迹并没有发生,nginx还是没有启动。

guoj@iZ2ze8wsk0m120dsi7m0juZ:~$ sudo cat /etc/init.d/nginx 
#!/bin/sh

### BEGIN INIT INFO
# Provides:       nginx
# Required-Start:    $local_fs $remote_fs $network $syslog $named
# Required-Stop:     $local_fs $remote_fs $network $syslog $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/nginx
NAME=nginx
DESC=nginx

# Include nginx defaults if available
if [ -r /etc/default/nginx ]; then
        . /etc/default/nginx
fi

STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"

test -x $DAEMON || exit 0

. /lib/init/vars.sh
. /lib/lsb/init-functions

# Try to extract nginx pidfile
PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]" } { if ($1 == "pid") print $2 }' | head -n1)
if [ -z "$PID" ]; then
        PID=/run/nginx.pid
fi

if [ -n "$ULIMIT" ]; then
        # Set ulimit if it is set in /etc/default/nginx
        ulimit $ULIMIT
fi

start_nginx() {
        # Start the daemon/service
        #
        # Returns:
        #   0 if daemon has been started
        #   1 if daemon was already running
        #   2 if daemon could not be started
        start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test &gt; /dev/null \
                || return 1
        start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
                $DAEMON_OPTS 2&gt;/dev/null \
                || return 2
}

test_config() {
        # Test the nginx configuration
        $DAEMON -t $DAEMON_OPTS &gt;/dev/null 2&gt;&1
}

stop_nginx() {
        # Stops the daemon/service
        #
        # Return
        #   0 if daemon has been stopped
        #   1 if daemon was already stopped
        #   2 if daemon could not be stopped
        #   other if a failure occurred
        start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
        RETVAL="$?"
        sleep 1
        return "$RETVAL"
}

reload_nginx() {
        # Function that sends a SIGHUP to the daemon/service
        start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME
        return 0
}

rotate_logs() {
        # Rotate log files
        start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
        return 0
}

upgrade_nginx() {
        # Online upgrade nginx executable
        # http://nginx.org/en/docs/control.html
        #
        # Return
        #   0 if nginx has been successfully upgraded
        #   1 if nginx is not running
        #   2 if the pid files were not created on time
        #   3 if the old master could not be killed
        if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then
                # Wait for both old and new master to write their pid file
                while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do
                        cnt=`expr $cnt + 1`
                        if [ $cnt -gt 10 ]; then
                                return 2
                        fi
                        sleep 1
                done
                # Everything is ready, gracefully stop the old master
                if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then
                        return 0
                else
                        return 3
                fi
        else
                return 1
        fi
}

case "$1" in
        start)
                log_daemon_msg "Starting $DESC" "$NAME"
                start_nginx
                case "$?" in
                        0|1) log_end_msg 0 ;;
                        2)   log_end_msg 1 ;;
                esac
                ;;
        stop)
                log_daemon_msg "Stopping $DESC" "$NAME"
                stop_nginx
                case "$?" in
                        0|1) log_end_msg 0 ;;
                        2)   log_end_msg 1 ;;
                esac
                ;;
        restart)
                log_daemon_msg "Restarting $DESC" "$NAME"

                # Check configuration before stopping nginx
                if ! test_config; then
                        log_end_msg 1 # Configuration error
                        exit $?
                fi

                stop_nginx
                case "$?" in
                        0|1)
                                start_nginx
                                case "$?" in
                                        0) log_end_msg 0 ;;
                                        1) log_end_msg 1 ;; # Old process is still running
                                        *) log_end_msg 1 ;; # Failed to start
                                esac
                                ;;
                        *)
                                # Failed to stop
                                log_end_msg 1
                                ;;
                esac
                ;;
        reload|force-reload)
                log_daemon_msg "Reloading $DESC configuration" "$NAME"

                # Check configuration before stopping nginx
                #
                # This is not entirely correct since the on-disk nginx binary
                # may differ from the in-memory one, but that's not common.
                # We prefer to check the configuration and return an error
                # to the administrator.
                if ! test_config; then
                        log_end_msg 1 # Configuration error
                        exit $?
                fi

                reload_nginx
                log_end_msg $?
                ;;
        configtest|testconfig)
                log_daemon_msg "Testing $DESC configuration"
                test_config
                log_end_msg $?
                ;;
        status)
                status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
                ;;
        upgrade)
                log_daemon_msg "Upgrading binary" "$NAME"
                upgrade_nginx
                log_end_msg $?
                ;;
        rotate)
                log_daemon_msg "Re-opening $DESC log files" "$NAME"
                rotate_logs
                log_end_msg $?
                ;;
        *)
                echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" &gt;&2
                exit 3
                ;;
esac

手动启动/etc/init.d/nginx start也是没起来。systemctl start nginx报错如下:

root@iZ2ze8wsk0m120dsi7m0juZ:/var/log# systemctl start nginx
Failed to start nginx.service: Unit nginx.service is masked.

执行下面命令:

systemctl unmask nginx.service
systemctl unmask nginx.socket
systemctl start nginx.service

使用

/etc/init.d/nginx start

systemctl start nginx

都OK啦。

guoj@iZ2ze8wsk0m120dsi7m0juZ:~$ systemctl status nginx
● nginx.service - LSB: starts the nginx web server
   Loaded: loaded (/etc/init.d/nginx; bad; vendor preset: enabled)
   Active: active (running) since Thu 2018-06-28 10:22:53 CST; 59min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 634 ExecStart=/etc/init.d/nginx start (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/nginx.service
           ├─1483 nginx: master process /usr/sbin/ngin
           ├─1484 nginx: worker proces
           └─1485 nginx: worker proces

最后,再吐槽一下阿里云涨价!降低了一半的配置年费还比原先的贵!真坑爹!

Cesium简介

基于几种新的HTML5技术构建的,其中最重要的是WebGL。
官网
github
API文档

支持Google Chrome、Firefox、Internet Explorer 11、Opera。

浏览器打开下面页面,查看是否支持:浏览器测试

  • install node.js

  • 下载代码并解压后进到根目录。

  • 运行npm install。

  • 运行npm start。(也可运行node server.js或者通过nginx、apache等web服务器加载当前目录)

cesium应用的基础是viewer,创建viewer并绑定在id=“cesiumContainer"的DIV上即可。

var viewer = new Cesium.Viewer('cesiumContainer');

运行上面代码显示如下:

关于Ubuntu系统中mysql的用户debian-sys-maint

最近在Ubuntu16上安装nodejs,发生错误如下:

mysql_upgrade: Got error: 1045: Access denied for user ‘debian-sys-maint’@’localhost’ (using password: YES) while connecting to the MySQL server

Do you want to continue? [Y/n] y
Setting up mysql-server-5.7 (5.7.21-0ubuntu0.16.04.1) ...
mysql_upgrade: Got error: 1045: Access denied for user 'debian-sys-maint'@'localhost' (using password: YES) while connecting to the MySQL server
Upgrade process encountered error and will not continue.
mysql_upgrade failed with exit status 11
dpkg: error processing package mysql-server-5.7 (--configure):
 subprocess installed post-installation script returned error exit status 1
dpkg: dependency problems prevent configuration of mysql-server:
 mysql-server depends on mysql-server-5.7; however:
  Package mysql-server-5.7 is not configured yet.

dpkg: error processing package mysql-server (--configure):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 mysql-server-5.7
 mysql-server
E: Sub-process /usr/bin/dpkg returned an error code (1)

这个mysql用户debian-sys-main是个什么鬼?为什么mysql_upgrade会用这个用户连接,而且为什么又会密码不对?

原来,在MySQL服务器的Debian/Ubuntu发行版下,该软件包依赖于一个特殊的mysql用户,用于名为debian-sys-maint的init脚本。用户的密码在/etc/mysql/debian-my.cnf中

root@iZ2ze8wsk0m120dsi7m0juZ:/# cat /etc/mysql/debian.cnf 
# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = aVKpepDSPym1V0nb
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host     = localhost
user     = debian-sys-maint
password = aVKpepDSPym1V0nb
socket   = /var/run/mysqld/mysqld.sock

默认情况,还用于日志的处理。参考/etc/logrotate.d/mysql-server:

root@iZ2ze8wsk0m120dsi7m0juZ:/# cat /etc/logrotate.d/mysql-server 
# - I put everything in one block and added sharedscripts, so that mysql gets 
#   flush-logs'd only once.
#   Else the binary logs would automatically increase by n times every day.
# - The error log is obsolete, messages go to syslog now.
/var/log/mysql.log /var/log/mysql/*log {
        daily
        rotate 7
        missingok
        create 640 mysql adm
        compress
        sharedscripts
        postrotate
                test -x /usr/bin/mysqladmin || exit 0
                # If this fails, check debian.conf! 
                MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
                if [ -z "`$MYADMIN ping 2&gt;/dev/null`" ]; then
                  # Really no mysqld or rather a missing debian-sys-maint user?
                  # If this occurs and is not a error please report a bug.
                  #if ps cax | grep -q mysqld; then
                  if killall -q -s0 -umysql mysqld; then
                    exit 1
                  fi 
                else
                  $MYADMIN flush-logs
                fi
        endscript
}

并且,在/etc/init.d/mysql脚本中也是使用这个作为默认配置文件:

...................
test -x /usr/bin/mysqld_safe || exit 0

. /lib/lsb/init-functions

SELF=$(cd $(dirname $0); pwd -P)/$(basename $0)
CONF=/etc/mysql/my.cnf
MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
...................

mysql-server-5.7.README.Debian有下面的描述:

* MYSQL WON'T START OR STOP?:
=============================
You may never ever delete the special mysql user "debian-sys-maint". This
user together with the credentials in /etc/mysql/debian.cnf are used by the
init scripts to stop the server as they would require knowledge of the mysql
root users password else.
So in most of the times you can fix the situation by making sure that the
debian.cnf file contains the right password, e.g. by setting a new one
(remember to do a "flush privileges" then).

当MySQL首次安装在Ubuntu/Debian上时,随机地为debian-sys-maint用户创建了一个密码,在MySQL中创建用户(在初始安装期间根MySQL用户没有密码,因此能够以root身份登录),并在系统上创建/etc/mysql/debian-my.cnf文件。使用Ubuntu/Debian软件包升级MySQL时,将使用debian-sys-maint用户,密码已存储在/etc/mysql/debian-my.cnf中。如果密码不正确,升级将会成功并出现一些警告,但用户不会自动重新创建。

解决方案:

那么解决方案是什么?那么,有办法来处理它。一种是在使用初始化脚本启动MySQL时忽略错误,并使用具有适当权限的用户使用mysqladmin关闭MYSQL。另一种方法是使用/etc/mysql/debian-my.cnf文件中的信息在MySQL中创建必要的debian-sys-maint用户(或更新用户的密码)。

GRANT ALL PRIVILEGES on *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
flush privileges;
root@iZ2ze8wsk0m120dsi7m0juZ:/# apt-get update
.....................

root@iZ2ze8wsk0m120dsi7m0juZ:/# apt-get install nodejs
Reading package lists... Done
Building dependency tree       
Reading state information... Done
nodejs is already the newest version (4.2.6~dfsg-1ubuntu4.1).
0 upgraded, 0 newly installed, 0 to remove and 109 not upgraded.
2 not fully installed or removed.
After this operation, 0 B of additional disk space will be used.
Do you want to continue? [Y/n] y
Setting up mysql-server-5.7 (5.7.21-0ubuntu0.16.04.1) ...
Checking if update is needed.
Checking server version.
Running queries to upgrade MySQL server.
Checking system database.
mysql.columns_priv                                 OK
mysql.db                                           OK
mysql.engine_cost                                  OK
mysql.event                                        OK
mysql.func                                         OK
mysql.general_log                                  OK
mysql.gtid_executed                                OK
mysql.help_category                                OK
......................................
Upgrade process completed successfully.
Checking if update is needed.
Setting up mysql-server (5.7.21-0ubuntu0.16.04.1) ...
...................

日志文件总管logrotate

logrotate官网

简介

logrotate是个十分有用的工具,它可以自动对日志进行截断、轮循、压缩以及删除旧的日志文件。配置完后,logrotate的运作完全自动化,不必进行任何人为干预。 配置文件通常在/etc/logrotate.conf中,独立配置通常在/etc/logrotate.d/下。

配置

/var/log/mysql.log /var/log/mysql/*log {
        daily
        rotate 7
        missingok
        create 640 mysql adm
        compress
        sharedscripts
        postrotate
                test -x /usr/bin/mysqladmin || exit 0
                MYADMIN="/usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf"
                if [ -z "`$MYADMIN ping 2&gt;/dev/null`" ]; then
                  if killall -q -s0 -umysql mysqld; then
                    exit 1
                  fi 
                else
                  $MYADMIN flush-logs
                fi
        endscript
}

手动运行

logroute -f /et/logrotate.conf

定时任务

logrotate需要的cron任务应该在安装时就自动创建了:/etc/cron.daily/logrotate

root@iZ2ze8wsk0m120dsi7m0juZ:/# cat /etc/cron.daily/logrotate 
#!/bin/sh

# Clean non existent log file entries from status file
cd /var/lib/logrotate
test -e status || touch status
head -1 status &gt; status.clean
sed 's/"//g' status | while read logfile date
do
    [ -e "$logfile" ] && echo "\"$logfile\" $date"
done &gt;&gt; status.clean
mv status.clean status

test -x /usr/sbin/logrotate || exit 0
/usr/sbin/logrotate /etc/logrotate.conf

logrotate自身日志在/var/lib/logrotate/status

root@iZ2ze8wsk0m120dsi7m0juZ:/# cat /var/lib/logrotate/status 
logrotate state -- version 2
"/var/log/messages" 2018-3-14-6:0:0
"/var/log/user.log" 2018-3-14-6:0:0
"/var/log/apt/history.log" 2018-3-1-6:25:1
"/var/log/nginx/error.log" 2018-3-10-6:25:2
"/var/log/alternatives.log" 2018-3-1-6:25:1
"/var/log/debug" 2018-3-14-6:0:0
"/var/log/php7.0-fpm.log" 2018-3-14-6:0:0
"/var/log/wtmp" 2018-3-1-6:25:1
"/var/log/apache2/other_vhosts_access.log" 2017-5-17-6:0:0
"/var/log/ufw.log" 2018-3-14-6:0:0
"/var/log/mysql/slow.log" 2018-3-14-6:25:1
"/var/log/syslog" 2018-3-14-6:25:1
"/var/log/mail.log" 2018-3-11-6:25:1
"/var/log/php7.1-fpm.log" 2018-3-14-6:0:0
"/var/log/apt/term.log" 2018-3-1-6:25:1
"/var/log/unattended-upgrades/unattended-upgrades.log" 2018-3-1-6:25:1
"/var/log/mysql/error.log" 2018-3-14-6:25:1
"/var/log/mongodb/mongodb.log" 2018-3-11-6:25:1
"/var/log/kern.log" 2018-3-4-6:25:1
"/var/log/apache2/error.log" 2017-6-20-6:25:1
"/var/log/php7.2-fpm.log" 2018-3-11-6:25:1
"/var/log/mail.info" 2018-3-14-6:0:0
"/var/log/lpr.log" 2018-3-14-6:0:0
"/var/log/mysql.log" 2018-3-14-6:0:0
"/var/log/php5.6-fpm.log" 2018-3-14-6:0:0
"/var/log/nginx/access.log" 2018-3-10-6:25:2
"/var/log/daemon.log" 2018-3-14-6:0:0
"/var/log/mail.warn" 2018-3-14-6:0:0
"/var/log/fail2ban.log" 2018-3-11-6:25:1
"/var/log/btmp" 2018-3-1-6:25:1
"/var/log/apache2/access.log" 2017-6-20-6:25:1
"/var/log/mail.err" 2018-3-14-6:0:0
"/var/log/cron.log" 2018-3-14-6:0:0
"/var/log/dpkg.log" 2018-3-1-6:25:1
"/var/log/auth.log" 2018-3-11-6:25:1
"/var/log/unattended-upgrades/unattended-upgrades-shutdown.log" 2017-12-25-6:0:0
"/var/log/unattended-upgrades/unattended-upgrades-dpkg.log" 2018-3-1-6:25:1
"/var/log/ppp-connect-errors" 2018-3-14-6:0:0
"/var/log/tomcat8/catalina.out" 2018-3-11-6:25:1

logstash多个配置文件数据混乱问题

程序自始至终只有一个pipline。要想多个pipline,只能通过启动多个logstash实例。
当指定的目录下的所有配置文件启动时,Logstash会自动把多个文件合并在一个文件中。
可以通过cat **.conf > /tmp/tmp.conf查看多个配置文件合并后的效果,排查问题。
假如有个2配置文件,output又指向同一源时,可以拆成3个文件来处理。

input1.conf

input { ....... .}
  filter { 
    if [type] == "1"{ 
       grok { ...........} 
    } 
}

input2.conf

input { ....... .}
  filter { 
    if [type] == "2"{ 
      grok { ...........} 
    } 
}

output.conf

output { 
  ................ 
}

nginx访问频次限制模块

nginx_http_limit_conn_zone模块

用于限制并发连接数,特别是单个IP​​地址的连接数量。 并非所有连接都被计算在内。只有在服务器处理了请求并且已经读取了整个请求头的情况下才计算连接。官方文档

用法

Syntax:limit_conn zone number;
Default:—
Context:http, server, location

实例

http{
	limit_conn_zone $binary_remote_addr zone=addr:10m;
	limit_conn_zone $binary_remote_addr zone=perip:10m;
	limit_conn_zone $server_name zone=perserver:10m;

	server{
		limit_conn perip 50;
		limit_conn perserver 50;
		limit_conn addr 1;
	}
}

说明

$remote_addr变量的大小可以从7到15个字节不等。存储状态在32位平台上占用32或64字节的内存,在64位平台上总是占用64字节。 $binary_remote_addr对于IPv4地址,变量的大小始终为4个字节,对于IPv6地址则为16个字节。存储状态在32位平台上始终占用32或64个字节,在64位平台上占用64个字节。1M字节的区域可以保持大约32000个32字节的状态或大约16000个64字节的状态。

如果共享内存空间被耗尽,服务器将会对后续所有的请求返回 503 (Service Temporarily Unavailable) 错误。

由于一个页面有多个子资源,如果毫无选择的都进行限制,那就会出现很多不必要的麻烦,如:一个页面有40个子资源,那么如果想让一个页面完整的显示,就需要将请求速度和连接数都调整到40,以此达到不阻塞用户正常请求。

所以制定哪些请求是需要进行限制的,如html页面;哪些是不需要限制的,如css、js、图片等,这样就需要通过配置对应的location进一步细化。

nginx_http_limit_req_zone模块

用于限制请求处理速率,特别是来自单个IP地址的请求的处理速率。官方文档

用法

Syntax:limit_req zone=name [burst=number] [nodelay];
Default:—
Context:http, server, location

设置共享内存区域和请求的最大突发大小。如果请求速率超过为某个区域配置的速率,则它们的处理会延迟,从而使请求以定义的速率处理。过多的请求被延迟,直到它们的数量超过最大突发大小,在这种情况下请求被终止并出现错误。默认情况下,最大突发大小等于零。

实例

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    server {
        location /search/ {
            limit_req zone=one burst=5;
        }
}

平均每秒不超过1个请求,并且突发不超过5个请求。 如果请求限制期间延迟请求过多,则不需要使用参数nodelay:

limit_req zone=one burst=5 nodelay;

MySQL对包含NULL字段排序

MySQL5.7文档-order

CREATE TABLE `sorted` (
	`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
	`idx` INT(11) UNSIGNED NULL DEFAULT NULL,
	PRIMARY KEY (`id`)
)

INSERT INTO `sorted` (`id`, `idx`) VALUES (1, 2);
INSERT INTO `sorted` (`id`, `idx`) VALUES (2, NULL);
INSERT INTO `sorted` (`id`, `idx`) VALUES (3, 3);
INSERT INTO `sorted` (`id`, `idx`) VALUES (4, 1);
INSERT INTO `sorted` (`id`, `idx`) VALUES (5, 1);
INSERT INTO `sorted` (`id`, `idx`) VALUES (6, 4);
INSERT INTO `sorted` (`id`, `idx`) VALUES (7, 3);
INSERT INTO `sorted` (`id`, `idx`) VALUES (8, NULL);
INSERT INTO `sorted` (`id`, `idx`) VALUES (9, 0);

NULL被视为低于任何非NULL值。

mysql> select * from sorted;
+----+------+
| id | idx  |
+----+------+
|  1 |    2 |
|  2 | NULL |
|  3 |    3 |
|  4 |    1 |
|  5 |    1 |
|  6 |    4 |
|  7 |    3 |
|  8 | NULL |
|  9 |    0 |
+----+------+
9 rows in set (0.00 sec)

方案一:

mysql> select id, idx from sorted order by -idx desc;
+----+------+
| id | idx  |
+----+------+
|  9 |    0 |
|  4 |    1 |
|  5 |    1 |
|  1 |    2 |
|  3 |    3 |
|  7 |    3 |
|  6 |    4 |
|  2 | NULL |
|  8 | NULL |
+----+------+
9 rows in set (0.00 sec)

方案二:

mysql> select id,idx from sorted order by isnull(idx), idx;
+----+------+
| id | idx  |
+----+------+
|  9 |    0 |
|  4 |    1 |
|  5 |    1 |
|  1 |    2 |
|  3 |    3 |
|  7 |    3 |
|  6 |    4 |
|  2 | NULL |
|  8 | NULL |
+----+------+
9 rows in set (0.00 sec)

方案三:

mysql> select id,idx from sorted order by if(isnull(idx), 0, 1) desc, idx;
+----+------+
| id | idx  |
+----+------+
|  9 |    0 |
|  4 |    1 |
|  5 |    1 |
|  1 |    2 |
|  3 |    3 |
|  7 |    3 |
|  6 |    4 |
|  2 | NULL |
|  8 | NULL |
+----+------+
9 rows in set (0.00 sec)

HTTP ETag

Etag全称EntityTags,HTTP1.1中才加入的,Response Header一个属性,用来帮助服务器控制Web端的缓存验证。由服务器端生成,并且随着文件的改变而改变。HTTP规范从未指定生成ETag的方法。

ETag是一个不透明的标识符,由Web服务器根据URL上的资源的特定版本而指定。如果那个URL上的资源内容改变,一个新的不一样的ETag就会被分配。用这种方法使用ETag即类似于指纹,并且他们能够被快速地被比较,以确定两个版本的资源是否相同。ETag的比较只对同一个URL有意义——不同URL上的资源的ETag值可能相同也可能不同,从他们的ETag的比较中无从推断。

既然已经有了Last_Modified,为什么还需要ETag呢?因为UNIX记录MTIME只能精确到秒,假如1秒内修改N次,Last_Modified就无法应对了。

Examples

ETag: W/"<etag_value&gt>" --弱校验ETag
ETag: "<etag_value>"  --强校验ETag

强校验的ETag匹配要求两个资源内容的每个字节需完全相同,包括所有其他实体字段(如Content-Language)不发生变化。允许重新装配和缓存部分响应,以及字节范围请求。

弱校验的ETag匹配要求两个资源在语义上相等,这意味着在实际情况下它们可以互换,而且缓存副本也可以使用。不过这些资源不需要每个字节相同,因此弱ETag不适合字节范围请求。当Web服务器无法生成强ETag不切实际的时候,比如动态生成的内容,弱ETag就可能发挥作用了。

原理

  • 客户端请求服务器资源A时,服务器计算出一个哈希值作为ETag的值放在Response Header中返回给客户端,客户端缓存资源A和它的ETag。
ETag: "12345678abcdefg"
  • 客户端再次请求资源A时,把缓存的ETag值放在Request Header的If-None-Match属性中。
If-None-Match: "12345678abcdefg"
  • 服务器再次计算A的哈希值并和浏览器请求的值做比较,若资源没有变化,则哈希值一致,就会返回304 Not Modified,客户端使用本地缓存。

304会返回空响应体,客户端会从本地缓存中取数据。

nginx配置

nginx在1.3.3版本加入后默认开启。