php高可用
1. 如何实现php+session+memcached高可用集群
在这个互联网高度发达的时代,许多应用的用户动辄成百上千万,甚至上亿。为了支持海量用户的访问,应用服务器集群这种水平扩展的方式是最常用的。这种情形下,就会涉及到许多单机环境下完全不需要考虑的问题,这其中session的创建、共享和存储是最常见之一。
在单机环境中,Session的创建和存储都是由同一个应用服务器实例来完成,而存储也仅是内存中,最多会在正常的停止服务器的时候,把当前活动的Session钝化到本地,再次启动时重新加载。
而多个实例之间,Session数据是完全隔离的。而为了实现Session的高可用,多实例间数据共享是必然的,下面我们以Redis 的SessionManager实现多Tomcat实例Session共享的配置为例,我们来梳理下一般session共享的流程:
添加具体要使用的manager的Jar文件及其依赖
redis session manager依赖jedis, commons-pool, commons-pool2
对应版本的redis session manager的jar文件
在TOMCAT_HOME/conf/context.xml中增加如下配置
<Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve" />
<Manager className="com.radiadesign.catalina.session.RedisSessionManager"
host="localhost"
port="6379" database="0"
maxInactiveInterval="30" />
其中host和port等替换为对应的配置信息
启动多个Tomcat实例,以自带的examples应用为例进行验证
访问examples应用的servlets/servlet/SessionExample,
在页面中添加数据到session中,并查看页面上对应的session信息
访问另一个实例上相同应用的页面,查看session信息,两者应该是一致的
使用redis-cli查看redis中存储的对应数据,相应的sessionId对应的数据已经保存了下来
以上是一个基本的配置过程,而在这些配置与验证的步骤中,第二步是核心逻辑实现。 前面的文章,曾介绍过Tomcat的Valve,在请求处理时,Pipeline中的各个Valve的invoke方法会依次执行。Tomcat的AccessLogValve介绍
此处的session处理,就是以一个自定义Valve的形式进行的。关于Session的文章,前面也写过几篇,会附在结尾处。
以下是RedisSessionhandlerValve的invoke方法,我们看,主要是在Valve执行后进行Session的存储或移除。
public void invoke(Request request, Response response) {
try {
getNext().invoke(request, response);
} finally {
final Session session = request.getSessionInternal(false);
storeOrRemoveSession(session);
manager.afterRequest();
}
}
而session的保存和移除又是通过manager执行的。 manager.save(session); manager.remove(session);
这里,manager就是前面定义的RedisSessionManager。默认单实例情况下,我们使用的都是StandardManager,对比一下两者,标准的Manager对于session的创建和删除,都会调到其父类ManagerBase中相应的方法,
public void add(Session session) {
sessions.put(session.getIdInternal(), session);
int size = getActiveSessions();
if( size > maxActive ) {
synchronized(maxActiveUpdateLock) {
if( size > maxActive ) {
maxActive = size;
}
}
}
}
public void remove(Session session, boolean update) {
if (session.getIdInternal() != null) {
sessions.remove(session.getIdInternal());
}
}
我们来看,由于其只保存在内存的Map中protected Map<String, Session> sessions = new
ConcurrentHashMap<>(),每个Tomcat实例都对于不同的map,多个实例间无法共享数据。
对应到RedisSessionManager对于session的处理,都是直接操作redis,基本代码是下面这个样:
public void save(Session session) throws IOException {
Jedis jedis = null;
Boolean error = true;
try {
RedisSession redisSession = (RedisSession) session;
Boolean sessionIsDirty = redisSession.isDirty();
redisSession.resetDirtyTracking();
byte[] binaryId = redisSession.getId().getBytes();
jedis = acquireConnection();
if (sessionIsDirty || currentSessionIsPersisted.get() != true) {
jedis.set(binaryId, serializer.serializeFrom(redisSession));
}
currentSessionIsPersisted.set(true);
jedis.expire(binaryId, getMaxInactiveInterval());
} }
移除时的操作是这样的
public void remove(Session session, boolean update) {
Jedis jedis = null;
Boolean error = true;
log.trace("Removing session ID : " + session.getId());
try {
jedis = acquireConnection();
jedis.del(session.getId());
error = false;
} finally {
if (jedis != null) {
returnConnection(jedis, error);
}
}
}
而此时,多个Tomcat实例都读取相同的Redis,session数据是共享的,其它实例的初始请求过来时,由于会执行findSession的操作,此时会从Redis中加载session,
public Session findSession(String id) throws IOException {
RedisSession session;
if (id == null) {
session = null;
currentSessionIsPersisted.set(false);
} else if (id.equals(currentSessionId.get())) {
session = currentSession.get();
} else {
session = loadSessionFromRedis(id); // 看这里,会从redis中load
if (session != null) {
currentSessionIsPersisted.set(true);
}
}
currentSession.set(session);
currentSessionId.set(id);
return session;
}
从而可以保证在一个实例被切换后,另外的实例可以继续响应同一个session的请求。
以上即为Redis实现session共享高可用的一些关键内容。有兴趣的朋友可以看下通过Memcached实现高可用,也是这个原理。顺着这个思路,如果你有将Session存储在其它地方的需求时,完全可以写一个出来,自己动手,丰衣足食。
总结一下,我们是通过自定义的Valve来实现请求后session的拦截,同时,使用自定义的SessionManager,来满足不同的session创建与存储的需求。而至于是存储在Redis/Memcached中,还是存储在DB中,只是位置的区别。原理,是一致的。
2. Nginx 与 PHP 合并部署与分开部署 哪种性能更好一些
当然是分开部署了,很简单的理解,同样的一项业务,分散到不同的营业部的话,处理业务的速度会相应加快,如果把人员集中到一个营业部,你会发现,光排队处理一项就把你搞死。
3. 架构高可用高并发系统的设计原则
通过学习《亿级流量网站架构核心技术》及《linux就该这么学》学习笔记及自己的感悟:架构设计之高可用高并发系统设计原则,架构设计包括墨菲定律、康威定律和二八定律三大定律,而系统设计包括高并发原则、高可用和业务设计原则等。
架构设计三大定律
墨菲定律 – 任何事没有表面看起来那么简单 – 所有的事都会比预计的时间长 – 可能出错的事情总会出错 – 担心某种事情发生,那么它就更有可能发生
康威定律 – 系统架构师公司组织架构的反映 – 按照业务闭环进行系统拆分/组织架构划分,实现闭环、高内聚、低耦合,减少沟通成本 – 如果沟通出现问题,应该考虑进行系统和组织架构的调整 – 适合时机进行系统拆分,不要一开始就吧系统、服务拆分拆的非常细,虽然闭环,但是每个人维护的系统多,维护成本高 – 微服务架构的理论基础 – 康威定律https://yq.aliyun.com/articles/8611– 每个架构师都应该研究下康威定律http://36kr.com/p/5042735.html
二八定律 – 80%的结果取决于20%的原因
系统设计遵循的原则
1.高并发原则
无状态
无状态应用,便于水平扩展
有状态配置可通过配置中心实现无状态
实践: Disconf、Yaconf、Zookpeer、Consul、Confd、Diamond、Xdiamond等
拆分
系统维度:按照系统功能、业务拆分,如购物车,结算,订单等
功能维度:对系统功能在做细粒度拆分
读写维度:根据读写比例特征拆分;读多,可考虑多级缓存;写多,可考虑分库分表
AOP维度: 根据访问特征,按照AOP进行拆分,比如商品详情页可分为CDN、页面渲染系统,CDN就是一个AOP系统
模块维度:对整体代码结构划分Web、Service、DAO
服务化
服务化演进: 进程内服务-单机远程服务-集群手动注册服务-自动注册和发现服务-服务的分组、隔离、路由-服务治理
考虑服务分组、隔离、限流、黑白名单、超时、重试机制、路由、故障补偿等
实践:利用Nginx、HaProxy、LVS等实现负载均衡,ZooKeeper、Consul等实现自动注册和发现服
消息队列
目的: 服务解耦(一对多消费)、异步处理、流量削峰缓冲等
大流量缓冲: 牺牲强一致性,保证最终一致性(案例:库存扣减,现在Redis中做扣减,记录扣减日志,通过后台进程将扣减日志应用到DB)
数据校对: 解决异步消息机制下消息丢失问题
数据异构
数据异构: 通过消息队列机制接收数据变更,原子化存储
数据闭环: 屏蔽多从数据来源,将数据异构存储,形成闭环
缓存银弹
用户层:
DNS缓存
浏览器DNS缓存
操作系统DNS缓存
本地DNS服务商缓存
DNS服务器缓存
客户端缓存
浏览器缓存(Expires、Cache-Control、Last-Modified、Etag)
App客户缓存(js/css/image…)
代理层:
CDN缓存(一般基于ATS、Varnish、Nginx、Squid等构建,边缘节点-二级节点-中心节点-源站)
接入层:
Opcache: 缓存PHP的Opcodes
Proxy_cache: 代理缓存,可以存储到/dev/shm或者SSD
FastCGI Cache
Nginx+Lua+Redis: 业务数据缓存
Nginx为例:
PHP为例:
应用层:
页面静态化
业务数据缓存(Redis/Memcached/本地文件等)
消息队列
数据层:
Nosql: Redis、Memcache、SSDB等
MySQL: Innodb/MyISAM等Query Cache、Key Cache、Innodb Buffer Size等
系统层:
CPU : L1/L2/L3 Cache/NUMA
内存
磁盘:磁盘本身缓存、dirtyratio/dirtybackground_ratio、阵列卡本身缓存
并发化
2.高可用原则
降级
降级开关集中化管理:将开关配置信息推送到各个应用
可降级的多级读服务:如服务调用降级为只读本地缓存
开关前置化:如Nginx+lua(OpenResty)配置降级策略,引流流量;可基于此做灰度策略
业务降级:高并发下,保证核心功能,次要功能可由同步改为异步策略或屏蔽功能
限流
目的: 防止恶意请求攻击或超出系统峰值
实践:
恶意请求流量只访问到Cache
穿透后端应用的流量使用Nginx的limit处理
恶意IP使用Nginx Deny策略或者iptables拒绝
切流量
目的:屏蔽故障机器
实践:
DNS: 更改域名解析入口,如DNSPOD可以添加备用IP,正常IP故障时,会自主切换到备用地址;生效实践较慢
HttpDNS: 为了绕过运营商LocalDNS实现的精准流量调度
LVS/HaProxy/Nginx: 摘除故障节点
可回滚
发布版本失败时可随时快速回退到上一个稳定版本
3.业务设计原则
防重设计
幂等设计
流程定义
状态与状态机
后台系统操作可反馈
后台系统审批化
文档注释
备份
4.总结
先行规划和设计时有必要的,要对现有问题有方案,对未来有预案;欠下的技术债,迟早都是要还的。
本文作者为网易高级运维工程师
4. 开发一个网站,后端用go语言,前端用PHP、Ruby还是python比较好
首先我告诉你,你所描述的全都是后端开发语言,并不是前端
GO、PHP、Ruby、Python全都是后端、且注重的领域不一样
GO专注于大型高并发、高性能项目,R、PY都是面向对象的动态槐漏嫌语言
接下来可以谈一谈目前还可以的PHP语言,开发网站首选,且过去几年PHP中的异步框架也开始兴起,比如Swoole、Swoft和ReactPHP等等,都是用PHP或其他语言编写的框架和服务器
可以让PHP程序员搜神不需要学习另外一门语言、也可以用PHP实现高并发、高可铅手用、微服务架构,即时通信等,关键还省钱解约成本,能够让用户运行真正的协程异步。
PHP7+Swoole性能仅次于C、Java和GO、让PHP程序员既可以动态开发又可以实现高并发!Swoole协程编程、并发编程、Swoft微服务、等技术都是成为一个中高级PHPer,架构师的必经之路,为了帮助大家进阶中高级涨薪、
我为大家准备了一套精品福利!还可加入大牛学习圈子,分享tp,laravel,Swoole,Swoft微服务等教程,各种大牛都是3-8年PHP开发者,每天还有12年的架构师做讲解,助你进阶中高级PHP程序员,增值涨薪!需要可关注本头条号,并且发送私信:PHPPHPer想掌握Swoole协程编程、并发编程、进阶PHP中高级,架构师的可以来学习
5. 大型的PHP应用,通常使用什么应用做消息队列
一、消息队列概述
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
二、消息队列应用场景
以下介绍消息队列在实际应用中常用的使用场景。异步处理,应用解耦,流量削锋和消息通讯四个场景。
2.1异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。(架构KKQ:466097527,欢迎加入)
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
2.2应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:
传统模式的缺点:
1) 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;
2) 订单系统与库存系统耦合;
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。
2.3流量削锋
流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
可以控制活动的人数;
可以缓解短时间内高流量压垮应用;
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;
秒杀业务根据消息队列中的请求信息,再做后续处理。
2.4日志处理
日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:
日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;
Kafka消息队列,负责日志数据的接收,存储和转发;
日志处理应用:订阅并消费kafka队列中的日志数据;
以下是新浪kafka日志处理应用案例:
(1)Kafka:接收用户日志的消息队列。
(2)Logstash:做日志解析,统一成JSON输出给Elasticsearch。
(3)Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,通过index组织数据,兼具强大的搜索和统计功能。
(4)Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK stack的重要原因。
2.5消息通讯
消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
点对点通讯:
客户端A和客户端B使用同一队列,进行消息通讯。
聊天室通讯:
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。
以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
三、消息中间件示例
3.1电商系统
消息队列采用高可用,可持久化的消息中间件。比如Active MQ,Rabbit MQ,Rocket Mq。(1)应用将主干逻辑处理完成后,写入消息队列。消息发送是否成功可以开启消息的确认模式。(消息队列返回消息接收成功状态后,应用再返回,这样保障消息的完整性)
(2)扩展流程(发短信,配送处理)订阅队列消息。采用推或拉的方式获取消息并处理。
(3)消息将应用解耦的同时,带来了数据一致性问题,可以采用最终一致性方式解决。比如主数据写入数据库,扩展应用根据消息队列,并结合数据库方式实现基于消息队列的后续处理。
3.2日志收集系统
分为Zookeeper注册中心,日志收集客户端,Kafka集群和Storm集群(OtherApp)四部分组成。
Zookeeper注册中心,提出负载均衡和地址查找服务;
日志收集客户端,用于采集应用系统的日志,并将数据推送到kafka队列;
四、JMS消息服务
讲消息队列就不得不提JMS 。JMS(Java Message Service,Java消息服务)API是一个消息服务的标准/规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
在EJB架构中,有消息bean可以无缝的与JM消息服务集成。在J2EE架构模式中,有消息服务者模式,用于实现消息与应用直接的解耦。
4.1消息模型
在JMS标准中,有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。
4.1.1 P2P模式
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
P2P的特点
每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)
发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
接收者在成功接收消息之后需向队列应答成功
如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。(架构KKQ:466097527,欢迎加入)
4.1.2 Pub/sub模式
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
Pub/Sub的特点
每个消息可以有多个消费者
发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
为了消费消息,订阅者必须保持运行的状态。
为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。
4.2消息消费
在JMS中,消息的产生和消费都是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。
(1)同步
订阅者或接收者通过receive方法来接收消息,receive方法在接收到消息之前(或超时之前)将一直阻塞;
(2)异步
订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。
JNDI:Java命名和目录接口,是一种标准的Java命名系统接口。可以在网络上查找和访问服务。通过指定一个资源名称,该名称对应于数据库或命名服务中的一个记录,同时返回资源连接建立所必须的信息。
JNDI在JMS中起到查找和访问发送目标或消息来源的作用。(架构KKQ:466097527,欢迎加入)
4.3JMS编程模型
(1) ConnectionFactory
创建Connection对象的工厂,针对两种不同的jms消息模型,分别有QueueConnectionFactory和TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。
(2) Destination
Destination的意思是消息生产者的消息发送目标或者说消息消费者的消息来源。对于消息生产者来说,它的Destination是某个队列(Queue)或某个主题(Topic);对于消息消费者来说,它的Destination也是某个队列或主题(即消息来源)。
所以,Destination实际上就是两种类型的对象:Queue、Topic可以通过JNDI来查找Destination。
(3) Connection
Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装)。Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。
(4) Session
Session是操作消息的接口。可以通过session创建生产者、消费者、消息等。Session提供了事务的功能。当需要使用session发送/接收多个消息时,可以将这些发送/接收动作放到一个事务中。同样,也分QueueSession和TopicSession。
(5) 消息的生产者
消息生产者由Session创建,并用于将消息发送到Destination。同样,消息生产者分两种类型:QueueSender和TopicPublisher。可以调用消息生产者的方法(send或publish方法)发送消息。
(6) 消息消费者
消息消费者由Session创建,用于接收被发送到Destination的消息。两种类型:QueueReceiver和TopicSubscriber。可分别通过session的createReceiver(Queue)或createSubscriber(Topic)来创建。当然,也可以session的creatDurableSubscriber方法来创建持久化的订阅者。
(7) MessageListener
消息监听器。如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。EJB中的MDB(Message-Driven Bean)就是一种MessageListener。
深入学习JMS对掌握JAVA架构,EJB架构有很好的帮助,消息中间件也是大型分布式系统必须的组件。本次分享主要做全局性介绍,具体的深入需要大家学习,实践,总结,领会。
五、常用消息队列
一般商用的容器,比如WebLogic,JBoss,都支持JMS标准,开发上很方便。但免费的比如Tomcat,Jetty等则需要使用第三方的消息中间件。本部分内容介绍常用的消息中间件(Active MQ,Rabbit MQ,Zero MQ,Kafka)以及他们的特点。
5.1 ActiveMQ
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
ActiveMQ特性如下:
⒈ 多种语言和协议编写客户端。语言: Java,C,C++,C#,Ruby,Perl,Python,PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP
⒉ 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)
⒊ 对spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去,而且也支持Spring2.0的特性
⒋ 通过了常见J2EE服务器(如 Geronimo,JBoss 4,GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE 1.4 商业服务器上
⒌ 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
⒍ 支持通过JDBC和journal提供高速的消息持久化
⒎ 从设计上保证了高性能的集群,客户端-服务器,点对点
⒏ 支持Ajax
⒐ 支持与Axis的整合
⒑ 可以很容易得调用内嵌JMS provider,进行测试
5.2 RabbitMQ
RabbitMQ是流行的开源消息队列系统,用erlang语言开发。RabbitMQ是AMQP(高级消息队列协议)的标准实现。支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX,持久化。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
几个重要概念:
Broker:简单来说就是消息队列服务器实体。
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
procer:消息生产者,就是投递消息的程序。
consumer:消息消费者,就是接受消息的程序。
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
消息队列的使用过程,如下:
(1)客户端连接到消息队列服务器,打开一个channel。
(2)客户端声明一个exchange,并设置相关属性。
(3)客户端声明一个queue,并设置相关属性。
(4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
(5)客户端投递消息到exchange。
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
5.3 ZeroMQ
号称史上最快的消息队列,它实际类似于Socket的一系列接口,他跟Socket的区别是:普通的socket是端到端的(1:1的关系),而ZMQ却是可以N:M 的关系,人们对BSD套接字的了解较多的是点对点的连接,点对点连接需要显式地建立连接、销毁连接、选择协议(TCP/UDP)和处理错误等,而ZMQ屏蔽了这些细节,让你的网络编程更为简单。ZMQ用于node与node间的通信,node可以是主机或者是进程。
引用官方的说法: “ZMQ(以下ZeroMQ简称ZMQ)是一个简单好用的传输层,像框架一样的一个socket library,他使得Socket编程更加简单、简洁和性能更高。是一个消息处理队列库,可在多个线程、内核和主机盒之间弹性伸缩。ZMQ的明确目标是“成为标准网络协议栈的一部分,之后进入Linux内核”。现在还未看到它们的成功。但是,它无疑是极具前景的、并且是人们更加需要的“传统”BSD套接字之上的一 层封装。ZMQ让编写高性能网络应用程序极为简单和有趣。”
特点是:
高性能,非持久化;
跨平台:支持Linux、Windows、OS X等。
多语言支持; C、C++、Java、.NET、Python等30多种开发语言。
可单独部署或集成到应用中使用;
可作为Socket通信库使用。
与RabbitMQ相比,ZMQ并不像是一个传统意义上的消息队列服务器,事实上,它也根本不是一个服务器,更像一个底层的网络通讯库,在Socket API之上做了一层封装,将网络通讯、进程通讯和线程通讯抽象为统一的API接口。支持“Request-Reply “,”Publisher-Subscriber“,”Parallel Pipeline”三种基本模型和扩展模型。
ZeroMQ高性能设计要点:
1、无锁的队列模型
对于跨线程间的交互(用户端和session)之间的数据交换通道pipe,采用无锁的队列算法CAS;在pipe两端注册有异步事件,在读或者写消息到pipe的时,会自动触发读写事件。
2、批量处理的算法
对于传统的消息处理,每个消息在发送和接收的时候,都需要系统的调用,这样对于大量的消息,系统的开销比较大,zeroMQ对于批量的消息,进行了适应性的优化,可以批量的接收和发送消息。
3、多核下的线程绑定,无须CPU切换
区别于传统的多线程并发模式,信号量或者临界区, zeroMQ充分利用多核的优势,每个核绑定运行一个工作者线程,避免多线程之间的CPU切换开销。
5.4 Kafka
Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群机来提供实时的消费。
Kafka是一种高吞吐量的分布式发布订阅消息系统,有如下特性:
通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。(文件追加的方式写入数据,过期的数据定期删除)
高吞吐量:即使是非常普通的硬件Kafka也可以支持每秒数百万的消息。
支持通过Kafka服务器和消费机集群来分区消息。
支持Hadoop并行数据加载。
Kafka相关概念
Broker
Kafka集群包含一个或多个服务器,这种服务器被称为broker[5]
Topic
每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic。(物理上不同Topic的消息分开存储,逻辑上一个Topic的消息虽然保存于一个或多个broker上但用户只需指定消息的Topic即可生产或消费数据而不必关心数据存于何处)
Partition
Parition是物理上的概念,每个Topic包含一个或多个Partition.
Procer
负责发布消息到Kafka broker
Consumer
消息消费者,向Kafka broker读取消息的客户端。
Consumer Group
每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)。
一般应用在大数据日志处理或对实时性(少量延迟),可靠性(少量丢数据)要求稍低的场景使用。
6. PHP中高级面试题 – 第三天
一、简述一下MongoDB的应用场景
mongodb 支持副本集、索引、自动分片,可以保证较高的性能和可用性。
更高的写入负载
默认情况下,MongoDB 更侧重高数据写入性能,而非事务安全,MongoDB 很适合业务系统中有大量 “低价值” 数据的场景。但是应当避免在高事务安全性的系统中使用 MongoDB,除非能从架构设计上保证事务安全。
高可用性
MongoDB 的复副集 (Master-Slave) 配置非常简洁方便,此外,MongoDB 可以快速响应的处理单节点故障,自动、安全地完成故障转移。这些特性使得 MongoDB 能在一个相对不稳定(如云主机)的环境中,保持高可用性。
数据量很大或者未来会变得很大
依赖数据库 (MySQL) 自身的特性,完成数据的扩展是较困难的事,在 MySQL 中,当一个单达表到 5-10GB 时会出现明显的性能降级,此时需要通过数据的水平和垂直拆分、库的拆分完成扩展,使用 MySQL 通常需要借助驱动层或代理层完成这类需求。而 MongoDB 内建了多种数据分片的特性,可以很好地适应大数据量的需求。
基于位置的数据查询
MongoDB 支持二维空间索引,因此可以快速及精确地从指定位置获取数据。
表结构不明确
在一些传统 RDBMS 中,增加一个字段会锁住整个数据库 / 表,或者在执行一个重负载的请求时会明显造成其它请求的性能降级。通常发生在数据表大于 1G 的时候(当大于 1TB 时更甚)。 因 MongoDB 是文档型数据库,为非结构货的文档增加一个新字段是很快速的操作,并且不会影响到已有数据。另外一个好处当业务数据发生变化时,是将不再需要由 DBA 修改表结构。
二、数据库设计经验,为什么进行分表?分库?一般多少数据量开始分表?分库?分库分表的目的?
1、为什么要分表
当一张表的数据达到几百万时,你查询一次所花的时间会变多,如果有联合查询的话,有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。
分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。数据库中的数据量不一定是可控的,在未进行分库分表的情况下,随着时间和业务的发展,库中的表会越来越多,表中的数据量也会越来越大,相应地,数据操作,增删改查的开销也会越来越大;另外,由于无法进行分布式式部署,而一台服务器的资源(CPU、磁盘、内存、IO 等)是有限的,最终数据库所能承载的数据量、数据处理能力都将遭遇瓶颈。
2、分表的方案
做 mysql 集群,有人会问 mysql 集群,根分表有什么关系吗?虽然它不是实际意义上的分表,但是它启到了分表的作用,做集群的意义是什么呢?为一个数据库减轻负担,说白了就是减少 sql 排队队列中的 sql 的数量,举个例子:有 10 个 sql 请求,如果放在一个数据库服务器的排队队列中,他要等很长时间,如果把这 10 个 sql 请求,分配到 5 个数据库服务器的排队队列中,一个数据库服务器的队列中只有 2 个,这样等待时间是不是大大的缩短了呢?
linux mysql proxy 的安装,配置,以及读写分离
mysql replication 互为主从的安装及配置,以及数据同步
优点:扩展性好,没有多个分表后的复杂操作(php 代码)
缺点:单个表的数据量还是没有变,一次操作所花的时间还是那么多,硬件开销大。
三、简述一下数据库主从复制,读写分离
* 什么是主从复制
主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库;
* 主从复制的原理:
1.数据库有个bin-log二进制文件,记录了所有的sql语句。
2.只需要把主数据库的bin-log文件中的sql语句复制。
3.让其从数据的relay-log重做日志文件中再执行一次这些sql语句即可。
* 主从复制的作用
1.做数据的热备份,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。
2.架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问频率,提高单机的I/O性能
3.主从复制是读写分离的基础,使数据库能制成更大 的并发。例如子报表中,由于部署报表的sql语句十分慢,导致锁表,影响前台的服务。如果前台服务使用master,报表使用slave,那么报表sql将不会造成前台所,保证了前台的访问速度。
* 主从复制的几种方式:
1.同步复制:所谓的同步复制,意思是master的变化,必须等待slave-1,slave-2,…,slave-n完成后才能返回。
2.异步复制:如同AJAX请求一样。master只需要完成自己的数据库操作即可。至于slaves是否收到二进制日志,是否完成操作,不用关心。MYSQL的默认设置。
3.半同步复制:master只保证slaves中的一个操作成功,就返回,其他slave不管。
这个功能,是由google为MYSQL引入的。
* 关于读写分离
在完成主从复制时,由于slave是需要同步master的。所以对于insert/delete/update这些更新数据库的操作,应该在master中完成。而select的查询操作,则落下到slave中。
7. php和mysql查询效率如何优化
那就用sphinx 技术,目前这个是最好的,没有之一。
下面是我网络弄过来的,怎么使用还得你自己看白皮书了。
Sphinx的主要特性包括:
高速索引 (在新款CPU上,近10 MB/秒);
高速搜索 (2-4G的文本量中平均查询速度不到0.1秒);
高可用性 (单CPU上最大可支持100 GB的文本,100M文档);
提供良好的相关性排名
支持分布式搜索;
提供文档摘要生成;
提供从MySQL内部的插件式存储引擎上搜索
支持布尔,短语, 和近义词查询;
支持每个文档多个全文检索域(默认最大32个);
支持每个文档多属性;
支持断词;
支持单字节编码与UTF-8编码。[1]