分类 其他 下的文章

一、刷breed

breed是一个专门用于刷固件的后台,支持对系统当前的数据进行操作。breed是刷固件最重要的一步,K2系统默认的版本已经做了限制,不能直接刷上breed,需要在网上找教程刷。

我手里的这个路由器原来刷过,因此这里略过这一步。

进去breed的方法

  1. 拔掉路由器电源,按住重置键,然后插上电源线,通电后10秒钟左右松开重置键。
  2. PC通过网线直连路由器,访问http://192.168.1.1

breed首页:

image72bde19e2f98c417.png

二、下载固件

相关资源地址:

下载固件在百度网盘下载,K2的固件名字是:RT-AC54U-GPIO-1-PSG1218-64M,不要下错了。

三、刷入固件

进入breed,固件更新页面选择下载的固件,然后点击上传(注意勾上自动重启选项):

imagedfbeb6a99ac68291.png

确认固件信息,主要是对比MD5是否正确,原始的MD5在百度网盘中有下载。确认无误后点击更新开始刷固件:

imagef959b6c3faaa4d55.png

刷固件需要1-2分钟时间,耐心等待,不要断开电源:

image9da269ad0e96538f.png

刷入完成后,设备自动重启,等待一两分钟系统启动:

imagec4088814141c788a.png

Padavan固件的首页是192.168.123.1,默认密码是admin/admin,启动后在浏览器直接访问:

imagec25d77c20d6085ba.png

大功告成!

四、其他

一、问题描述

搭了一个开源图床,因为备案的原因部署在海外。访问速度太慢准备上CDN(有一个已备案的域名),但是在部署CDN的途中就出现了各种问题。鼓捣了几天终于解决了,记录下踩坑记录和解决方案。

软件环境:

图床是直接用docker部署了,没有再手动去折腾lnmp环境了,别人已经准备好的docer环境拿过来就直接用了。

用一个nginx作为CDN和图床服务的代理客户端,不直接把图床暴露出来了,也是一般服务的部署方式。

平常的服务大部分都是这么干的,用起来很舒服。

这个部署在http环境下是没有问题的,能正常使用。但是把http改成https的时候就出问题了,一开始的问题就是跨域:

因为nginx是配置的http反向代理:proxy_pass http://127.0.0.1:xxxx,到docker中的也是http,所以它返回到前端的js/css资源也是http的。而此时CDN开启了强制https,浏览器中已经是https,跨域就这么产生了。

为了解决这个问题,就在docker的web环境中也加一个强制https跳转,想着apache也强制https,那么就不会返回http资源,跨域解决。

想象是美好的,现实是残酷的,这么一配置之后,CDN就挂了,原因:重定向次数过多

于是分析了一下,这么干确实有问题:

  1. nginx反向代理是用的http,对apache来说它收到了http请求,于是302重定向到https的页面。
  2. 然后CDN就访问重定向的https的页面,但是nginx收到之后又代理到http。

于是乎,CDN和apache之间就产生了某种误会,导致302死循环诞生!因此,这么干不行!得另寻它法。

那么现在的问题就产生了:如何圆润的以https的方式访问到docker内部的资源?

方案一:nginx和apache改成https通信

这个方案要做的工作:

  1. 手动生成https证书
  2. 在apache内部署https
  3. nginx和apache通过https代理

apache不熟悉,看了https部署比nginx要麻烦一些,而且内部通信也用https对性能有影响,所以就不考虑了(虽然这个方法可能比较有效,但是内心比较排斥apache,甚至想把docker中的apache换掉都不想用它配置https)。

方案二:使用nginx的sub_filter替换http为https

验证了一下,不可行,因为实际上返回到客户端的资源文件都是相对文件目录,不是http的全路径资源。

于是就崩了,百度了几天也没有办法解决。最后,在google上搜,竟然一下就找到了解决方案。

二、完美解决方案

设置http头部X-Forwarded-Proto,这个头部的作用是用于识别协议(HTTP 或 HTTPS),主要针对内部访问重定向时的协议。因此,只要在反向代理时添加以下配置就好了:

proxy_set_header X-Forwarded-Proto $scheme;

$scheme是nginx的内部变量,表明当前访问的协议,当前如果是https,那么转发到后台服务的时候就是https。这样问题就解决了。

一、编程和语言

1.1 C/C++

指针数组、数组指针、函数指针的概念

什么是结构体对齐

静态数组和动态数组区别

​ 静态数组是编译时就分配好大小的数组,内存在栈区(全局数组在全局区),能分配的大小有限,受限于栈的大小。动态是程序运行后动态调用malloc或者new生成的,内存在堆区,可分配大小相较于栈区较大。

static的作用

sizeof的作用

C++

  • 重载和重写的区别
    答:重载时同一个函数的不同实现方式,通过入参的不同调用不同的函数(重载函数不能以返回值为区别)。重写是指子类覆盖父类的函数,隐藏父类实现达到多态的机制。
  • 运算符重载
  • C++中virtual关键字的作用
    virtual是实现多态的前提,对函数使用virtual将在类中产生虚函数表。对类使用virtual该类将变成虚基类。
  • 菱形继承:B和C继承A,D继承B和C)时每个对象的空间结构分布,比如问D有几份虚表,D中B和C的成员空间排布。
  • 指针和引用的区别
  • new/delete/new[]/delete []/malloc/free的区别
  • RAII/pimpl的惯用方式
  • 构造函数的调用顺序
  • 静态类静态方法
  • 智能指针的实现原理

STL

  • vecotr 和list 的区别,适用情况
  • vector的扩容机制
  • set承载结构体
  • 迭代器的使用方式,迭代器失效场景

其他

  • 程序的内存布局:(堆、栈、静态/全局/局部变量)
  • 函数的堆栈,调用函数时参数如何入栈。
  • 内存泄漏的原因及避免措施
    内存泄漏的原因是因为申请了内存没有被释放掉,写代码时应有良好的编程习惯,对申请的内存一定要即时释放
  • gdb的使用方法(断点、查看内存、执行跟踪、了解CPU主要寄存器作用)

1.2 Go

  • 协程实现原理
  • 管道实现原理

1.3 设计模式

    • OO思想、常见设计模式(单例模式、工厂设计模式、装饰者模式、Builder模式、生产者消费者模式、策略模式等)
    • Observer模式的思想,策略模式的思想,两者区别,优缺点,适用情况
    • OOP特性,通过哪些机制实现的

二、linux

2.1 系统编程

  • 多线程相关的API:创建、等待、终止以及获取线程ID等
  • 进程相关的API:创建、等待、终止以及获取进程ID等。
  • 线程同步方式:互斥体、读写锁、屏障、信号量、条件变量、自旋锁
  • 多线程和多进程的区别,为什么选用多线程或者是多进程
  • 线程和进程里面各有什么资源,都有什么用处?
    进程间都是相互独立的,产生一个新的进程,大部分的数据都被独立开来,每个进程拥有自己的堆区、栈区等数据,遵循“读时共享、写时复制”的原则。多个线程间大部分数据是共享的,如全局变量、堆等,都是共享的,线程只有自己独立的栈以及线程ID等少量资源,线程还拥有自己独立的信号屏蔽字。
  • forkwait有什么作用,fork进程时的操作。
    fork会产生一个子进程,在子进程中,fork返回0,父进程中,返回新创建的子进程id。wait是父进程用来回收子进程资源用的,避免子进程不回收处在僵尸态。fork后子进程和父进程采用“读时共享、写时复制”的方式来运行。
  • 父子进程、孤儿进程以及僵尸进程,僵尸进程如何产生和消除?
    在进程中调用fork函数会产生一个子进程,当前进程即为新进程的父进程,父进程调用fork返回子进程ID,子进程返回0。孤儿进程是指某个进程的父进程执行完退出了,但是子进程还在执行,此时子进程就是孤儿进程,会被1号进程init回收。僵尸进程是指子进程运行完毕退出了,父进程没有调用wait或者waitpid回收子进程资源,导致子进程处于僵尸态。消除僵尸进程的方法是杀掉父进程,使子进程被1号进程收养并回收掉。
  • 进程间通信:共享内存、匿名管道、有名管道、消息队列、信号量和socket。
  • linux应用层常见的锁,什么是死锁、如何避免死锁。
    互斥量、读写锁、自旋锁、信号量、屏障以及条件变量等。死锁是指进程间由于加锁不当导致程序hang住的现象,死锁只会发生在一个线程试图锁住另一个线程以相反顺序锁住的互斥量以及同一个进程对同一把锁重复加锁。
    避免死锁:锁住的区域尽量少,加完即使释放,尽量不多多个进程同时加多把锁,加锁时注意顺序。
  • 守护进程,操作以及实现原理。

2.2 网络编程

  • socket API:connect/accept/bind/listen/send/sendto/recv/recvfrom/select/gethostbyname
  • 异步IO框架:select/poll/epoll
  • 什么是阻塞模式和非阻塞模式,两种模式下的socket在connectsend以及recv等行为上的区别,如何将socket设置为非阻塞的
    阻塞模式:函数没有达到触发状态时,会一直卡在这里处于等待状态,直到又事件发生为止。
    将socket设置为非阻塞的只要使用fcntl函数给文件描述符的状态加上O_NONBLOCK标记即可。如果是文件描述符,直接在打开时也可以设置非阻塞。
  • 大端小端问题,主机是什么字节序
  • epoll水平触发与边缘触发
    边沿触发(ET模式):epoll中产生可读事件后,如果没有读完缓冲数据域,剩余的数据部分将会在下次有数据过来时才能继续读。水平触发(LT模式):epoll产生可读事件后,如果没有一次没有读完,epoll_wait()继续返回可读事件。
    正常来说,ET模式比LT模式效率更高,因为它在epoll产生的触发事件更少,更多情况下都采用“ET+非阻塞”模式。
  • libeventreactor模式。
    libevent的核心思想便是reactor,当一个socket产生可读事件时,读取数据,然后设置可写事件,再对socket写数据。
    工作流程:socket --> 添加到epoll --> 可读 --> 从epoll中摘下,读数据 --> 设置可写事件 --> 放回红黑树
  • 为什么select有文件句柄限制,pollepoll没有。
    select的文件句柄限制是写死在头文件里面的,编译时就已经固定了。poll和select工作原理类似,它的出现就是为了解决select文件句柄限制的问题。
    而epoll基于事件触发,和前面两者不同,它底层通过红黑树来存储文件描述符,插入删除的效率非常高。
  • epoll_event结构中的epoll_data_t的fd与ptr的使用场景。
    以reactor模式为例,可以把fd设置为当前的文件描述符,ptr设置为函数指针,当socket有可读或者可写事件的时候,自动调用函数指针执行,从而达到异步IO的效果。
  • select函数可以检测网络异常吗
    可以用过select设置超时时间来检测网络异常,很多时候可以通过非阻塞socket+select来对函数添加超时逻辑。
  • send/recv以及read/write返回值大于0、等于0、小于0的区别,错误码EINTR如何处理。
    大于0表示读到n字节的数据,等于0表示对端已经关闭连接,小于0表示错误,需要判断错误码。
    常见的错误码EINTR表示遇到中断了,重新读。EAGAIN也要重新读。
  • 发送数据缓冲区与接收数据缓冲区如何设计,如何编写正确的收数据代码与发数据代码。
  • shutdown与优雅关闭
    close会直接关闭两端的socket连接,并销毁内存对象。shutdown只会关掉本机的连接,对向还可以传输数据。也就是TCP的瓣关闭状态
  • 如何解决tcp粘包问题。
    添加协议,最简单的就是添加一个数据包头,然后写明该条数据包的长度n。读取时根据长度来读数据。
  • http代理、socks4代理与socks5代理如何编码实现。
  • gethostbyname阻塞与错误码获取问题
    gethostbyname默认是阻塞的
  • 如何清除无效的死链(端与端之间的线路故障)
    长连接,持续发送心跳包检测。
  • 长连接和短连接
    长连接表示连接一直处于打开状态,并且定时发送心跳包,当一定时间内心跳包如果发送失败或者没有收到回包,就认为连接发生故障。长连接的目的就是为了实时检测网络故障,避免出现连接异常断开,但是另一端并不知情还处在打开的状态。

2.3 操作系统

2.4 linux命令和shell

  • crontabiptables等命令的的使用
  • 进程、线程状态查看命令top/strace/pstack
  • 内存状态查看命令memstat/free
  • IO状态查看命令iostat/df/du
  • linux文件的权限、用户、时间(ctime/mtime/atime)和inode
  • 文件传输命令scp/rz/sz命令
  • 文件定位命令find/whereis命令
  • 硬链接和软链接,ln命令的用法,硬链接和软连接区别
  • lsof命令
  • kill用法,某个进程杀不掉的原因(进入内核态,忽略kill信号)
  • 系统管理命令(如查看内存使用、网络情况)
  • 管道的使用|
  • grep的使用
  • 文本处理命令awk/sed/grep
  • 本编辑工具vi/vim
  • 了解shell基本语法、变量操作、函数、循环/条件判断等程序结构
  • linux环境下的几种文件类型
  • linux常用端口和网络命令

三、计算机网络

3.1 基础网络知识

  • TCP/UDP报头格式,tcp和ip包头常见有哪些字段。
  • TCP/IP协议栈层次结构。
  • TCP三次握手和四次挥手,各种状态的细节点:CLOSE_WAIT、TIME_WAIT、2MSL。
  • TCP与UDP的区别与适用场景
  • nagle / keepalive / linger等选项的区别
  • 拥塞控制慢开始、拥塞避免、快重传、滑动窗口协议、停止等待协议、超时重传机制
  • IP地址子网划分
  • DNS解析过程
  • 地址解析协议ARP
  • 交换机和路由器的区别
  • http和https区别,https在请求时额外的过程,https是如何保证数据安全的
  • SESSION机制、cookie机制
    session和cookie的目的相同,都是为了克服http协议无状态的缺陷,但完成的方法不同。 session通过cookie,在客户端保存session id,而将用户的其他会话消息保存在服务端的session对象中, 与此相对的,cookie需要将所有信息都保存在客户端。因此cookie存在着一定的安全隐患,例如本地cookie 中保存的用户名密码被破译,或cookie被其他网站收集(例如:1. appA主动设置域B cookie,让域B cookie获取;2. XSS,在appA上通过javascript获取document.cookie,并传递给自己的appB)。
  • HTTP请求方法(GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE)
  • 熟悉tcpdump命令,windows下wireshark使用

3.2 常见问题

  • A与B建立了正常连接后,从未相互发过数据,这个时候B突然机器重启,问A此时的tcp状态处于什么状态?如何消除服务器程序中的这个状态
  • 聊天用什么协议(UDP),为什么使用UDP
  • 微信qq用什么协议
  • 跟多个人聊天怎么实现的(多线程),多线程怎么判断和哪个人聊天,需要设置什么全局变量

四、数据库

4.1 MySql

  • 数据表结构设计(三范式、字段属性)
  • 查询优化(索引的概念与创建、sql优化)
  • 事务四大特性(ACID)
  • 数据库隔离级别,每个级别会引发什么问题,mysql默认是哪个级别
  • MYSQL的两种存储引擎区别(事务、锁级别等等),各自的适用场景
  • 数据库的优化(从sql语句优化和索引两个部分回答)
  • 索引有B+索引和hash索引,各自的区别
  • B+索引数据结构,和B树的区别
  • 索引的分类(主键索引、唯一索引),最左前缀原则,哪些情况索引会失效
  • 聚集索引和非聚集索引区别
  • 有哪些锁(乐观锁悲观锁),select时怎么加排它锁
  • 关系型数据库和非关系型数据库区别
  • 数据库的主从复制
  • long_query怎么解决
  • 使用explain优化sql和索引
  • 内连接、外连接、交叉连接、笛卡儿积等
  • MVCC机制
  • 死锁怎么解决
  • varchar和char的使用场景。
  • mysql并发情况下怎么解决(通过事务、隔离级别、锁)
  • 字典树
  • 平时怎么写数据库的模糊查询

4.2 Redis

  • redis基本数据结构
  • redis队列应用场景
  • redis和Memcached的区别(支持数据持久化)
  • 分布式使用场景(储存session等)
  • 发布/订阅使用场景
  • redis的数据存储结构、rehash;
  • redis的事务与集群。
  • redis的持久化
  • redis热备
  • redis和的hash和string选择的优劣
  • redis单线程设计的原因
  • redis淘汰策略

五、数据结构和算法

5.1 排序和查找

5.2 树

  • AVL和红黑树的区别和原理实现
  • 哈夫曼树,哈夫曼编码。哈夫曼译码
  • AVL树和B树的概念、细节
  • 红黑树的概念、平均算法复杂度、最好最坏情况下的算法复杂度,左右旋转、颜色变换
  • 层次遍历、求深度、求两个节点距离、翻转二叉树、前中后序遍历
  • B+树和B树的区别,插入节点怎么分裂

5.3 哈希表

  • 建表以及解决哈希冲突
  • 哈希表的冲突检测、哈希函数常用实现、算法复杂度
  • 哈希表插入元素算法,元素类型是任意类型
  • hashmap的实现原理 采用什么方法能保证每个bucket中的数据更均匀

5.4 链表

  • 删除一个节点
  • 单链表倒转
  • 两个链表找相交的部分
  • 插入节点、链表逆置、使用链表进行大数字的加减,双向链表实现队列、寻找链表中的环

5.5 堆

  • 大量数据中寻找最大N个数字几乎每次都会问
  • 堆在插入时进行的调整

5.6 栈和队列

  • 两个栈实现队列

5.6 其他

  • KMP算法描述

5.7 常见问题

  • 单纯反转,hello world! :!!dlrow olleh 剑指offer原题。然后问,如果不用栈怎么做。指针找空格,后往前反转
  • 1000万个数,取出top100 堆实现和二分
  • 有100W个数字存放在一个文件中,然后让你随机生成100个数字不要与文件中的数字重复。(关键代码实现)典型的大文件处理
  • 两个有序数组,a[],b[],大小分别为m,n,求第k 大的数
  • 在一组数里面找到一个数,比它左右相邻的数都要大
  • 一个文本文件中每一行中有一个URL,最多一万行,统计每一个URL的次数,输出到另外一个文件中,每一行前面是URL,后面是个数。
  • 一个函数实现给定字符串,去除前面和后面的空格,比如“ ab cd ”,最后得到的结果是”ab cd”,不能改变字符串的地址
  • 对比cookie和session,有一个值错误则不正确
  • 查找10的阶乘后面有几个0
  • 字符串移位,给出字符串abc##dfg##gh,实现将所有#移至字符串串头。输出####abcdfggh(个人认为可以用后向移位,减少移位次数)
  • 给出一颗二叉树,两个叶节点,找到这两个叶节点互连通的一条最短路径
  • 两个日期计算天数差
  • 100个有序数组合并
  • 在一个字符串中找出第一个字符出现的位置,保证高效

alpine是一个轻量级的linux系统,由于太轻量了,目前被广泛用于docker镜像的制作上了(最新版的docker镜像才5M)。

体积小就有缺点了,缺点就是内部除了基本的命令以外,大部门的功能都不支持,默认连bash都没有。需要自己手动添加并重新构建。

1. 基于alpine:3.8添加bash并设置阿里云源

FROM alpine:3.8

MAINTAINER MaQian

RUN echo "https://mirrors.aliyun.com/alpine/v3.8/main/" > /etc/apk/repositories

RUN apk update \
        && apk upgrade \
        && apk add --no-cache bash bash-doc bash-completion \
        && rm -rf /var/cache/apk/* \
        && /bin/bash

2. 基于最新版alpine并设置阿里云源

相比上面的多了一个获取当前版本的逻辑,Dockerfile如下:

from alpine:latest

MAINTAINER MaQian

RUN alpine_version=`cat /etc/issue | head -1 | awk '{print $5}'` \
    && echo "https://mirrors.aliyun.com/alpine/v${alpine_version}/main/" > /etc/apk/repositories   \
    && apk update && apk upgrade && apk add --no-cache bash bash-doc bash-completion \
    && rm -rf /var/cache/apk/*

CMD ["/bin/bash"]

一、问题现象

使用golang编译了一个二进制程序,在CentOSUbuntu的镜像上运行是可以的,但是在Alpine运行就不行,使用./运行报错:

/bin/sh: ./saas_server: not found

二、解决方案

编译时添加参数CGO_ENABLED=0,关闭CGO就可以了:

CGO_ENABLED=0 go build

三、参考文档

Installed Go binary not found in path on Alpine Linux Docker

一、Let's Encrypt

Let's Encrypt SSL证书是一个免费的公益项目,由Mozilla、Cisco、Akamai、IdenTrust、EFF等组织人员发起,主要的目是为了推进网站从HTTP向HTTPS过度的进程,目前已经有越来越多的商家加入和赞助支持。

使用Let's Encrypt生成域名证书的前置条件:

  1. 拥有域名,能自主配置DNS记录。或者提供web服务器验证,需要在网站目录下放一个文件,推荐第一种方式。
  2. 获取证书的环境要能访问到DNS服务器,因为中途需要校验DNS解析。
  3. 拥有主机的超级权限,中途需要更新和安装组件。

二、申请流程

第一步:拉代码,代码开源于Github

> git clone https://github.com/letsencrypt/letsencrypt
Cloning into 'letsencrypt'...
remote: Enumerating objects: 29, done.
remote: Counting objects: 100% (29/29), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 61424 (delta 7), reused 4 (delta 2), pack-reused 61395
Receiving objects: 100% (61424/61424), 20.12 MiB | 5.01 MiB/s, done.
Resolving deltas: 100% (44625/44625), done.

完成后执行命令生成证书:

> ./certbot-auto certonly -d *.maqian.art --manual \
>     --preferred-challenges dns \
>     --server https://acme-v02.api.letsencrypt.org/directory

解释一下各个参数的含义:

  • certonly: 表示当前为安装模式
  • --manual: 表示手动安装插件,不要自动安装了
  • --preferred-challenges dns: 校验方式为dns验证
  • -d *.maqian.art: 要生成的域名列表,可以为多个,如果是多个分别以-d加上即可
  • --server: Let's Encrypt ACME v2版本使用的服务器

接下来的步骤一直接受即可,直到出现添加DNS记录为止:

Requesting to rerun ./certbot-auto with root privileges...
[sudo] password for ma: 
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): maqian@dyxmq.cn

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for sinfor.maqian.io

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.maqian.art with the following value:

zw1MeEkmGZOqSqiySp9Ke8S5a9BXC3O4tYzlbjwU-CU

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

此时需要在DNS服务商处添加dns解析,记录类型为TXT,记录为_acme-challenge,值为zw1MeEkmGZOqSqiySp9Ke8S5a9BXC3O4tYzlbjwU-CU。当DNS记录设置好后,新开一个终端查询解析是否生效:

> nslookup -type=txt _acme-challenge.maqian.art
Server:        100.100.2.136
Address:    100.100.2.136#53

Non-authoritative answer:
_acme-challenge.maqian.art    text = "zw1MeEkmGZOqSqiySp9Ke8S5a9BXC3O4tYzlbjwU-CU"

Authoritative answers can be found from:

当查询到的记录和给定的都一致之后按下任意键执行下一步,如果DNS验证成功就会出现以下信息:

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/maqian.art-0001/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/maqian.art-0001/privkey.pem
   Your cert will expire on 2019-03-22. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

此时就表示证书已经申请完成了,存放的路径为:/etc/letsencrypt/live/maqian.art-0001。

> sudo ls /etc/letsencrypt/live/maqian.art-0001/ -l
total 4
lrwxrwxrwx 1 root root  39 Dec 22 23:46 cert.pem -> ../../archive/maqian.art-0001/cert1.pem
lrwxrwxrwx 1 root root  40 Dec 22 23:46 chain.pem -> ../../archive/maqian.art-0001/chain1.pem
lrwxrwxrwx 1 root root  44 Dec 22 23:46 fullchain.pem -> ../../archive/maqian.art-0001/fullchain1.pem
lrwxrwxrwx 1 root root  42 Dec 22 23:46 privkey.pem -> ../../archive/maqian.art-0001/privkey1.pem
-rw-r--r-- 1 root root 692 Dec 22 23:46 README

三、安装到nginx

上面一共生成了四个文件,各自的用途为:

  • cert.pem: Apache服务器端证书
  • chain.pem: Apache根证书和中继证书
  • fullchain.pem: Nginx所需要ssl_certificate文件
  • privkey.pem: 安全证书KEY文件

部署到nginx只需要添加一下指令即可:

ssl on;
ssl_certificate /etc/letsencrypt/live/maqian.art-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/maqian.art-0001/privkey.pem;

打开网站,点开左上角地址栏的https,查看证书:

一、概述

docker默认存在/var/lib/docker目录下,一般情况下这个目录都没有单独挂载,都是放在根目录下的,目录较小。

为了避免占用太多/var目录空间,并且方便管理,可以把存储目录放到其他的文件夹,例如/data/docker。

二、步骤

创建想要修改的目录,假设是/data/docker,首先创建文件夹并赋予权限。

> mkdir /data/docker
> chgrp -R docker /data/docker

停掉docker,修改docker的systemd服务文件,位于/usr/lib/systemd/system/docker.service,修改ExecStart一行:

> systemctl restart docker
> sed -i 's#ExecStart=/usr/bin/dockerd#ExecStart=/usr/bin/dockerd --graph /data/docker#g' /usr/lib/systemd/system/docker.service

重启docker:

> systemctl daemon-reload
> systemctl start docker 

验证是否修改成功:

> docker info | grep "Root"
Docker Root Dir: /data/docker
> ll /data/docker/
total 48
drwx------ 2 root root 4096 Dec 22 22:39 builder
drwx--x--x 3 root root 4096 Dec 22 22:39 containerd
drwx------ 2 root root 4096 Dec 22 22:39 containers
drwx------ 3 root root 4096 Dec 22 22:39 image
drwxr-x--- 3 root root 4096 Dec 22 22:39 network
drwx------ 3 root root 4096 Dec 22 22:39 overlay2
drwx------ 4 root root 4096 Dec 22 22:39 plugins
drwx------ 2 root root 4096 Dec 22 22:39 runtimes
drwx------ 2 root root 4096 Dec 22 22:39 swarm
drwx------ 2 root root 4096 Dec 22 22:39 tmp
drwx------ 2 root root 4096 Dec 22 22:39 trust
drwx------ 2 root root 4096 Dec 22 22:39 volumes

nginx中只有if关键字,并不支持else语法,if的使用方法为:

if ($xxx = xxx) {
    xxx
}

和代码不同的是:if条件语句判断相等时只要一个等号,不能是==

虽然不支持else,但是可以使用以下的方法来模拟实现else:

server {
    server_name *.maqian.io;
    listen 80;
    
    location / {
        set $is_matched 0;
        if ($host = a.maqian.io) {
            proxy_pass http://127.0.0.1:1001/;
            set $is_matched 1;
        }
        
        if ($host = b.maqian.io) {
            proxy_pass http://127.0.0.1:1002/;
            set $is_matched 1;
        }
        # 没有匹配到,跳转到默认页面
        if ($is_matched = 0) {
            proxy_pass http://127.0.0.1;
        }
        
        # xxx
        # xxx
        # xxx
    }
}

nginx访问限频

一、并发访问限制

ngx_http_limit_conn_module是一个默认安装的内置模块,被用来限制在某一个关键字维度上的最大并发数量,通常情况下,这个维度被设置为访问者的IP。在计算的一个连接当前的并发数量时,不是一连接就会被计数,而是当所有请求头都被读完才计数。它的示例配置为:

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    # ...
    server {
        # ...
        location /download/ {
            limit_conn addr 1;
        }
    }
}

以上配置通过limit_conn_zone指令定义了一个名为addr的并发限制器,它以$binary_remote_addr(即访问的IP地址)作为key,分配一块10m的空间来保存所有的连接数量。

而后的对应的server段中,使用这个addr,限制同一时刻最多只能有1个连接。所以整个配置的意思就是:限制单个IP同一时刻最多有3个访问连接。

相关的参考文档:Module ngx_http_limit_conn_module

1.1 案例一:针对IP的并发数量限制

当前有一个站点d2.maqian.co,希望同一时刻同一IP最多只能3个并发访问:

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m
    server {
        listen       80;
        server_name  d2.maqian.co;
        limit_conn   addr 3;
        limit_rate   1m; # 限制访问速率1M/s
    
        location / {
            root   html;
            index  index.html index.htm;
        }
        
        access_log logs/access.log main;
        error_log logs/error.log;
    }
}

网站的根目录下有个文件master.zip,大小8.8M,在另一台客户端上使用ab命令执行并发测试:

ab -c 5 -n 10 http://d2.maqian.co/master.zip # 同一时刻5个并发连接,一共10个连接

测试过程中Nginx的访问日志:

> cat access.log
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:19 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:17:45:27 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"

可以看到,除了3个连接的的返回值为200以外,其他的都返回状态码503。

为什么三个200访问的日志在最后呢?

因为下载的文件内容是8.8M,配置中有限制最大下载速率为1M/s,所以它大概需要9秒才能下载完成,状态码是在连接返回完成才打印出来的,并且可以看到503状态码和200状态码的日志间隔差不多刚好8-9s。

那为什么要限制下载速率呢?

当前在内网环境下,下载速度非常快,8.8M的文件几乎1S内就能下载完成,连接很难并发,即使并发了,10个连接也很有可能有多个成功了。

1.2 针对服务端同一时刻的访问频率限制

和上面同样的环境,我们不限制单个IP的并发访问数量,而希望同一时刻服务器最多处理10个连接:

http {
    limit_conn_zone $server_name zone=web_server:10m;
    server {
        listen       80;
        server_name  d2.maqian.co;
        limit_conn web_server 10;
        limit_conn_log_level info;    
        limit_rate 1m;
    
        location / {
            root   html;
            index  index.html index.htm;
        }
    
        access_log logs/access.log main;
        error_log logs/error.log;
    }
}

执行命令测试:

ab -c 11 -n 15 http://d2.maqian.co/master.zip # 同时产生11个连接,一共访问15次

nginx访问日志:

192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:49 +0800] GET "d2.maqian.co/master.zip" 503 537 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"
192.168.123.101 - - [20/Oct/2018:18:23:58 +0800] GET "d2.maqian.co/master.zip" 200 9204205 "-" "ApacheBench/2.3" "-"

限制了同一时刻服务端只能有10个连接之后,现象也和上面的一样,15个连接有5个返回错误,但是不同的是这些连接可以同时来自于同一个IP。

1.3 多关键字配合使用

上面的基于IP和服务端访问限制可以同时使用,并且不只是这两个字段可以配合使用,其他的变量也都可以同时使用,具体的变量可以参考:Alphabetical index of variables

例如我们可以同时限制单个IP并发连接数为3,并且同时访问服务端的连接数为100:

http {
    limit_conn_zone $server_name zone=web_server:10m;
    limit_conn_zone $binary_remote_addr zone=addr:10m
    server {
        listen       80;
        server_name  d2.maqian.co;
        limit_conn web_server 100; # 同一时刻最多100个连接访问服务端
        limit_conn addr 3; # 同一时刻同一IP最多3个连接访问
        limit_conn_log_level info;    
        limit_rate 1m;
    
        location / {
            root   html;
            index  index.html index.htm;
        }
    
        access_log logs/access.log main;
        error_log logs/error.log;
    }
}

二、限制访问频率

ngx_http_limit_req_module模块用于限制每个IP访问某个关键字维度的请求速率,其参数用法如下:

limit_req_zone key zone=name:size rate=rate;

以上的配置创建一个速率限制器,限制单个IP在key这个维度上的访问速率。和上面一样,这个key也通常被设置成访问者的IP。使用时在对应的server段内设置:

limit_req zone=name [burst=number] [nodelay];

burst表示令牌数量,连接满了之后,给接下来的连接发放令牌进行等待。令牌数量超出后,可以选择继续等待令牌或者直接返回错误状态。

这里的逻辑可以看成去银行办业务:人多的时候需要等号,number可以堪称最大的等号数量,rate可以堪称银行的窗口个数。银行同一时刻处理rate个客户的请求,并且同时允许number个客户排队,超出后,根据nodelay是否被设置来判断该连接是应该被丢弃还是等待。

参考文档:Module ngx_http_limit_req_module

2.1 限制每秒只处理同一个用户的一个请求

http {
    # 以请求IP作为KEY,设置访问频率为1秒1次请求
    limit_req_zone $binary_remote_addr zone=addr:10m rate=1r/s;
    server {
        listen       80;
        server_name  d2.maqian.co;
        # 设置队列为5,最多有5个连接等待,超出的不继续等待
        limit_req zone=addr burst=5 nodelay;
        limit_rate 1m;
    
        location / {
            root   html;
            index  index.html index.htm;
        }

        access_log logs/access.log main;
        error_log logs/error.log;
    }
}

为了避免大文件下载耗时,这里不再和上面一样下载大文件,使用小文件测试:

ab -c 6 -n 100 http://d2.maqian.co # 6个并发连接,访问100次

得到日志后,复制到当前目录下,分别分析200和503响应的次数:

> grep "503" access.log | wc -l
94
> grep "200" access.log | wc -l
6

nginx第一秒处理第一个请求,同时给接下来的5个请求排队,剩下的都直接返回503,所以返回200的次数为6,503的次数为94。

ab返回的结果也能看到成功和失败的数量:

一、关于gitbook

gitbook是一款写书软件,可以很方便的把一系列markdown文本整合成一个书籍网站发布。

以下是一个预览页面:

gitbook的名字中虽然有git,但是实际上和git没有任何关系。就像java和javascripts一样。

二、安装gitbook

gitbook实际上是一个node.js工具,因此使用前要先安装node.js,或者直接安装npm工具:

sudo apt install npm

确认nodejs和npm命令可用:

> node -v
v10.16.0
> npm -v
6.9.0

安装gitbook:

sudo npm install gitbook-cli -g

三、使用gitbook生成第一本书

在要写书的目录内,执行gitbook init即可初始化一本书:

image.png

默认会生成两个文件:README.mdSUMMARY.md。其中README.md文件是对书籍整体的介绍,而SUMMARY.md中记录了章节目录信息。

发布第一本书

使用gitbook serve可发布书籍信息,执行后默认在本地搭起一个服务端监听4000端口:

在浏览器访问4000端口即可预览:

执行gitbook serve后会在当前目录下生成一个_book的文件夹,文件夹里面保存了发布书籍的静态文件资源。

> ll _book/
total 12
drwxrwxr-x. 10 maqian maqian  270 Sep 13 21:04 gitbook
-rw-rw-r--.  1 maqian maqian 6172 Sep 13 21:04 index.html
-rw-rw-r--.  1 maqian maqian  568 Sep 13 21:04 search_index.json

静态文件也可以直接使用nginx或者其他web服务器来发布,gitbook serve实际上是先生成静态文件,然后再托管这些文件作为web服务器。

如若不想使用gitbook serve提供的服务,可以直接使用gitbook build编译出静态文件:

imagedbe8c5cede9c3529.png

三、目录结构

3.1 SUMMARY.md

默认情况下SUMMARY.md中的内容:

# Summary

* [Introduction](README.md)

3.1.1 添加子目录

效果:

imagee5e99079a08eed2b.png

SUMMARY.md:

# Summary

* [Introduction](README.md)
* [Css](css/README.md)
    * [css1](css/css1.md)
    * [css2](css/css2.md)
* [Javascripts](js/README.md)
    * [js1](js/js1.md)
    * [js2](js/js2.md

3.1.2 section分块

效果:

imagebc967a125dd2b3a8.png

SUMMARY.md:

# Summary

* [Introduction](README.md)

## Part I

* [Css](css/README.md)
    * [css1](css/css1.md)
    * [css2](css/css2.md)

## Part II

* [Javascripts](js/README.md)
    * [js1](js/js1.md)
    * [js2](js/js2.md)