Nginx高性能Web服务器详解

Keywords: #Nginx #反向代理
[TOC] Nginx服务器架构初探 Nginx服务器的Web请求处理机制。 多进程方式 多线程方式 异步方式。异步方式是和多进程多线程方式完全不同的一种处理客户端请求的方式。其中包含几个概念: 网络通信中的同步机制和异步机制是描述通信模式的概念。同步机制,是指发送方发送请求后,需要等待接收到接收方的相应后,才接着发送下一个请求。异步机制,发送方发出一个请求后,不等待接收方相应这个请求,就继续发送下个请求。 阻塞和非阻塞用来描述进程处理调用的方式。在网络通信中,主要指socket的阻塞和非阻塞的方式,而socket的实质也就是IO操作。其调用方式为,调用结果返回前,当前线程从运行状态被挂起,一直等到调用结果返回后,才进入就绪状态,获取CPU后继续执行。非阻塞调用方式中,如果调用结果不能马上返回,当前线程不会被挂起,而是立即返回执行下一个调用。 Nginx服务器的事件处理机制。 在非阻塞调用中,IO调用是如何把自己的状态通知给工作进程的呢。一般有两个方案,轮询和事件驱动模型。IO调用完全由事件驱动模型来管理,事件准备好后就通知工作进程事件已经准备就绪。 事件驱动模型一般是由事件收集器、事件发送器、事件处理器组成。 事件收集器收集用户行为、硬件行为、软件行为。事件发送器每传递过来一个请求,目标对象创建一个进程/线程/一个待处理事件列表使用非阻塞IO方式,调用事件处理器来处理该请求。 select库。是linux和windows都支持的基本事件驱动模型库。它首先创建所关注事件的描述符集合。对于一个描述符,可以关注其上面的读事件,写事件,异常事件,所以要创建三类事件描述符集合,分别用来收集读事件、写事件、异常事件的描述符。其次调用底层提供的select()函数,等待事件发生,然后轮询所有事件描述符集合中的每个事件描述符,检查是否有相应事件发生。 poll库,Windows不支持。其和select的基本工作方式相同,区别在于select需要轮询三个集合,而poll只需要创建一个集合,在每个描述符对应的结构上分别设置读事件、写事件、异常事件,最后轮询的时候同时检查这三种事件是否发生。是select的一个优化。 epoll库,是一种高效的方式。上两种的方式是创建一个待处理事件列表,然后把列表给内核,返回的时候再去轮询检查这个列表,以判断事件是否发生。在epoll中,把描述符列表的管理交给内核,一旦有某种事件发生,内核把发生事件的描述符列表通知给epoll库,这样避免了轮询整个描述符列表。epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的活跃的描述符进行操作。 Nginx服务器的代理服务 正向代理服务,局域网内的机器借助代理服务器访问局域网外的网站,主要是为了增强局域网内部网络的安全性。正向代理服务器不支持外部对内部网络的访问。 反向代理服务,如果局域网向Internet提供资源,让Internet上的其他用户可以访问局域网内的资源,也可以设置一个代理服务器,它提供的服务就叫反向代理。可以看到反向代理服务和正向代理服务在功能逻辑上是相反的。 Buffer和Cache虽然都是用于提供IO吞吐效率的,但是它们是一对不同的概念,“缓冲”和“缓存”。缓冲主要用于传输效率不同步或者优先级别不相同的设备之间传递数据,一般工作对一方数据进行临时存放,再统一发送的办法传递给另一方,以降低进程之间的等待时间,保证速度较快的进程不发生间断,当数据传送完了,数据本身就没有用处了。缓存主要用于将硬盘上已有的数据在内存中建立缓存数据,提高数据的访问效率。 一个典型的反向代理配置: upstream backend { server 192.168.1.27:8080 weight=6; server 192.168.1.27:8081 weight=4; } server { listen 80; location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } 在实际中,可以对所有请求实行轮询、加权,对特定资源,不同域名等进行负载均衡。

mongoDB命令行操作

Keywords: #mongodb
登陆 /usr/local/mongodb30/bin/mongo -u mongo -p xxxxxx 10.131.120.xxx:79xx/test [--authenticationDatabase=admin] 如果不存在test则会建立test库 查看数据库 show dbs 切换数据库,如不存在则建立 use test 查看当前数据库所有集合 show collections 新建collection db.createCollection("Hello") 删除collection db.Hello.drop() 重命名collection db.hello2.renameCollection("HELLO") 向集合user中插入数据 db.user.insert({'name':'Gal Gadot','gender':'female','age':28,'salary':11000}) 或 db.user.save({'name':'Wentworth Earl Miller','gender':'male','age':41,'salary':33000}) 查找user所有记录 db.user.find() 条件查找 单一条件:等于: db.user.find({"age":23}) 大于: db.user.find({salary:{$gt:5000}}) 包含(用正则): db.user.find({name:/a/}) 多条件:与: db.user.find({age:{$lt:30},salary:{$gt:6000}}) 或: db.user.find({$or:[{salary:{$gt:10000}},{age:{$lt:25}}]}) 查询第一条记录 db.user.findOne({$or:[{salary:{$gt:10000}},{age:{$lt:25}}]}) 查询记录指定字段 db.user.find({},{name:1,age:1,salary:1,sex_orientation:true}) 这儿字段用1或true表示 查询指定字段的数据并去重 db.user.distinct('gender') 查询结果集的操作 格式化打印: db.user.find().pretty() 显示结果集前三条: db.user.find().limit(3) 显示第一条以后的所有数据: db.user.find().skip(1) 结果集升序: db.user.find().sort({salary:1}) 结果集降序: db.user.find().sort({salary:-1}) 统计总条数 db.user.find().count() 统计指定条件条数 db.user.find({$or: [{salary: {$lt:4000}}, {salary: {$gt:10000}}]}).

Python语法知识点

Keywords: #Python
[TOC] 变量和结构基础 #注释;缩进视为代码块;句末无分号; 浮点数,当需要用科学计数时,1.2*10^6表示为1.2e6;0.00012表示为1.2e-4; 字符串用’或者”,多行使用”’语法 print('''one two three''') 布尔值,True或False,注意大小写 空值,None 变量赋值 a=123 b='test' 同一个变量可以被赋值不同类型的值。这儿和静态语言不同。 常量,Python中一般使用大写表示常量,和变量赋值一样,只是约定的常量,不是真不能改变的。 算术操作符 1/2 #整数除法默认返回地板除,为0 1.0/2.0 #浮点除法执行真正的除法,为0.5 1//2 #地板除,为0.这是为了兼容新版把传统除法默认替换为真正的除法 10%3 #取余,为1 3**2 #幂运算,为9 其余的加减乘除和位运算都与C语言相通 编码 计算机中8bit表示1byte,所以1byte能表示256个值,在ASCII编码中,1byte表示一个字符。 在中文中1byte表示一个字符是不够的,所以使用2byte,有65536个值。使用了GB2312编码。 为了兼容大多语言,有了Unicode编码,通常用2byte表示。大多语言和操作系统支持Unicode。 但是这样英文在前面补零就浪费IO,所以出现了可变长编码utf-8。可以使用1~6byte表示,英文为1byte,汉字通常3byte。 在计算机内存中,统一使用Unicode编码,当需要保存或者传输时转换成utf-8 序列包括字符串、列表和元组。 序列操作符 seq[ind] #获取下标为ind的元素 seq[ind1:ind2] #获取两下标之间的元素 seq*expr #重复expr次 seq1+seq2 #连接两序列 obj in seq #判断是否包含,返回True或False obj not in seq #判断obj是否不包含 Python的字符串类型str,在内存中用Unicode表示。如果需要在网络传输或者保存到本地,就需要把str转为以字节为单位的bytes x=b'ABC' 格式化 'hello,%s' % 'world' 'hello,%s and %s' % ('a', 'b') 'rate %d %%' 7 #rate 7 % 列表是一种可变的有序的集合。

用awk解决一个问题

Keywords: #shell
工作上要处理一个问题,每天分析员会把全国路由节点近段使用DNSIP最多次数的数据生成到一个文件中,我需要把这些数据每天更新到redis集群中去。文件不算大,大约30万行每天,体积在20M左右。 因为环境问题考虑用bash编程,最开始考虑读每行然后插入。但是效率肯定不会好,于是尝试使用awk编程,写一个函数处理IP转INT,然后把函数转为系统函数 export -f function 然后在awk中调用 awk 'NF>1{system("ip2int "$1" "$2)}' $date > out 其中$!,$2都是IP,传到ip2redis函数中。它可以一次转两个IP。 这样效率很慢,我试了试,大约1小时。 然后就考虑用awk编程,直接在awk中使用 awk '\ BEGIN{FS="[\t.]"} NF>7{ip=($1*(2^24))+($2*(2^16))+($3*(2^8))+$4; dns=($5*(2^24))+($6*(2^16))+($7*(2^8))+$8; print "*3\r\n$3\r\nSET\r\n$"length(ip)"\r\n"ip"\r\n$"length(dns)"\r\n"dns"\r\n"} \' $date > outs 首先使用\t和.作为分隔符,然后取前4个数组为第一个IP,5-8个为DNSIP,分别对其转整数,然后按照REDIS协议生成字符串以便导入。 这个脚本执行时,大约只需要5s。 顺道说句REDIS的批量导入: cat outs | redis-cli --pipe 这个命令不到不少坑,按照他们的格式操作了还是总报错误。最后发现了关键点,awk每输出一行都会在后面加个换行,这个是有问题的。 之前还考虑过用纯字符’\r\n’ ,后来发现不是关键所在。 解决办法是设置换行为”” BEGIN{FS="[\t.]";ORS=""} 关于redis protocol也值得说一下,在使用redis pipe的时候,是将批量的redis命令导入。但是需要先转换成redis协议支持的格式,如下: *3\r\n #这个*后面表示要传送几个字段,这是是三段,SET key value $3\r\n #这个$后面表示这个字段的字节数,SET是3 SET\r\n #这是第一个字段,其他命令也是这种格式 $3\r\n key\r\n $5\r\n value\r\n

TCP/IP详解读书笔记

Keywords: #ARP #IP #TCP #TCP/IP #UDP #读书笔记
[TOC] 概述 网络层,处理分组在网络中的活动。在TCP/IP协议族中,网络层协议包括IP协议,ICMP协议,IGMP协议。 传输层主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议族中,有两个互不相同的传输协议:TCP传输控制协议,UDP用户数据报协议。 IP地址长32bit,有abcde五类互联网地址。有三类P地址:单播地址,广播地址,多播地址。 由于TCP,UDP,ICMP和IGMP都要向IP传送数据,因此IP必须在生成的IP首部中加入某种标识,以表明数据属于哪一层。所以它在首部存入了一个8bit的数值,称为协议域。6为TCP,17标识UDP。 很多应用程序使用TCP或UDP来传输数据。传输层协议在生成报文首部时存入一个应用程序的标识符。TCP和UDP都用一个16bit的端口号来标识不同的应用程序。 网络接口要发送和接收IP,ARP,RARP数据,所以也要在以太网帧首部加入16bit的帧类型域,指明生成数据的网络层协议。 DNS:域名系统 DNS是一种用于TCP/IP应用程序的分布式数据库,他提供主机名字和IP地址之间的转换及有关电子邮件的选路信息。 链路层 以太网是当今TCP/IP采用的主要的局域网技术。IEEE 802是另一个不同的标准集。两种帧格式都采用48bit的目的地址和源地址。ARP和RARP协议对32bit的IP地址和48bit的硬件地址进行映射。 大多数产品支持环回接口Loopback Interface,以允许运行在同一台主机上的客户程序和服务器程序通过TCP/IP进行通信。A类网络号127就是为环回接口预留的。根据惯例大多数系统把127.0.0.1分配给这个接口。 以太网和802.3对数据帧的长度都有一个现实,其最大值分别是1500和1492字节。链路层这个特性叫做MTU,最大传输单元。如果IP层有一个数据报要传,而且数据长度比链路层的MTU要大,那么IP层就要进行分片。 如果两台主机之间的通信需要经过多个网络,那个每个网络的链路层就可能有不同的MTU。重要的不是两台主机所在网络的MTU的值,而是路径中最小的MTU。他被成为路径MTU。两台主机之间的路径MTU不一定是常数,取决于当时所选择的路由。而且选路不一定是对称的,因此路径MTU在两个方向上不一定是一致的。 MTU降到256以下,将降低传输大块数据的最大吞吐量。 IP:网际协议 所有TCP,UDP,ICMP,IGMP数据都以IP数据报格式进行传输。 不可靠的意思是他不能保证IP数据报能成功地到达目的地。如果发生某种错误,如某个路由器暂时用完了缓冲区,IP会丢弃该数据报,然后发送ICMP消息报给信源端。 无连接指IP并不维护任何关于后续数据报的状态信息。每个数据报的处理都是相互独立的。这说明相同信源发送两个连续数据报,他们都独立选择路由,可能到达次序不确定。 TTLtime-to-live生存时间字段设置了数据报可以经过的最多路由器数。它指定了数据报的生存时间。TTL的初始值是由源主机设置通常为32或64,一旦经过一个处理它的路由器,他的值就-1.当该字段值为0时,数据报就被丢弃,并发ICMP报文通知源主机。 每一份IP数据报都包含源IP地址和目的IP地址。 IP路由选择是简单的。如果目的主机与源主机直接相连或者在同一个共享网络上,那么IP数据报就直接送到目的主机上。否则就把数据报发往一默认路由器上。 IP层在内存中有一个路由表,当收到一份数据进行发送时,它都要对该表搜索一次。 路由表的每一项都包含如下信息: 目的IP地址。它既可以是一个完整的主机地址,也可以是一个网络地址(0.0.0.0),以指代网络中的所有主机。 下一跳路由器next-hop router的IP地址,或者有直接连接的网络IP地址。下一站路由器是指一个在直接相连网络上的路由器,通过它可以转发数据报。 标志。其中一个标志目的IP地址是网络地址还是主机地址。 为数据报的传输指定一个网络接口。 IP路由选择是逐条地hop-by-hop进行的。IP并不知道到任何目的地完整路径。所有IP路由选择只为数据传输提供下一站路由器的IP地址。IP路由选择主要完成如下功能: 搜索路由表,寻找能与目的地址完全匹配的表目(网络号和主机号都要匹配)。 搜索路由表,寻找能与目的网络号相匹配的表目。如果能找到,把报文发送给该表目指定下一站路由器或者直接连接的网络接口。这个要考虑子网掩码。 搜索路由表,寻找标为默认的表目。 如果上面步骤都没完成,则会返回主机不可达的错误。 数据报中的目的IP地址始终不发生任何变化。 IP地址不被单纯看做由一个网络号和一个主机号组成,而是把主机号再分成一个子网号和一个主机号。 子网掩码是一个32bit的值,其中值为1的bit留给网络号和子网号,0的bit留给主机号。 ARP:地址解析协议 数据链路比如以太网或令牌环网都有自己的寻址机制(通常为48bit地址)。当一台主机把以太网数据帧发送到位于同一局域网上的另一台主机主机时,是根据48bit的以太网地址来确定目的接口的。 ARP为IP地址到对应的硬件地址之间提供动态映射。 当用户输入ftp bsdi的时候,如上图步骤: FTP客户端调用DNS解析gethostbyname把主机名转换成32bit的IP地址。 FTP客户端请求TCP用得到的IP地址建立连接。 封包,TCP和IP包。如果是内网直接送到目的主机。如果是远程网络通过IP选路确认下一跳路由器地址。 假定是一个以太网,那么发送端主机必须把32bitIP地址转换成48bit的以太网地址。 从逻辑Internet地址到对应的物理硬件地址需要进行翻译。这就是ARP的地址。 ARP发送一份叫做ARP请求的以太网数据帧给以太网上的每个主机。这个过程叫广播。如上图虚线。 目的主机的ARP层收到这份广播报文后,发送一个ARP应答,包含IP地址及对应的硬件地址。 收到ARP应答后,使用ARP进行一应答交换的IP数据报现在就可以传送了。 发送IP数据报到目的地址。 在ARP后有一个概念,网络接口有一个硬件地址(48bit的值,表示不同的以太网或令牌环网络接口)。在硬件层面上进行的数据帧交换必须有正确的接口地址。但是TCP/IP有自己的地址:32bit的IP地址。ARP的功能是在32bit的IP地址和采用不同网络技术的硬件地址之间提供动态映射。 在以太网报头前两个地址是以太网的源地址和目的地址,目的地址全为1的是广播地址,所有以太网接口都要接受广播的数据帧。 当ARP回复时会把硬件地址填进去。 如果ARP请求是从一个网络主机发往另外一个网络的主机,那么连接这两个网络的路由器就可以回答该请求,这个过程称为委托ARP或者ARP代理。这样就可以欺骗发起ARP请求的发送端,使他误以为路由器就是目的主机。路由器的功能相当于目的主机的代理。 Traceroute程序 开始时发送一个TTL为1的UDP数据报,然后将TTL字段每次+1,以确定路径中的每个路由器。每个路由器在丢弃UDP数据报的时候都返回一个ICMP超时报文,而最终目的主机则产生一个ICMP端口不可达的报文。 IP选路 查看主机路由表netstat -rn,route,noName。 (略) 用n参数把主机名变成ip,否则会是default或者zl-link之类的。 flags有几种重要的标志: U 该路由可以使用 G 该路由是到一个网关

永久的记忆——排序算法解析

Keywords: #排序 #排序算法 #算法
[TOC] 冒泡排序 一队人按高矮排序,从第一个开始,和下一个比较,其中高的一个交换到后面。 然后第二个再和第三个比较,较高的交换到后面。 依次重复,第一次排完后,队伍中最高的将会被排到最后面。 然后剩下的n-1个人重复上面的动作,其中最高的又将排到n-1的最后,这样进行n-1次全部排完。 这样过程看起来就像每次排列中最高的那个人像气泡一样冒到最后。 所以比较次数为(n-1)+(n-2)+…+1=n(n-1)/2,即时间复杂度为O(n^2) 选择排序 一队人按高矮排序,第一个人依次与剩下的人进行比较,如果与他比较的人较矮,则交换位置。这样一个轮回下来,将得到队列里最矮的。 然后将最矮的排在第一个,剩下的重复刚才的动作,依次全部排完,得到最后的排列。 这样的比较次数为n-1+n-2+…+1=n(n-1)/2,即时间复杂度也为O(n^2) 插入排序 我们打扑克的时候,右手抓牌,插到左边的牌中,找到正确的位置插进去,左边的牌一直有序。这就是插入排序。 假设有一堆牌,从第二张开始,和第一张比较,如果第二张比第一张小,则交换。 然后第三张牌与已经排好的第二张比较,如果小于第二张,则依次交换,直到其大于其前面的牌为止。 后面重复之前的动作,直到最后一张牌插到前面正确的位置。 这样比较次数为1+2+…+n+1=n(n-1)/2,这样时间复杂度也是O(n^2),但是因为其排好的已经是有序的,只要不是原本已经有序的倒序排列,其效率是高于n^2的。而如果原本就是正向有序的,则时间复杂度变成n。如果是部分有序的,效率也很好。这也是希尔排序的基础。 希尔排序 希尔排序是对插入排序的改进,原理是将间隔h的元素变成有序的,这样的数组叫h有序数组。 实现时,第一次假设间隔为n/2,这样每个小组为两个元素。这时先对两个元素进行交换。 第二次间隔为n/4,这样每个小组为四个元素,这样之前排过的仍在同一组,不过合并了。这样效率也是不错的,对这样的小组进行插入排序。 然后间隔依次除以2,这样缩小增量,直到最后变成一组。然后再进行一次插入排序。这样效率很高。 归并排序 把大数据拆成若干个小问题的分治思想。把大数据拆到最后每个小数据只有一个数,然后开始两两合并,这就是归并(递归的合并)。 合并的时候方法如下,两组数,各自有序。两个指针,首先两个0位置的数比较,较小的放到新数组中,指针+1,剩下的两个位置最小的进行比较,小的放入新数组中,指针+1.最后一组剩下的数依次放入新数组。 快速排序 思路还是分治。首先选一个切分元素,把所有小于他的放左边,大于他的放右边。 然后左边,右边分别进行上面的子操作。无限切分下去直到最后每组两个,排好后所有数据就排好了。 排列方法,首先选一个切分元素,一般选第一个值下标0。 两个指针,分别从1,n-1开始向中间推进。 首先n-1向左推进,直到找到一个小于切分元素的值。然后1开始向右推进,直到找到第一个大于切分元素的值。交换这两个值。然后继续推进,直到全部交换完。然后把切分元素交换到中间合适的位置,排在第一个比他大的数前面。 然后对左边右边分别依次递归,最后完成排序。 堆排序 堆是一种树状结构,是一颗完全被填满的二叉树,底层的元素被从左到右填入,被称为完全二叉树。堆有堆序性,父亲总是大于或小于儿子。而每个节点和其兄弟节点没有顺序关系。 完全二叉树有规律,可以使用一个数组表示而不需要指针。对于任意元素i,左儿子在2i,有儿子在2i+1,父亲在i/2. 所以用堆排序,只需要每次取最大元素,放到队列底部,就能完成排序。 堆一般包含插入操作和删除操作。插入时,先创建空穴,然后依次与父亲比较,依次上冒(交换),直到满足堆序性为止。与父亲比较使用i/2计算。 删除时,只能删除根节点,即最大值或最小值。 删除时,先删除根节点,然后把最后一个节点放到根节点,释放最后节点。 这时的根节点不是最大值或最小值。如果是最大堆,他要判断自己是否大于两儿子,如不是,找到两儿子中较大的交换。然后再依次与自己儿子进行比较,直到满足堆序性。 计数排序 高效的线性排序。原理是设置一个数组的索引值代表待排序元素的值,值来代表出现的次数。然后排列时,根据次数和值就知道该值应该排到什么地方。 这个方法有限制,首先是待排序的值只能是整形,还要知道最大的值,便于分配统计数组空间。 基数排序 也是一种线性排序。先把个位排序,然后对十位排序,然后最百位排序,依次排序最后得到有序。 桶排序 也是一种线性排序。方法如下,比如要排序的值在1~1000之间,每10个为一个桶,这样1~10在编号为1的桶,11~20在编号为2的桶,其中(i-1)*10+1,i*10存在i的桶里。 然后对每个桶进行排序,方法随便。 依次输出每个桶,即得到排序的数。

海量数据架构设计概念

Keywords: #大数据 #架构
[TOC] 无状态 指不会存会话,便于应用水平扩展。 一般采用session集中管理,多个无状态应用连接到同一个session服务器。或者像淘宝那种,采用client cookie而无session。 数据闭环 数据实现自我管理,不依赖其他任何一个系统。 首先是数据异构,把各个依赖系统的数据拿过来存储。异构的数据是原子的,便于再处理加工。 然后是数据聚合,按照所需把原子的数据聚合起来,组成json数据。 这儿的异构依赖MQ等队列机制来接受数据变更。 浏览器缓存 设置请求的过期时间,入响应头Expires,Cache-control进行控制。 CDN缓存 通过推送或者拉取机制更新内容。将静态资源部署在离用户最近的节点上。 接入层缓存 负载均衡 负载均衡作为应用服务器的流量入口,将用户的请求转发给它,实现客户端到真实服务器的透明转发。 lvs一般比nginx,haproxy性能要好些。它是建立在传输层上面的,可以对HTTP,聊天室,数据库做负载均衡,而且性能达到了硬件服务器的60%,其他的只有10%左右。仅仅做请求分发,本身没有流量。 nginx工作在应用层,可以针对HTTP本身来做分发,比如域名,目录等。配置简单。 负载均衡调度算法: 最少连接方式,最快模式,观察模式(根据连接数和响应时间),预测模式,动态性能分配,规则模式等。 反向代理的模式 同步模式:(apache-mod_proxy和squid),浏览器发起请求,立刻被转发到后台,直到请求结束,这个通道一直存在。这种性能不如异步模式,后端要维护很多连接。 异步模式:(lighttpd 和 nginx),浏览器发起请求,请求数据先发送到nginx,结束后再发送给后台,后端处理后,先发送给nginx,nginx再发送给用户。 正向代理和反向代理 正向代理:通过代理服务器访问原始服务器的数据。 反向代理:反向代理服务器相当于原始服务器,用户直接访问,而真实返回的数据是反向代理服务器从其他服务器取得的,但是给人感觉就像反向代理服务器提供的。

LINUX下字符相关命令

Keywords: #Linux
[TOC] awk 报告生成器 awk对每一行的内容进行处理,按照’条件{执行}条件{执行}’ 默认以空格或\t为分隔符,$1代表分割后的第一段内容 cat a.txt|awk '$1==0 {print $1"\t"$2}' NF 指字段总数,$0代表整行 NR 代表当前行数 FS 指分隔符,也可以用参数awk -F:设置分隔符为: awk '{FS=":"}$3<10{print $3}' filename 这样用:来分隔,却只能从第二行开始,第一行还是用空格分隔的,所以要预先设定使用BEGIN关键词 awk 'BEGIN{FS=":"}$3<10{print $3}' filename 这儿的BEGIN关键词和END关键词分别对应第一个和最后一个{},这两个操作都不针对任何一行 在执行中可以使用变量,比如用来统计当前行的百分比或者累加 cat pay.txt | \ awk 'NR==1{printr "%10s %10s\n", $1, $2, "Total" } NR>=2{total=$1+$2;printf "%10d %10d %10d\n", $1, $2, total}' 这儿的total变量,使用时不需要使用$,另外两个表达式间用;或者按ENTER作为分隔 sed 流编辑器 一次处理一行数据,把当前行放到缓冲区,然后处理送往屏幕。本身是个管道命令。 sed [-nefr] [动作] -i是直接修改读取文件内容,不是输出到屏幕 动作说明:[n1[,n2]]function 比如1,2d表示第一二行删除 a新增,c替换,d删除,i插入,p打印,s替换 cat a.txt | sed ‘2,3d’ cat a.txt | sed ‘2a test’ #这个test变成了第三行 cat a.

常用算法复习归纳

Keywords: #算法
[TOC] 字符串算法 旋转字符串 把abcdef的ef放前面,变成efabcd 思路:摇手法,先把这两组分别旋转,再一起翻转。 dcba fe -> ef abcd 所以解题思路分两步 1,写一个通用的内部旋转方法。 reverseStr(s, from, to) while(from<to) t=s[from] s[from++]=s[to] s[to--]=t 这个旋转使用了临时变量,也可以使用异或运算来**交换字符串**,不使用临时变量 2,利用摇手法三步完成。 包含字符串 aef是否在abcdefgA中,不用考虑次序,只考虑单个包含 思路:把长字符串映射到一个hashtable中 26个字母可以放到32位的int中,每个占一个次序的标识符 对每个字母遍历放入int a中 while(int i=0;i<s.length();i++) a |= 1<<(s[i]-'A'); 这儿的**1«shift**是一个常用方法,把0x01左移用来代表某位的标示,这儿用位或运算|来忽略重复的情况 然后把要算的aef等字符转换成同样的int,取位与运算,若为0退出 回文判断,abcdcba这种 这种首先可以从两边向中间推进,两个指针,每次的值都应该相等。 然后用栈也行,先依次压入栈,出来的时候就是反向的,也应该等于原来的字符串。 栈的应用 栈先入后出,有对称的特性,所以可以处理这类问题,比如括号,引号这类。 处理路径/a/b/../c/d/../e/f,这种化简也用栈处理,先分隔,然后依次压栈,碰到..后,弹出栈顶的元素。 求最大回文字串,12212321 参考: Manacher’s ALGORITHM: O(n)时间求字符串的最长回文子串 Longest Palindromic Substring Part II 最长回文子串 数组队列 寻找最小的K个数 一共有n个数,寻找最小的k个,无需排序 思路:先排序然后直接定位前k个?时间复杂度太高 其实只要找出前k个的kmax就行了,把比他小的找出来 先把n分成k块和n-k块 对前面k个数选出最大的,这时的时间复杂度为O(k),最大值为kmax 然后用kmax和后面的n-k个数进行比对,当kmax<i,继续 当kmax>i,交换kmax和i,然后重新对前k个数排序,找出kmax 所以有O(k(n-k))=O(kn) 这类问题一般都是维持一个大小为k的容器来处理 查找排序 找出n个的数组中次数超过一半的数 思路:可以用hash,val=>次数,再遍历hash 或设两个临时值,candidate是值,ntimes是次数 遍历: 1,当前值赋给ca,nt为1 2,如果下一个也是它,则nT+1 如果不是,则nT-1

PHP的一些底层笔记

Keywords: #fastcgi #FPM #Nginx #PHP
引擎(Zend)+组件(ext)的模式降低内部耦合 中间层(sapi)隔绝web server和php Sapi主要有apache2handler,cgi,cli等接口 PHP类型 php的变量都保存在一个结构中 其中真正的值都保存在zvalue_value中 typedef struct _zval_struct { zvalue_value value; zend_uint refcount; zend_uchar type; zend_uchar is_ref; } zval; refcount为引用计数,zval.type为Is_Long或者Is_Bool时,则去取zval.value.lval或dval,字符串取str,资源也会去lval,像是对应资源链表的偏移值。zvalue_value的数据结构为union typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value; PHP变量 给变量赋值时,PHP会分配一个zval来存值,而zval的结构中是没有变量名的。 然后变量名和指向这个zval的指针会填入一个数组hashtable中。 不同的变量会保存在不同的定义域的活动符号表中(活动符号表和全局符号表)(symbol_table和active_symbol_table)(在函数中,我们可以通过显式申明global来使用全局变量。在active_symbol_table中创建symbol_table中同名变量的引用,如果symbol_table中没有同名变量则会先创建) $var=”a”; var(全局符号表)=>*zval指(针) 在函数b中的变量,则保存在b符号表中,而函数的参数$var也是保存在a符号表中,不过会把其指针指向一份全局变量$var的copy的zval $var1=$var; 复制给另一个变量,其实只是添加了同一个指针,引用计数refcount+1 $var=1; 这样会修改一个变量,执行copy on write机制,如果refcount>1,则会复制一个新的zval,将原有refcount-1,并修改符号表指针。 上面这个是复制,而当引用时: $a=”v”; $b=&$a; $b=1; 这时修改一个is_ref=1的zval时,会执行change on write,当is_ref=1是,不会复制新的zval 因为is_ref是bool型,所以当其zval存在is_ref时,就表示所有指向其的符号表都是引用,复制的要分离出去。还有一种情况,同时又引用和赋值的情况,这个时候会分离出复制新的zval,来确保is_ref=1时不会同时存在非引用的情况。 可以看这儿,(那如果是混合了引用(assign-by-reference)和普通赋值(assign-by-value)的脚本,又是什么情况呢?) unset只会清除符号表,而不会去管zval,zval被GC只看refcount是否为0. 引用传递 function do_zval_test(&$s){ $s = "after"; return $s; } $a = "before"; $b = do_zval_test($a); 在函数调用结束之后 $a的is_ref恢复成0