redis应用场景

毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象构建不同的冰箱。希望你喜欢这个比喻。

一、Redis常用数据类型

Redis最为常用的数据类型主要有以下五种:

  • String
  • Hash
  • List
  • Set
  • Sorted set

在具体描述这几种数据类型之前,我们先通过一张图了解下Redis内部内存管理中是如何描述这些不同数据类型的:

首先Redis内部使用一个redisObject对象来表示所有的key和value,redisObject最主要的信息如上图所示:type代表一个value对象具体是何种数据类型,encoding是不同数据类型在redis内部的存储方式,比如:type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际redis内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:”123″ “456”这样的字符串。

这里需要特殊说明一下vm字段,只有打开了Redis的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。通过上图我们可以发现Redis使用redisObject来表示所有的key/value数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给Redis不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用,我们随后会具体讨论。

二、各种数据类型应用和实现方式

下面我们先来逐一的分析下这五种数据类型的使用和内部实现方式:

1、String

String 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字。

常用命令:get、set、incr、decr、mget等。

应用场景:String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类,即可以完全实现目前 Memcached 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication等功能。除了提供与 Memcached 一样的get、set、incr、decr 等操作外,Redis还提供了下面一些操作: 

  • 获取字符串长度
  • 往字符串append内容
  • 设置和获取字符串的某一段内容
  • 设置及获取字符串的某一位(bit)
  • 批量设置一系列字符串的内容

使用场景:常规key-value缓存应用。常规计数: 微博数, 粉丝数。

实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。

2、Hash

常用命令:hget,hset,hgetall 等。

应用场景:

我们简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:

第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。

第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。

那么Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口,如下图:

也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。

这里同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。

使用场景:存储部分变更数据,如用户信息等。

实现方式:

上面已经说到Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

3、List

常用命令:lpush,rpush,lpop,rpop,lrange等。

应用场景:

Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。

List 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用List结构,我们可以轻松地实现最新消息排行等功能。List的另一个应用就是消息队列,
可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。Redis还提供了操作List中某一段的api,你可以直接查询,删除List中某一段的元素。

实现方式:

Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样List即可以作为栈,也可以作为队列。

使用场景:

消息队列系统

使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

比如:将Redis用作日志收集器

实际上还是一个队列,多个端点将日志信息写入Redis,然后一个worker统一将所有日志写到磁盘。

取最新N个数据的操作

记录前N个最新登陆的用户Id列表,超出的范围可以从数据库中获得。

//把当前登录人添加到链表里
ret = r.lpush("login:last_login_times", uid)

//保持链表只有N位
ret = redis.ltrim("login:last_login_times", 0, N-1)

//获得前N个最新登陆的用户Id列表
last_login_list = r.lrange("login:last_login_times", 0, N-1)

比如sina微博:

在Redis中我们的最新微博ID使用了常驻缓存,这是一直更新的。但是我们做了限制不能超过5000个ID,因此我们的获取ID函数会一直询问Redis。只有在start/count参数超出了这个范围的时候,才需要去访问数据库。

我们的系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,而主页或第一个评论页是不会麻烦到硬盘上的数据库了。

4、Set

常用命令:

sadd,spop,smembers,sunion 等。

应用场景:

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

Set 就是一个集合,集合的概念就是一堆不重复值的组合。利用Redis提供的Set数据结构,可以存储一些集合性的数据。

案例:

在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

Set是集合,是String类型的无序集合,set是通过hashtable实现的,概念和数学中个的集合基本类似,可以交集,并集,差集等等,set中的元素是没有顺序的。

实现方式:

set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

使用场景:

交集,并集,差集:(Set)

//book表存储book名称

set book:1:name    ”The Ruby Programming Language”

set book:2:name     ”Ruby on rail”

set book:3:name     ”Programming Erlang”

//tag表使用集合来存储数据,因为集合擅长求交集、并集

sadd tag:ruby 1

sadd tag:ruby 2

sadd tag:web 2

sadd tag:erlang 3

//即属于ruby又属于web的书?

 inter_list = redis.sinter("tag.web", "tag:ruby") 

//即属于ruby,但不属于web的书?

 inter_list = redis.sdiff("tag.ruby", "tag:web") 

//属于ruby和属于web的书的合集?

 inter_list = redis.sunion("tag.ruby", "tag:web")

获取某段时间所有数据去重值

这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就行了,set意为集合,所以会自动排重。

5、Sorted Set

常用命令:

zadd,zrange,zrem,zcard等

使用场景:

Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。

和Set相比,Sorted Set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列,比如一个存储全班同学成绩的Sorted Set,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。另外还可以用Sorted Set来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

实现方式:

Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

三、Redis实际应用场景

1、显示最新的项目列表

下面这个语句常用来显示最新项目,随着数据多了,查询毫无疑问会越来越慢。

SELECT * FROM foo WHERE ... ORDER BY time DESC LIMIT 10

      在Web应用中,“列出最新的回复”之类的查询非常普遍,这通常会带来可扩展性问题。这令人沮丧,因为项目本来就是按这个顺序被创建的,但要输出这个顺序却不得不进行排序操作。类似的问题就可以用Redis来解决。比如说,我们的一个Web应用想要列出用户贴出的最新20条评论。在最新的评论边上我们有一个“显示全部”的链接,点击后就可以获得更多的评论。我们假设数据库中的每条评论都有一个唯一的递增的ID字段。我们可以使用分页来制作主页和评论页,使用Redis的模板,每次新评论发表时,我们会将它的ID添加到一个Redis列表:

LPUSH latest.comments <ID>

我们将列表裁剪为指定长度,因此Redis只需要保存最新的5000条评论:

LTRIM latest.comments 0 5000

每次我们需要获取最新评论的项目范围时,我们调用一个函数来完成(使用伪代码):

FUNCTION get_latest_comments(start, num_items):  
    id_list = redis.lrange("latest.comments",start,start+num_items - 1)  
    IF id_list.length < num_items  
        id_list = SQL_DB("SELECT ... ORDER BY time LIMIT ...")  
    END  
    RETURN id_list  
END

       这里我们做的很简单。在Redis中我们的最新ID使用了常驻缓存,这是一直更新的。但是我们做了限制不能超过5000个ID,因此我们的获取ID函数会一直询问Redis。只有在start/count参数超出了这个范围的时候,才需要去访问数据库。

       我们的系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,而主页或第一个评论页是不会麻烦到硬盘上的数据库了。

2、排行榜应用,取TOP N操作

     这个需求与上面需求的不同之处在于,取最新N个数据的操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。

热门,排行榜应用:

//将登录次数和用户统一存储在一个sorted set里
zadd login:login_times 5 1
zadd login:login_times 1 2
zadd login:login_times 2 3
//当用户登录时,对该用户的登录次数自增1
ret = r.zincrby("login:login_times", 1, uid)
//那么如何获得登录次数最多的用户呢,逆序排列取得排名前N的用户
ret = r.zrevrange("login:login_times", 0, N-1)

     另一个很普遍的需求是各种数据库的数据并非存储在内存中,因此在按得分排序以及实时更新这些几乎每秒钟都需要更新的功能上数据库的性能不够理想。典型的比如那些在线游戏的排行榜,比如一个Facebook的游戏,根据得分你通常想要:

– 列出前100名高分选手

– 列出某用户当前的全球排名

这些操作对于Redis来说小菜一碟,即使你有几百万个用户,每分钟都会有几百万个新的得分。模式是这样的,每次获得新得分时,我们用这样的代码:

ZADD leaderboard  <score>  <username>

你可能用userID来取代username,这取决于你是怎么设计的。得到前100名高分用户很简单:

ZREVRANGE leaderboard 0 99

 用户的全球排名也相似,只需要:

ZRANK leaderboard <username>

3、删除与过滤

      我们可以使用LREM来删除评论。如果删除操作非常少,另一个选择是直接跳过评论条目的入口,报告说该评论已经不存在。 有些时候你想要给不同的列表附加上不同的过滤器。如果过滤器的数量受到限制,你可以简单的为每个不同的过滤器使用不同的Redis列表。毕竟每个列表只有5000条项目,但Redis却能够使用非常少的内存来处理几百万条项目。

4、按照用户投票和时间排序

     排行榜的一种常见变体模式就像Reddit或Hacker News用的那样,新闻按照类似下面的公式根据得分来排序:score = points / time^alpha 因此用户的投票会相应的把新闻挖出来,但时间会按照一定的指数将新闻埋下去。下面是我们的模式,当然算法由你决定。模式是这样的,开始时先观察那些可能是最新的项目,例如首页上的1000条新闻都是候选者,因此我们先忽视掉其他的,这实现起来很简单。每次新的新闻贴上来后,我们将ID添加到列表中,使用LPUSH + LTRIM,确保只取出最新的1000条项目。有一项后台任务获取这个列表,并且持续的计算这1000条新闻中每条新闻的最终得分。计算结果由ZADD命令按照新的顺序填充生成列表,老新闻则被清除。这里的关键思路是排序工作是由后台任务来完成的。

5、处理过期项目

另一种常用的项目排序是按照时间排序。我们使用unix时间作为得分即可。 模式如下:

– 每次有新项目添加到我们的非Redis数据库时,我们把它加入到排序集合中。这时我们用的是时间属性,current_time和time_to_live。

– 另一项后台任务使用ZRANGE…SCORES查询排序集合,取出最新的10个项目。如果发现unix时间已经过期,则在数据库中删除条目。

6、计数

      Redis是一个很好的计数器,这要感谢INCRBY和其他相似命令。我相信你曾许多次想要给数据库加上新的计数器,用来获取统计或显示新信息,但是最后却由于写入敏感而不得不放弃它们。好了,现在使用Redis就不需要再担心了。有了原子递增(atomic increment),你可以放心的加上各种计数,用GETSET重置,或者是让它们过期。例如这样操作:

INCR user:<id> EXPIRE

你可以计算出最近用户在页面间停顿不超过60秒的页面浏览量,当计数达到比如20时,就可以显示出某些条幅提示,或是其它你想显示的东西。

7、特定时间内的特定项目

        另一项对于其他数据库很难,但Redis做起来却轻而易举的事就是统计在某段特点时间里有多少特定用户访问了某个特定资源。比如我想要知道某些特定的注册用户或IP地址,他们到底有多少访问了某篇文章。每次我获得一次新的页面浏览时我只需要这样做:

SADD page:day1:<page_id> <user_id>

当然你可能想用unix时间替换day1,比如time()-(time()%3600*24)等等。 想知道特定用户的数量吗?只需要使用

SCARD page:day1:<page_id>

需要测试某个特定用户是否访问了这个页面?

SISMEMBER page:day1:<page_id>

8、查找某个值所在的区间(区间无重合) :(Sorted Set)

例如有下面两个范围,10-20和30-40

  • A_start 10, A_end 20
  • B_start 30, B_end 40

我们将这两个范围的起始位置存在Redis的Sorted Sets数据结构中,基本范围起始值作为score,范围名加start和end为其value值:

redis 127.0.0.1:6379> zadd ranges 10 A_start
(integer) 1
redis 127.0.0.1:6379> zadd ranges 20 A_end
(integer) 1
redis 127.0.0.1:6379> zadd ranges 30 B_start
(integer) 1
redis 127.0.0.1:6379> zadd ranges 40 B_end
(integer) 1

这样数据在插入Sorted Sets后,相当于是将这些起始位置按顺序排列好了。现在我需要查找15这个值在哪一个范围中,只需要进行如下的zrangbyscore查找:

redis 127.0.0.1:6379> zrangebyscore ranges (15 +inf LIMIT 0 1
1) "A_end"

这个命令的意思是在Sorted Sets中查找大于15的第一个值。(+inf在Redis中表示正无穷大,15前面的括号表示>15而非>=15)查找的结果是A_end,由于所有值是按顺序排列的,所以可以判定15是在A_start到A_end区间上,也就是说15是在A这个范围里。至此大功告成。

9、交集,并集,差集:(Set)

//book表存储book名称
set book:1:name    ”The Ruby Programming Language”
set book:2:name     ”Ruby on rail”
set book:3:name     ”Programming Erlang”

//tag表使用集合来存储数据,因为集合擅长求交集、并集
sadd tag:ruby 1
sadd tag:ruby 2
sadd tag:web 2
sadd tag:erlang 3

//即属于ruby又属于web的书?
 inter_list = redis.sinter("tag.web", "tag:ruby") 
//即属于ruby,但不属于web的书?
 inter_list = redis.sdiff("tag.ruby", "tag:web") 
//属于ruby和属于web的书的合集?
 inter_list = redis.sunion("tag.ruby", "tag:web")

本文来自网络:https://www.cnblogs.com/xiaoxi/p/7007695.html

Ajax跨域请求COOKIE无法带上 怎么搞?

jquery的ajax的post方法请求:

$.ajax({
type: “POST”,
url: “http://xxx.com/api/test”,
dataType: ‘jsonp’,
xhrFields: {
withCredentials: true
},
crossDomain: true,
success:function(){
},
error:function(){
}
})

服务器端设置:

header(“Access-Control-Allow-Credentials: true”);

header(“Access-Control-Allow-Origin: http://www.xxx.com”);

redis监控

 

  • 摘要:redis-cli提供原生的监控视图1、redis-cli–stat查看当前连接的客户端数,连接数等2、redis-cli–bigkeys对当前占用内存最大的键值和平均的键值数据,也可以通过指定-i参数定时查看当前的视图情况。下图中为测试库,只对键值c做了操作,因此如下图中最大的键值为c3、redis-cli–scan提供和keys*相似的功能,查看当前的键值情况,可以通过正则表达如–pattern’user:*’进行键值的分析,并且配合shell命令进行相关的统计分
  • redis-cli提供原生的监控视图
    1、redis-cli –stat查看当前连接的客户端数,连接数等

    redis监控_Python

    2、redis-cli –bigkeys对当前占用内存最大的键值和平均的键值数据,也可以通过指定-i参数定时查看当前的视图情况。下图中为测试库,只对键值c做了操作,因此如下图中最大的键值为c

    redis监控_Python

    3、redis-cli –scan提供和keys *相似的功能,查看当前的键值情况,可以通过正则表达如

    –pattern ‘user:*’
    进行键值的分析,并且配合shell命令进行相关的统计分析

    redis-cli提供原生的Monitor监控命令以及基于该命令的扩展监控框架

    1、redis-cli monitor打印出所有sever接收到的命令以及其对应的客户端地址

    redis监控_Python

    2、RedisLive是基于python3实现的可视化监控界面

    https://github.com/nkrode/RedisLive

    http://www.nkrode.com/article/real-time-dashboard-for-redis

    3、redis-stat是基于ruby实现的可视化监控界面

    https://github.com/junegunn/redis-stat

    4、redis-faina是基于python2实现的可视化监控界面,监控100行命令

    redis监控_Python

    https://github.com/facebookarchive/redis-faina

本文来自网络:https://www.aliyun.com/jiaocheng/452905.html

 

phpredis中的connect和pconnect

本文转自网络:https://blog.csdn.net/u013474436/article/details/53118475

现在不管是在缓存方面,还是NoSQL方面,Redis很火也很流行,但是使用方面的经验不是很多,包括Redis的一些优化配置,还有使用Redis的一些技巧和经验都没有一个官方的指导,所以在网上能搜索到很多相关的东西,但是发现不一定完全匹配自己遇到的一些问题,而且有的文章只是告诉你要这么做,但是没有深究到底是为什么?

最近碰到一个项目的优化,该项目其实逻辑很简单,大的结构就是处理用户的请求,然后读Redis,返回对应的数据。而问题的所在在于该项目有个特点,就是存在整点效应——平时访问量比较低,服务器压力不大,但是整点的时候并发能够达到平时的4-5倍,服务器完全抗不住。所以我们就慢慢梳理整个项目的架构,还有流程,想从中发现问题的所在。

为了先解决并发高服务器负载过高的问题,我们首先分析了…(话扯远了,背景就介绍到这里,呵呵。。)

首先先介绍下connectpconnect的区别。
connect:脚本结束之后连接就释放了。
pconnect:脚本结束之后连接不释放,连接保持在php-fpm进程中。
为了验证这点,我们可以写个脚本测试一下。
其中服务器是nginxphp-fpm采用静态方式,因为动态方式下php-fpm的进程数量可能会变化,所以为了简单我们采用静态方式启动。
其中php-fpm的数量我们设置成5个。

下面的脚本测试使用connect的情况,我们让脚本连接到redis,然后休眠10s。

<?php
$app = new App ();
$app->get ( '/', function () {
    $redis = new Redis ();
    $redis->connect ( '127.0.0.1' );
    sleep(10);
    echo 'Hello World';
    // $redis->close ();
} );

return $app;

然后我们运行5个请求:

curl http://localhost:8081

这时候我们可以看下redis中的connect_clients:

$ redis-cli info | grep connected_clients
connected_clients:14

等脚本运行完毕之后我们再看一下connect_clients:

$ redis-cli info | grep connected_clients
connected_clients:9

之前建立的redis连接资源被释放了。
我们修改上面的代码,把connect改成pconnect

<?php
$app = new App ();
$app->get ( '/', function () {
    $redis = new Redis ();
    $redis->pconnect ( '127.0.0.1' );
    sleep(10);
    echo 'Hello World';
    // $redis->close ();
} );

return $app;

和上面同样的操作,发现脚本脚本运行结束后connected_clients还是14:

$ redis-cli info | grep connected_clients
connected_clients:14

这说明脚本运行结束后,redis连接资源并没有释放,而是由php-fpm进程保持(可以通过 kill php-fpm看到,当脚本停止运行后连接释放)

所以使用pconnect代替connect,可以减少频繁建立redis连接的消耗。

另外,使用pconnect还可以减少同一个进程(php-fpm)频繁建立连接的消耗,可以通过以下代码验证:

使用connect的情况:

<?php
$redis1 = new Redis();
$redis1->connect('127.0.0.1');
sleep(5);
$redis2 = new Redis();
$redis2->connect('127.0.0.1');
sleep(5);
//$redis->close();
//$redis2->close();

运行上述脚本,会发现connect_clients会增加2个。

使用pconnect的情况:

<?php
$redis1 = new Redis();
$redis1->pconnect('127.0.0.1');
sleep(5);
$redis2 = new Redis();
$redis2->pconnect('127.0.0.1');
sleep(5);
//$redis->close();
//$redis2->close();

而运行上述代码,connect_clients只会增加1个,这说明在一个进程中,pconnect是可以保持redis连接状态提供复用的。

Nginx中使用htpasswd配置Http认证

监控Nginx服务器运行情况的模块。只要用户在浏览器输入http://your_ip/status,就可以访问监控页面。这样很不安全,因为任何人都可以访问这个页面。是否可以再添加一个授权模块呢?

答案是肯定的,Nginx的源码提供了ngx_http_auth_basic_module这个模块,它可以来解决这个问题。这个模块是默认就编译进nginx的,可以直接拿来使用。

ngx_http_auth_basic_module它提供了最基本的http认证,这是http协议支持的,它会弹出一个框让你输入用户名和密码,只有用户名和密码输入正确了才能访问,这样就能防止http://your_ip/status被任何人访问了。

ngx_http_auth_basic_module是使用文件作为存储介质的,用户名是明文存储,而密码是加密之后再存储,这样在认证框输入的用户名和密码必须和文件的信息匹配才能认证成功。这里使用htpasswd这个命令来生成存放用户名和密码的文件。
什么是 htpasswd ?

htpasswd 是开源 http 服务器 apache httpd 的一个命令工具,用于生成 http 基本认证的密码文件。
安装

ubuntu

sudo apt-get install apache2-utils

1

centos

yum -y install httpd

1

使用
语法

htpasswd(选项)(参数)

1

htpasswd使用帮助

htpasswd 选项说明

-c 创建passwdfile.如果passwdfile 已经存在,那么它会重新写入并删去原有内容.
-n 不更新passwordfile,只将加密后的用户名密码显示在屏幕上;
-m 默认采用MD5算法对密码进行加密
-d 采用CRYPT算法对密码进行加密
-p 不对密码进行进行加密,即使用普通文本格式的密码
-s 采用SHA算法对密码进行加密
-b 命令行中一并输入用户名和密码而不是根据提示输入密码,可以看见明文,不需要交互
-D 删除指定的用户

htpasswd 参数说明

用户:用户名
密码:用户的密码

实例

利用htpasswd命令添加用户

shell> htpasswd -c /usr/local/nginx/.htpasswd Javen
New password:
Re-type new password:
Adding password for user Javen

或者不用交互的模式

shell> htpasswd -bc /usr/local/nginx/.htpasswd Javen javen205

在/usr/local/nginx/目录下生成一个.htpasswd文件,用户名Javen,密码javen205,默认采用MD5加密方式。

在原有密码文件中增加下一个用户

去掉-c选项即可在第一个用户之后添加第二个用户

shell> htpasswd -b /usr/local/nginx/.htpasswd JJ jj

不更新密码文件,只显示加密后的用户名和密码

不更新.passwd文件,只在屏幕上输出用户名和经过加密后的密码。

shell> htpasswd -nb Javen Javen205

利用htpasswd命令删除用户名和密码

shell> htpasswd -D /usr/local/nginx/.htpasswd Javen
Deleting password for user Javen

利用htpasswd命令修改密码

htpasswd 并没有直接修改密码的函数,需要先使用htpasswd删除命令删除指定用户,再利用htpasswd添加用户命令创建用户即可实现修改密码的功能。

htpasswd -D .htpasswd Javen
htpasswd -b .htpasswd Javen javen205

Nginx中使用htpasswd

在配置文件中打开http basic auth认证。

location /status {
auth_basic “Restricted”;
auth_basic_user_file /usr/local/nginx/.htpasswd;
vhost_traffic_status_display;
vhost_traffic_status_display_format html;
}

效果图

访问时提示密码框

本文来自网络:https://blog.csdn.net/zyw_java/article/details/80561316

如果设置好,访问的时候提示500错误,查看nginx错误日志,基本是htpasswd访问权限问题

centos7利用ngxtop监控nginx

在使用centos7的软件包管理程序yum安装python-pip的时候会报一下错误:

No package python-pip available.
Error: Nothing to do

说没有python-pip软件包可以安装。

这是因为像centos这类衍生出来的发行版,他们的源有时候内容更新的比较滞后,或者说有时候一些扩展的源根本就没有。

所以在使用yum来search python-pip的时候,会说没有找到该软件包。因此为了能够安装这些包,需要先安装扩展源EPEL。

EPEL(http://fedoraproject.org/wiki/EPEL) 是由 Fedora 社区打造,为 RHEL 及衍生发行版如 CentOS、Scientific Linux 等提供高质量软件包的项目。

首先安装epel扩展源:

sudo yum -y install epel-release

然后安装python-pip

安装pip

# sudo yum -y install python-pip

安装完之后别忘了清除一下cache

sudo yum clean all

安装好pip之后我们就可以安装ngxtop了

#sudo pip install ngxtop

安装成功,现在可操作ngxtop命令了,

#ngxtop

效果如下

#ngxtop

通常情况下我们的配置文件都在vhosts里面
# ngxtop -c /server/nginx/conf/vhosts/123.conf 

解决CentOS 7中php-fpm进程数过多导致服务器内存资源消耗较大的问题

前言:

最近服务器内存使用率一直居高不下,检查之后发现可能和php-fpm进程数过多有关。本文记录了我优化php-fpm配置文件的过程并补充了一些和php-fpm有关的知识。

什么是php-fpm:

php-fpm即FastCGI进程管理器,用于控制php的内存和进程等。

操作环境:

CentOS 7

问题检查:

首先查看php进程总数:
回显:
根据回显信息可知,查询时有20个php-fpm进程。
也可以通过下面的命令列出消耗内存最多的前50个进程:

解决过程:

下面我们通过修改配置文件来优化php-fpm,以达到降低内存使用率的目的。
我的php-fpm配置文件在 .../server/php/etc/ 目录下,进入该目录。
先备份一下原配置文件:
注:
使用vim打开配置文件后,按Esc键,之后输入”/”, 再输入“xxx”可以查找“xxx”这个字符串。
具体配置参数如下:
重启服务器:
重启后可以发现内存使用率相比于之前降低了。
注:
0. php-fpm.conf中参数作用的说明:
pm.max_children:静态方式下开启的php-fpm进程数量。
pm.start_servers:动态方式下的起始php-fpm进程数量。
pm.min_spare_servers:动态方式下的最小php-fpm进程数。
pm.max_spare_servers:动态方式下的最大php-fpm进程数量。
1. 进行上述设置时,我将php-fpm的执行方式设置成了动态:
2. php-fpm有两种执行方式:
一种是Static静态,另一种是Dynamic动态。如果设置成静态,那么只有 pm.max_children 这个参数起作用。如果设置成动态则有 pm.start_servers 、 pm.min_spare_servers 和 pm.max_spare_servers 这三个参数起作用。设置成动态后,当php-fpm启动时会按照 pm.start_servers 的参数设置启动相应个数的进程,之后php-fpm的进程数量将维持在 pm.min_spare_servers 和 pm.max_spare_servers 指定的个数之间。
3. 如何选择使用php-fpm的静态还是动态执行方式:
动态php-fpm执行方式允许php-fpm释放多余进程,从而节约内存资源。
静态php-fpm执行方式不允许php-fpm释放多于进程,避免了频繁开启或停止php-fpm进程,从而降低了服务器在某些情况下的响应时间。
本文转自网络:http://zhaokaifeng.com/?p=653

实时查看php-fpm的状态

location ~ ^/status$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}

然后在php-fpm.conf里面打开选项
pm.status_path = /status
这样的话通过http://域名/status就可以看到当前的php情况,以前之知道可以配置location来看nginx的状态,没想到还可以看php-fpm的状态,,真的是学习了,,看到的状态如下:
pool: www php运行的组
process manager: dynamic php-fpm运行的方式
start time: 04/Jun/2012:16:05:32 +0800 开始时间
start since: 5932
accepted conn: 65678 接受链接
listen queue: 0 监听队列
max listen queue: 1 最大监听队列
listen queue len: 128 监听队列len
idle processes: 82 空闲进程
active processes: 4 活动进程
total processes: 86 总进程
max active processes: 25 最大活动进程
max children reached: 0 最大的子进程达到
实时的查看php-fpm的状态,进而优化php-fpm

本文来自网络:https://blog.csdn.net/qdujunjie/article/details/43446667

php-fpm参数调优

关于php-fpm.conf参数调优,只对重要的参数进程调优.其它可参数前辈的.

http://php.net/manual/zh/install.fpm.configuration.php (官方的)

http://www.cnblogs.com/argb/p/3604340.html

http://www.cnblogs.com/jonsea/p/5522018.html

https://www.zybuluo.com/phper/note/89081

http://blog.64mazi.com/299/(推荐阅读)

1
2
3
4
5
关于emergency_restart_threshold 和emergency_restart_interval 参数,设置多少合适
额定内存为4G,拿出一半内存做php使用,即2G,每个php-fpm进程30m(经验值)
则2g最多可以承受2048/30~=68即设置为
emergency_restart_threshold=60(60个进程数)
emergency_restart_interval=60s(60秒内出现60个进程则优雅的重进php-fpm服务)
pm表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件的说明。

下面4个参数的意思分别为:

pm.max_children:静态方式下开启的php-fpm进程数量。
pm.start_servers:动态方式下的起始php-fpm进程数量。
pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
pm.max_spare_servers:动态方式下的最大php-fpm进程数量。

如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。

如果dm设置为dynamic,那么pm.max_children参数失效,
后面3个参数生效。
系统会在php-fpm运行开始的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。

公式:

假定一个php-fpm进程为30m计算,若4g内存,拿出一半做的php-fpm使用即2g
mem=2048pm=dynamicpm.maxchildren=100(

mem/20)
pm.start_servers = 75 (mem/30)minspareservers+(maxspareserversminspareservers)/2pm.minspareservers=50(

mem/40)
pm.max_spare_servers = 50 ($mem/20)

pm.max_children = 50 是最大可创建的子进程的数量。必须设置。这里表示最多只能50个子进程。

pm.start_servers = 20 随着php-fpm一起启动时创建的子进程数目。默认值:min_spare_servers + (max_spare_servers – min_spare_servers) / 2。这里表示,一起启动会有20个子进程。

pm.min_spare_servers = 10
设置服务器空闲时最小php-fpm进程数量。必须设置。如果空闲的时候,会检查如果少于10个,就会启动几个来补上。

pm.max_spare_servers = 30
设置服务器空闲时最大php-fpm进程数量。必须设置。如果空闲时,会检查进程数,多于30个了,就会关闭几个,达到30个的状态。

实际上的内存消耗是max_children*max_requests*每个请求使用内存
其它php优化
尽量少安装PHP模块,最简单是最好(快)的
socket连接FastCGI,/dev/shm是内存文件系统,socket放在内存中肯定会快些
cat >> /etc/security/limits.conf <<EOF
* soft nproc 65535
* hard nproc 65535
* soft nofile 65535
* hard nofile 65535
EOF
增加 PHP-FPM 打开文件描述符的限制:
# vi $php_install_dir/etc/php-fpm.conf
rlimit_files = 51200

以下摘自(麻子来了)http://blog.64mazi.com/299/

一、常用参数解释:


1)pm = dynamic #对于专用服务器,pm可以设置为static。
#如何控制子进程,选项有static和dynamic。
如果选择static,则由pm.max_children指定固定的子进程数。
如果选择dynamic,则由pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers 参数决定.
2)pm.max_children

在同一时间最大的进程数

pm.max_children = 120

3)pm.start_servers

php-fpm启动时开启的等待请求到来的进程数,默认值为:min_spare_servers + (max_spare_servers - min_spare_servers) / 2

pm.start_servers = 80

4)pm.min_spare_servers
在空闲状态下,运行的最小进程数,如果小于此值,会创建新的进程
pm.min_spare_servers = 60

5)pm.max_spare_servers
在空闲状态下,运行的最大进程数,如果大于此值,会kill部分进程
pm.max_spare_servers = 120

6)pm.process_idle_timeout
空闲多少秒之后进程会被kill,默认为10s
pm.process_idle_timeout = 10s

7)pm.max_requests
每个进程处理多少个请求之后自动终止,可以有效防止内存溢出,如果为0则不会自动终止,默认为0#设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 
如果设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0.
pm.max_requests = 500 

8)pm.status_path
注册的URI,以展示php-fpm状态的统计信息
     pm.status_path = /status
     其中统计页面信息有:
     pool 进程池名称
     process manager 进程管理器名称(static, dynamic or ondemand)
     start time php-fpm启动时间
     start since php-fpm启动的总秒数
     accepted conn 当前进程池接收的请求数
     listen queue 等待队列的请求数
     max listen queue 自启动以来等待队列中最大的请求数
     listen queue len 等待连接socket队列大小
     idle processes 当前空闲的进程数
     active processes 活动的进程数
     total processes 总共的进程数(idle+active)
     max active processes 自启动以来活动的进程数最大值
     max children reached 达到最大进程数的次数

9)ping.path
ping url,可以用来测试php-fpm是否存活并可以响应
ping.path = /ping

10)ping.response
ping url的响应正文返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong.
ping.response = pong


11)pid = run/php-fpm.pid
#pid设置,默认在安装目录中的var/run/php-fpm.pid,建议开启
 
12)error_log = log/php-fpm.log
#错误日志,默认在安装目录中的var/log/php-fpm.log
 
13)log_level = notice
#错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice.
 
14)emergency_restart_threshold = 60
emergency_restart_interval = 60s
#表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。
 
15)process_control_timeout = 0
#设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0.
 
16)daemonize = yes
#后台执行fpm,默认值为yes,如果为了调试可以改为no。在FPM中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。
 
17)listen = 127.0.0.1:9000
#fpm监听端口,即nginx中php处理的地址,一般默认值即可。可用格式为: 'ip:port', 'port', '/path/to/unix/socket'. 每个进程池都需要设置.
 
18)listen.backlog = -1
#backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。 19)listen.allowed_clients = 127.0.0.1
#允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。
默认值是any。每个地址是用逗号分隔. 如果没有设置或者为空,则允许任何服务器请求连接


20)#unix socket设置选项,如果使用tcp方式访问,这里注释即可。
listen.owner = www
listen.group = www
listen.mode = 0666
#启动进程的帐户和组
user = www
group = www
21)request_terminate_timeout = 0 #设置单个请求的超时中止时间. 
该选项可能会对php.ini设置中的'max_execution_time'因为某些特殊原因没有中止运行的脚本有用. 设置为 '0' 表示 'Off'.当经常出现502错误时可以尝试更改此选项。 

22)request_slowlog_timeout = 10s #当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off' 

23)slowlog = log/$pool.log.slow #慢请求的记录日志,配合request_slowlog_timeout使用 

24)rlimit_files = 1024 #设置文件打开描述符的rlimit限制. 默认值: 系统定义值默认可打开句柄是1024,可使用 ulimit -n查看,ulimit -n 2048修改。 

25)rlimit_core = 0 #设置核心rlimit最大限制值. 可用值: 'unlimited' 、0或者正整数. 默认值: 系统定义值. 

26)chroot = #启动时的Chroot目录. 所定义的目录需要是绝对路径. 如果没有设置, 则chroot不被使用. 

27)chdir = #设置启动目录,启动时会自动Chdir到该目录. 所定义的目录需要是绝对路径. 默认值: 当前目录,或者/目录(chroot时) 

28)catch_workers_output = yes #重定向运行过程中的stdout和stderr到主要的错误日志文件中. 
如果没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 
默认值: 空. 

二、php对子进程的三种管理方式
static:
表示在php-fpm运行时直接fork出 pm.max_chindren个子进程
dynamic:
表示,运行时fork出pm.start_servers个进程,随着负载的情况,动态的调整,最多不超过pm.max_children个进程。同时,保证闲置进程数不少于pm.min_spare_servers数量,否则新的进程会被创建,当然也不是无限制的创建,最多闲置进程不超过pm.max_spare_servers数量,超过则一些闲置进程被清理。
ondemand: 
当有请求时,创建进程,启动不创建,最多不超过pm.max_chindren进程数,当进程闲置会在pm.process_idle_timeout秒后被及时释放。


三、重要参数的理解与设置

 

【重要一】
request_terminate_timeout = 120
#表示等待120秒后,结束那些没有自动结束的php脚本,以释放占用的资源。
当PHP运行在php-fpm模式下,php.ini配置的max_execute_time是无效的,
需要在php-fpm.conf中配置另外一个配置项:request_terminate_timeout;以下是官方文档的说明:


set_time_limit()和max_execution_time只影响脚本本身执行的时间。
(这两个参数在php.ini中)任何发生在诸如使用system()的系统调用,流操作,数据库操作等的脚本执行的最大时间不包括其中.
还可以是以下情况:
参考:http://zyan.cc/tags/request_terminate_timeout/1/
  1. $ctx = stream_context_create(array(  
  2.    ‘http’ => array(  
  3.        ‘timeout’ => 1 //设置一个超时时间,单位为秒  
  4.        )
  5.    )
  6. );
  7. file_get_contents(“http://example.com/”, 0, $ctx);  //设置超时时间


下面4个参数的意思分别为:
pm.max_children:静态方式下开启的php-fpm进程数量。
pm.start_servers:动态方式下的起始php-fpm进程数量。
pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
pm.max_spare_servers:动态方式下的最大php-fpm进程数量。

如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。 
系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和 pm.max_spare_servers之间调整php-fpm进程数。

比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服 
务器的负载情况来设置,比较合适的值在5~10之间。


pm = dynamic模式非常灵活,也通常是默认的选项。
但是,dynamic模式为了最大化地优化服务器响应,会造成更多内存使用,因为这种模式只会杀掉超出最大闲置进程数(pm.max_spare_servers)的闲置进程,
比如最大闲置进程数是30,然后网站经历了一次访问高峰,高峰期时共动态开启了50个进程全部忙碌,0个闲置进程数,
接着过了高峰期,可能没有一个请求,于是会有50个闲置进程,但是此时php-fpm只会杀掉20个子进程,
始终剩下30个进程继续作为闲置进程来等待请求,这可能就是为什么过了高峰期后即便请求数大量减少服务器内存使用却也没有大量减少,
也可能是为什么有些时候重启下服务器情况就会好很多,因为重启后,php-fpm

本文来自网络:http://www.cnblogs.com/300js/p/5920124.html

避免PHP-FPM内存泄漏导致内存耗尽

对于PHP-FPM多进程的模式,想要避免内存泄漏问题很简单,就是要让PHP-CGI在处理一定数量进程后退出即可。
否则PHP程序或第三方模块(如Imagemagick扩展)导致的内存泄漏问题会导致内存耗尽或不足。
php-fpm.conf中有相关配置:
#请自行按需求配置

pm.max_requests = 1024

实际上还有另一个跟它有关联的值max_children,这个是每次php-fpm会建立多少个进程,这样实际上的内存消耗是max_children*max_requests*每个请求使用内存。

另外一些粗暴的方法包括建立cron kill掉占用内存过多的php-cgi,

1.检查php进程的内存占用,杀掉内存使用超额的进程

一般情况下,如果php-cgi进程占用超过1%的内存,就得考虑一下是否要杀掉它了。因为普通情况下,php-cgi进程一般占用0.2%或以下。

这里提供一个脚本供各位使用,就是放在cron任务里,每分钟执行一次。

使用crontab -e 命令,然后添加如下调度任务

* * * * * /bin/bash /usr/local/script/kill_php_cgi.sh

kill_php_cgi.sh脚本如下

#!/bin/sh
#如果是要杀掉php-fpm的进程,下面的语句中php-cgi请改成php-fpm
pids=`ps -ef|grep php-cgi|grep -v “grep”|grep -v “$0″| awk ‘{print $2}’`
if [ “$pids” != “” ];then
for  pid  in   $pids;
do
kill -9 $pid
done

fi

2.增加内存,将PHP_FCGI_MAX_REQUESTS的值设置成跟你内存总存储量相对应的值

3.优化程序,降低处理每次请求占用的内存大小

如果PHP-FPM能够提供配置子进程内存超过指定大小就被kill,那就省事多了

以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索内存 , 程序 , 进程 , 内存泄漏 , 配置 php-fpm php fpm内存泄漏、如何避免内存泄漏、c 避免内存泄漏、避免内存泄漏、信息泄漏可导致,以便于您获取更多的相关知识。

本文转自网络  https://yq.aliyun.com/ziliao/9107