设计模式

3种基本设计模式

  1. 工厂模式,工厂方法或者类生成对象,而不是在代码中直接new
  2. 单例模式,使某个类的对象仅允许创建一个
  3. 注册模式,全局共享和交换对象

代理模式

  1. 在客户端与实体之间建立一个代理对象(proxy),客户端对实体进行操作全部委派给代理对象,隐藏实体的具体实现细节
  2. proxy还可以与业务代码分离,部署到另外的服务器。业务代码中通过RPC来委派任务

迭代器模式

  1. 在不需要了解内部实现的前提下,遍历一个聚合对象的内部元素
  2. 相比于传统的编程模式,迭代器模式可以隐藏遍历元素的所需的操作

装饰器模式

  1. 可以动态的添加修改类的功能
  2. 一个类提供了一项功能,如果要修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重新实现类的方法
  3. 使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性

观察者模式

  1. 当一个对象状态发生改变时,依赖它的对象全部会受到通知,并自动更新
  2. 场景: 一个事件发生后,要执行一连串更新操作。传统的编程方式就是在事件的代码之后直接加入处理逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码
  3. 观察者模式实现了低耦合,非侵入的通知和更新机制

高级 PHP

面向对象5原则

单一职责原则(single-resposibility principle)

其核心思想为:一个类,最好只做一件事,只有一个引起它的变化的原因

开放-封闭原则(The Open-Close principle)

其核心思想为:对扩展开放,对修改封闭
软件实体(类、模块、函数)应该是可扩展的,但是不可修改的。也就是说,对于扩展是开放的,对于更改是封闭的。怎样可能在不改动模块源代码的情况下去更改它的行为呢?怎样才能在无需对模块进行改动的情况下改变它的功能呢?关键是抽象!因此在进行面向对象设计时要尽量考虑接口封装机制、抽象机制和多态技术。该原则同样适合于非面向对象的方法,是软件工程设计方法的重要原则之一

Liskov 替换原则(liskov-substitution principle)

其核心思想:子类必须能够替换其基类
子类应当可以替换父类出现在父类能够出现的任何地方

依赖倒置原则(dependency-inversion principle)

其核心思想:依赖于抽象
一个类不应该强依赖另外一个类,每个类对于另外一个类都是可替换的
1、高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
2、抽象不应该依赖于细节。细节应该依赖于抽象。在进行业务设计时,于特定业务有关的依赖关系应该尽量依赖接口和抽象类,而不是依赖于具体类。具体类只负责相关业务的实现,修改具体类不影响与特定业务有关的依赖关系。

接口分离原则(interface-segregation principle)

linux相关操作

防火墙

关闭防火墙

/etc/init.d/iptables stop
service iptables stop # 停止服务

查看防火墙信息

/etc/init.d/iptables status

开放端口:8080

/sbin/iptables -I INPUT -p tcp –dport 8080 -j ACCEPT

重启防火墙以便改动生效:(或者直接重启系统)

/etc/init.d/iptables restart

将更改进行保存

通过网址自动部署web

  1. 部署代码至单独的web站点
    https://github.com/markomarkovic/simple-php-git-deploy.git
  2. 配置www-data用户
    /usr/sbin/nologin=>/bin/bash,且可找到www-data用户的目录:/var/www
  3. 设置www-data登录密码
    sudo passwd www-data
  4. 设置/var/www归属
    sudo chown -R www-data:www-data /var/www/
  5. 切换到www-data用户
    su /var/www
  6. /var/www/.ssh目录中生成ssh key
    sudo -Hu www-data ssh-keygen -t rsa -C “entimm@gmail.com”
  7. 登录git托管网址,把id_rsa.pub公约贴上去
  8. 设置webhook

很有用的一些技巧

在利用 xdebug 和 phpstorm 调试通过终端执行的 PHP 脚本时,在终端执行先语句,即可对该脚本进行调试

1
export XDEBUG_CONFIG = "idekey=PHPSTORM"

通过SSH方式登录远程服务器时,通过下面方式免除每次都要输入密码

1
2
3
4
5
6
7
8
9
10
11
12
# 先在本机生成 authentication keys,注意不能输入 passphrase
# 这里假设 A 是本机,B是远端
a@A:~> ssh-keygen -t rsa
# 然后登录 B,并建立一个目录 ~/.ssh
a@A:~> ssh b@B mkdir -p .ssh
# 最后把 A 的 id_rsa.pub 追加到 B 的 ~/.ssh/authorized_keys 中去
a@A:~> cat ~/.ssh/id_rsa.pub | ssh b@B 'cat >> ~/.ssh/authorized_keys'
# 然后就成功了
a@A:~> ssh b@B

使用的 PHP 的内建服务器

1
php -S 主机名或地址@端口号 -t 目录

svn 检出当前目前下的文件或文件名,这在开发过程中不想检出全部数据时很有用

1
svn update --set-depth immediates

git 新建一个全新的空白分支

1
2
git checkout --orphan 分支名
git clean -f

面试问题总结

php魔术方法(14项)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__construct() 实例化类时自动调用.
__destruct() 类对象使用结束时自动调用.
__set() 在给未定义的属性赋值的时候调用.
__get() 调用未定义的属性时候调用.
__isset() 使用isset()或empty()函数时候会调用.
__unset() 使用unset()时候会调用.
__sleep() 使用serialize序列化时候调用.
__wakeup() 使用unserialize反序列化的时候调用.
__call() 调用一个不存在的方法的时候调用.
__callStatic()调用一个不存在的静态方法是调用.
__toString() 把对象转换成字符串的时候会调用.比如 echo.
__invoke() 当尝试把对象当方法调用时调用.
__set_state() 当使用var_export()函数时候调用.接受一个数组参数.
__clone() 当使用clone复制一个对象时候调用.

HTTP协议中几个状态码的含义.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功并且服务器创建了新的资源
202 Accepted 服务器已接受请求,但尚未处理
301 Moved Permanently 请求的网页已永久移动到新位置.
302 Found 临时性重定向.
303 See Other 临时性重定向,且总是使用 GET 请求新的 URI.
304 Not Modified 自从上次请求后,请求的网页未修改过.
400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求.
401 Unauthorized 请求未授权.
403 Forbidden 禁止访问.
404 Not Found 找不到如何与 URI 相匹配的资源.
500 Internal Server Error 最常见的服务器端错误.
503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护).
100 Continue
101 Switching Protocols
200 OK
201 Created
202 Accepted
203 Non-Authoritative Information
204 No Content
205 Reset Content
206 Partial Content
300 Multiple Choices
301 Moved Permanently
302 Found
303 See Other
304 Not Modified
305 Use Proxy
307 Temporary Redirect
400 Bad Request
401 Unauthorized
402 Payment Required
403 Forbidden
404 Not Found
405 Method Not Allowed
406 Not Acceptable
407 Proxy Authentication Required
408 Request Time-out
409 Conflict
410 Gone
411 Length Required
412 Precondition Failed
413 Request Entity Too Large
414 Request-URI Too Large
415 Unsupported Media Type
416 Requested range not satisfiable
417 Expectation Failed
500 Internal Server Error
501 Not Implemented
502 Bad Gateway
503 Service Unavailable
504 Gateway Time-out
505 HTTP Version not supported

语句include和require的区别

在失败的时候:
include 产生一个 warning ,而 require 直接产生错误中断;

require 在运行前载入;
include 在运行时载入;

require_onceinclude_once 可以避免重复包含同一文件.

如果网站框架的设计能避免重复引用的话,推荐用include/require,
因为他们比_once速度稍快.

事务的特征(ACID)

面试可能会被问到的问题

抽象 vs 接口

抽象类是一种不能被实例化的类,只能作为其他类的父类来使用。
抽象类是通过关键字 abstract 来声明的。
抽象类与普通类相似,都包含成员变量和成员方法,两者的区别在于,抽象类中至少要包含一个抽象方法,
抽象方法没有方法体,该方法天生就是要被子类重写的。
抽象方法的格式为:abstract function abstractMethod();

因为php中只支持单继承,如果想实现多重继承,就要使用接口。也就是说子类可以实现多个接口。
接口是通过interface关键字来声明的,接口中的成员常量和方法都是public的,方法可以不写关键字public,
接口中的方法也是没有方法体。接口中的方法也天生就是要被子类实现的。

抽象类和接口实现的功能十分相似,最大的不同是接口能实现多继承。在应用中选择抽象类还是接口要看具体实现。
子类继承抽象类使用extends,子类实现接口使用implements。

文件上传注意事项

  1. 首现要在php.ini中开启文件上传;
  2. 在php.ini中有一个允许上传的最大值,默认是2MB。必要的时候可以更改;
  3. 上传表单一定要记住在form标签中写上enctype=”multipart/form-data”;

cookie作用

  1. 记录用户访问的部分信息
  2. 在页面间传递变量

php 的数据类型

php支持8种原始数据类型。
包括:
四种标量类型(布尔型boolean,整型interger,浮点型float/double , 字符串string)
两种复合类型(数组array , 对象object)
两种特殊类型(资源resource,NULL)

Cgi、FastCgi 与 PHP-FPM

CGI 的作用

web server(比如说 nginx)只是内容的分发者。

  • 如果请求 /index.html,那么 web server 会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。
  • 如果现在请求的是 /index.php,根据配置文件,nginx 知道这个不是静态文件,需要去找 PHP 解析器来处理,那么他会把这个请求简单处理后交给 PHP 解析器。Nginx 会传哪些数据给 PHP 解析器呢?url、查询字符串、POST 数据、HTTP header等等,CGI就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。

当 web server 收到 /index.php 这个请求后,会启动对应的 CGI 程序,这里就是 PHP 的解析器。接下来 PHP 解析器会解析 php.ini 文件,初始化执行环境,然后处理请求,再以规定 CGI 规定的格式返回处理后的结果,退出进程。web server 再把结果返回给浏览器。

fastCgi 是什么

fastCgi 是用来提高 CGI 程序性能的。

那么 CGI 程序的性能问题在哪呢?”PHP 解析器会解析 php.ini 文件,初始化执行环境”,就是这里了。标准的 CGI 对每个请求都会执行这些步骤,所以处理每个请求的时间会比较长。

那么 fastCgi 是怎么做的呢?首先,fastCgi 会先启一个 master,解析配置文件,初始化执行环境,然后再启动多个 worker。当请求过来时,master 会传递给一个 worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率自然是高。而且当 worker 不够用时,master 可以根据配置预先启动几个 worker 等着;当然空闲 worker 太多时,也会停掉一些,这样就提高了性能,也节约了资源。这就是 fastCgi 对进程的管理。

PHP-FPM 是什么

PHP-FPM 是一个实现了 FastCgi 的程序,被 PHP 官方收录。

mysql知识

MySQL存储引擎 MyISAM 和 InnoDB 的区别

  • MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.
  • InnoDB不支持FULLTEXT类型的索引.
  • InnoDB中不保存表的具体行数,但是MyISAM只要简单的读出保存好的行数即可.
  • MyISAM支持表锁,只有读读之间是并发的,写写之间和读写之间(读和插入之间是可以并发的,去设置concurrent_insert参数,定期执行表优化操作,更新操作就没有办法了)是串行的,所以写起来慢;InnoDB支持行锁,这个一般指的是sql用到索引的时候,行锁是加在索引上的,不是加在数据记录上的,如果sql没有用到索引,仍然会锁定表,普通的select是不需要锁的
  • MyISAM索引btree上的节点是一个指向数据物理位置的指针,所以查找起来很快;nnodb索引节点存的则是数据的主键,所以需要根据主键二次查找
  • 因为在使用索引的时候用的是行锁,锁的粒度小,竞争相同锁的情况就少,就增加了并发处理,所以并发读写的效率还是很优秀的,问题在于索引查询后的根据主键的二次查找导致效率低

数据库设计范式

第一范式:每个属性都不可再分解
第二范式:每个属性都依赖与主键
第三范式:除了主键外,其他属性都没有依赖关系

MYSQL 性能优化

  • 选择合适的存储引擎,设计良好的数据库结构,选择合适的表字段数据类型,允许部分数据冗余(空间换时间)
  • 适当的添加索引
  • 优化 sql 语句,不同的语句,根据你选择的引擎、表中数据的分布情况、索引情况、数据库优化策略、查询中的锁策略等因素,最终查询的效率相差很大;优化要从整体去考虑,有时你优化一条语句后,其它查询反而效率被降低了,所以要取一个平衡点
  • Cache(缓存数据)
    查询:查询之前,要在Memcached或Redis中查找结果,如果找到,则返回它;如果未找到,则到数据库服务器上执行查询,并将结果返回给Memcached或Redis
    插入:先把数据插入数据库,在内存中受此影响的数据库将变成无效
  • 读写分离
  • 分布数据(分割数据)找规律分表,减少单表中的数据量提高查询速度

关于缓存

  • 一般的首页不应当有查询,对首页生成静态页面
  • 不经常改动的页面,生成静态页面.

切分

  • 垂直切分保证业务的独立性,防止不同业务争抢资源,毕竟业务是有优先级的
  • 切分后也可对不同片数据进行不同优化。如按时间切分,超过一定时间数据不允许修改,就可以引入压缩了,数据传输及读取减少很多
  • 数据是否存在明显的冷热(考虑旧数据归档)

关于高并发的处理

用同步队列,就可以实现。库存比如是1000,那就存1000个随机数到队列中,拿到随机数的人去换取真实的商品。这样数据库的压力都小。最多更新1000次。队列可以系统加载时创建,也可以做为持久化保存在文件中。

关于库存和秒杀的解决方案

  1. 用额外的单进程处理一个队列,下单请求放到队列里,一个个处理,就不会有并发的问题了,但是要额外的后台进程以及延迟问题,不予考虑。
  2. 根据update结果来判断,我们可以加一个判断条件update … where 库存>0,如果返回false,则说明库存不足,并回滚事务。
  3. 借助文件排他锁,在处理下单请求的时候,用flock锁定一个文件,如果锁定失败说明有其他订单正在处理,此时要么等待要么直接提示用户”服务器繁忙”
1
2
3
4
5
6
7
8
9
//阻塞(等待)模式
$fp = fopen("lock.txt", "w+");
if(flock($fp,LOCK_EX))
{
//..处理订单
flock($fp,LOCK_UN);
}
fclose($fp);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//非阻塞模式
$fp = fopen("lock.txt", "w+");
if(flock($fp,LOCK_EX | LOCK_NB))
{
//..处理订单
flock($fp,LOCK_UN);
}
else
{
echo "系统繁忙,请稍后再试";
}
fclose($fp);
//采用哪种方式,看并发数量吧。