├── array.md
├── .DS_Store
├── images
├── ok.png
├── .DS_Store
├── 302.png
├── 403.png
├── 404.png
├── auto-load.png
├── iterator.png
├── iterator2.png
├── nginx-fz.png
├── nginx_gzip1.png
├── nginx_gzip2.png
└── nginx_gzip2.2.png
├── miaosha.md
├── http.md
├── untitled
├── jwt_diy
├── config.php
├── user.php
├── login.php
└── Core
│ ├── Common.php
│ ├── RedisService.php
│ └── Controller.php
├── grammar.md
├── README.md
├── redis.md
├── oop.md
├── mysql.md
├── senior.md
├── arithmetic.md
├── nginx.md
├── designPatterns.md
└── high-php.md
/array.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/.DS_Store
--------------------------------------------------------------------------------
/images/ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/ok.png
--------------------------------------------------------------------------------
/images/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/.DS_Store
--------------------------------------------------------------------------------
/images/302.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/302.png
--------------------------------------------------------------------------------
/images/403.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/403.png
--------------------------------------------------------------------------------
/images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/404.png
--------------------------------------------------------------------------------
/images/auto-load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/auto-load.png
--------------------------------------------------------------------------------
/images/iterator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/iterator.png
--------------------------------------------------------------------------------
/images/iterator2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/iterator2.png
--------------------------------------------------------------------------------
/images/nginx-fz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/nginx-fz.png
--------------------------------------------------------------------------------
/images/nginx_gzip1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/nginx_gzip1.png
--------------------------------------------------------------------------------
/images/nginx_gzip2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/nginx_gzip2.png
--------------------------------------------------------------------------------
/images/nginx_gzip2.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lisiqiong/phper/HEAD/images/nginx_gzip2.2.png
--------------------------------------------------------------------------------
/miaosha.md:
--------------------------------------------------------------------------------
1 |
2 | 基本需求-扣库存实现
3 | 1.初始化库存到本地库存
4 | 2.本地减库存,成功则进行统一减库存,失败则返回
5 | 3.统一减库存成功则写入MQ,异步创建订单
6 | 4.告知用户抢购成功
--------------------------------------------------------------------------------
/http.md:
--------------------------------------------------------------------------------
1 | # http协议学习
2 | * http协议说明(#http协议说明)
3 | * 无状态
4 | * http状态码
5 | * telnet实现get请求
6 | * telnet实现post请求
7 | * telnet实现文件上传
8 |
9 | ## http协议说明
10 |
11 | ### http协议执行过程
12 | - 1.客户机通过tcp/ip协议建立到服务器的tcp连接
13 | - 2.客户端向服务端发送http协议请求
14 | - 3.服务端向客户端发送http协议应答包
15 | - 4.断开连接,客户端渲染html文档
16 |
--------------------------------------------------------------------------------
/untitled:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 我们公司目前未上市,2017年公司另外成立了一个合伙人有限公司,去投现在的公司。本来是想拉投资的结果,情况是直到现在投资也没有拉到。2017年购买了2.5w公司的股权,当时投股前,开会讨论的是员工如果离职公司按净资产回购股权,但是公司说现在还亏钱,回购的话我们不划算,让我可以先离职再等等,事实情况是去年有个同事购买的是5w股到现在都公司都没有回购。我们签的合伙人协议书所有人都签了字,不过是一份,直到现在我要离职了,才给了我们一份复印件。
5 | 下面是我的出资证明书,公司六月份资产负载表,以及股权协议书的入伙以及退伙的合同条款
6 | 我想咨询的问题是:
7 | 1.我是一个小股东
--------------------------------------------------------------------------------
/jwt_diy/config.php:
--------------------------------------------------------------------------------
1 | [
6 | 'host' => 'localhost',
7 | 'port' => '6379',
8 | 'auth' => '123456',
9 | 'db_id' => 0,//redis的第几个数据库仓库
10 | ],
11 | //是否开启接口校验,true开启,false,关闭
12 | 'checkApi'=>true,
13 | //加密sign的盐值
14 | 'apiSerect'=>'test_jwt'
15 | ];
--------------------------------------------------------------------------------
/jwt_diy/user.php:
--------------------------------------------------------------------------------
1 | 2,
20 | "name"=>'巴八灵',
21 | "age"=>30,
22 | ];
23 | if($this->mid==$_POST['mid']){
24 | Common::outJson(0,'成功获取用户信息',$userInfo);
25 | }else{
26 | Common::outJson(-1,'未找到该用户信息');
27 | }
28 | }
29 | }
30 | //获取用户信息
31 | $user = new UserController();
32 | $user->getUser();
--------------------------------------------------------------------------------
/jwt_diy/login.php:
--------------------------------------------------------------------------------
1 | set($key,$uid,3600);
29 | $data['token'] = $token;
30 | Common::outJson(0,'登录成功',$data);
31 |
--------------------------------------------------------------------------------
/jwt_diy/Core/Common.php:
--------------------------------------------------------------------------------
1 | $code,
15 | 'msg'=>$msg,
16 | ];
17 | if(!empty($data)){
18 | $outData['data'] = $data;
19 | }
20 | echo json_encode($outData);
21 | die();
22 | }
23 |
24 | /***
25 | * @desc 创建token
26 | * @param $uid
27 | */
28 | public static function createToken($uid){
29 | $time = time();
30 | $rand = mt_rand(100,999);
31 | $token = md5($time.$rand.'jwt-token'.$uid);
32 | return $token;
33 | }
34 |
35 | /**
36 | * @desc 获取配置信息
37 | * @param $type 配置信息的类型,为空获取所有配置信息
38 | */
39 | public static function getConfig($type=''){
40 | $config = include "./config.php";
41 | if(empty($type)){
42 | return $config;
43 | }else{
44 | if(isset($config[$type])){
45 | return $config[$type];
46 | }
47 | return [];
48 | }
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/jwt_diy/Core/RedisService.php:
--------------------------------------------------------------------------------
1 | redis = new \Redis();
20 | $this->port = $config['port'] ? $config['port'] : 6379;
21 | $this->host = $config['host'];
22 | if(isset($config['db_id'])){
23 | $this->dbId = $config['db_id'];
24 | $this->redis->connect($this->host, $this->port);
25 | }
26 | if(isset($config['auth']))
27 | {
28 | $this->redis->auth($config['auth']);
29 | $this->auth = $config['auth'];
30 | }
31 | $this->redis->select($this->dbId);
32 | }
33 |
34 | /**
35 | *@desc 得到实例化的对象
36 | ***/
37 | public static function getInstance($config){
38 | if(!self::$_instance instanceof self) {
39 | self::$_instance = new self($config);
40 | }
41 | return self::$_instance;
42 |
43 | }
44 |
45 | /**
46 | *@desc 防止克隆
47 | **/
48 | private function __clone(){}
49 |
50 | /*
51 | *@desc 设置字符串类型的值,以及失效时间
52 | **/
53 | public function set($key,$value=0,$timeout=0){
54 | if(empty($value)){
55 | $this->error = "设置键值不能够为空哦~";
56 | return $this->error;
57 | }
58 | $res = $this->redis->set($key,$value);
59 | if($timeout){
60 | $this->redis->expire($key,$timeout);
61 | }
62 | return $res;
63 | }
64 |
65 | /**
66 | *@desc 获取字符串类型的值
67 | **/
68 | public function get($key){
69 | return $this->redis->get($key);
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/grammar.md:
--------------------------------------------------------------------------------
1 | # php语法基础整理
2 | + [运算符++](#运算符++)
3 | + [数组指针](#数组指针)
4 | + 文件操作管理
5 | + 图片操作
6 | + 字符串操作
7 |
8 | ## 数组指针
9 | ### 1.介绍几个数组指针的函数
10 | - current() - 返回数组中的当前单元
11 | - end() - 将数组的内部指针指向最后一个单元
12 | - prev() - 将数组的内部指针倒回一位
13 | - reset() - 将数组的内部指针指向第一个单元
14 | - each() - 返回数组中当前的键/值对并将数组指针向前移动一步
15 |
16 |
17 | ```
18 | '.current($listArr).PHP_EOL;
27 | next($listArr);
28 | echo "第二个元素".key($listArr).'=>'.current($listArr).PHP_EOL;
29 | next($listArr);
30 | echo "第三个元素".key($listArr).'=>'.current($listArr).PHP_EOL;
31 | end($listArr);
32 | echo "最后一个元素".key($listArr).'=>'.current($listArr).PHP_EOL;
33 | prev($listArr);//内部指针倒回一位
34 | echo "倒数第二位".key($listArr).'=>'.current($listArr).PHP_EOL;
35 | reset($listArr);
36 | echo "第一个元素".key($listArr).'=>'.current($listArr).PHP_EOL;
37 |
38 | ```
39 |
40 | #### 输出结果
41 | ```
42 | 第一个元素0=>1232
43 | 第二个元素1=>2456
44 | 第三个元素2=>7789
45 | 最后一个元素11=>3214
46 | 倒数第二位10=>2212
47 | 第一个元素0=>1232
48 | ```
49 |
50 | ### 2.使用each循环数组
51 | ```
52 | _config = Common::getConfig();
33 | //2.获取redis对象
34 | $redisConfig = $this->_config['redis'];
35 | $this->redis = RedisService::getInstance($redisConfig);
36 |
37 | //3.token校验
38 | $this->checkToken();
39 | //4.校验api的合法性check_api为true校验,为false不用校验
40 | if($this->_config['checkApi']){
41 | // 5. sign签名验证
42 | $this->checkSign();
43 |
44 | //6.校验nonce,预防接口重放
45 | $this->checkNonce();
46 | }
47 | }
48 |
49 | /**
50 | * @desc 校验token的有效性
51 | */
52 | private function checkToken(){
53 | if(!isset($_POST['token'])){
54 | Common::outJson('10000','token不能够为空');
55 | }
56 | $this->token = $_POST['token'];
57 | $key = "token:".$this->token;
58 | $mid = $this->redis->get($key);
59 | if(!$mid){
60 | Common::outJson('10001','token已过期或不合法,请先登录系统 ');
61 | }
62 | $this->mid = $mid;
63 | }
64 |
65 | /**
66 | * @desc 校验签名
67 | */
68 | private function checkSign(){
69 | if(!isset($_GET['sign'])){
70 | Common::outJson('10002','sign校验码为空');
71 | }
72 | $this->sign = $_GET['sign'];
73 | $postParams = $_POST;
74 | $params = [];
75 | foreach($postParams as $k=>$v) {
76 | $params[] = sprintf("%s%s", $k,$v);
77 | }
78 | sort($params);
79 | $apiSerect = $this->_config['apiSerect'];
80 | $str = sprintf("%s%s%s", $apiSerect, implode('', $params), $apiSerect);
81 | if ( md5($str) != $this->sign ) {
82 | Common::outJson('10004','传递的数据被篡改,请求不合法');
83 | }
84 | }
85 |
86 | /**
87 | * @desc nonce校验预防接口重放
88 | */
89 | private function checkNonce(){
90 | if(!isset($_POST['nonce'])){
91 | Common::outJson('10003','nonce为空');
92 | }
93 | $this->nonce = $_POST['nonce'];
94 | $nonceKey = sprintf("sign:%s:nonce:%s", $this->sign, $this->nonce);
95 | $nonV = $this->redis->get($nonceKey);
96 | if ( !empty($nonV)) {
97 | Common::outJson('10005','该url已经被调用过,不能够重复使用');
98 | } else {
99 | $this->redis->set($nonceKey,$this->nonce,360);
100 | }
101 | }
102 |
103 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### phper技术栈超全整理
2 | - php
3 | - 基础语法
4 | * [运算符](grammar.md#运算符)
5 | * [数组指针](grammar.md#数组指针)
6 | * 文件操作管理
7 | * 图片操作
8 | * 字符串操作
9 | - [面向对象](oop.md)
10 | - 魔术方法
11 | - [对象引用](oop.md#对象引用)
12 | - [访问控制](oop.md#访问控制private)
13 | - [访问控制之继承](oop.md#访问控制之继承)
14 | - [对象遍历](oop.md#对象遍历)
15 | - [设计模式](designPatterns.md)
16 | - [单例模式](designPatterns.md#单例模式)
17 | - [简单工厂模式](designPatterns.md#简单工厂模式)
18 | - [观察者模式](designPatterns.md#观察者模式)
19 | - [建造者模式](designPatterns.md#建造者模式)
20 | - [策略模式](designPatterns.md#策略模式)
21 | - [责任链模式](designPatterns.md#责任链模式)
22 | - [对象映射模式](designPatterns.md#对象映射模式)
23 | - [队列消息模式](designPatterns.md#队列消息模式)
24 | - [迭代器模式](designPatterns.md#迭代器模式)
25 | - [注册树模式](designPatterns.md#注册树模式)
26 | - php高级
27 | * php缓冲区
28 | * [php自动加载](high-php.md#php自动加载)
29 | * [php反射](high-php.md#php反射)
30 | * PHP中间件
31 | * [php接口数据安全解决方案一](high-php.md#php接口数据安全解决方案一)
32 | * [php接口数据安全解决方案二](high-php.md#php接口数据安全解决方案二)
33 | * php7新特性
34 | * [swoole](https://github.com/lisiqiong/swoole-demo)
35 | * [常用算法](arithmetic.md)
36 | + 排序
37 | + [冒泡排序](arithmetic.md#冒泡排序)
38 | + [快速排序](arithmetic.md#快速排序)
39 | + [选择排序](arithmetic.md#选择排序)
40 | + 查找
41 | + [二分法查找](arithmetic.md#二分法查找)
42 | + [递归](arithmetic.md#递归)
43 | + [顺序查找](arithmetic.md#顺序查找)
44 | + 其它
45 | + [乘法口诀](arithmetic.md#乘法口诀)
46 | + [寻最小的n个数](arithmetic.md#寻最小的n个数)
47 | + [寻相同元素](arithmetic.md#寻相同元素)
48 | + [抽奖](arithmetic.md#抽奖)
49 | + [数组反转](arithmetic.md#数组反转)
50 | + [随机打乱数组](arithmetic.md#随机打乱数组)
51 | + [寻找最小元素](arithmetic.md#寻找最小元素)
52 | - http协议
53 | * [http协议说明](http.md#http协议说明)
54 | * 无状态
55 | * http状态码
56 | * telnet实现get请求
57 | * telnet实现post请求
58 | * telnet实现文件上传
59 | - mysql
60 | + mysql查询缓存
61 | + 存储引擎区别
62 | + 索引优化
63 | + mysql分区分表
64 | + 主从复制
65 | + 读写分离
66 | + 双主热备
67 | + 负载均衡
68 | + 数据库备份和维护
69 | - [nginx](nginx.md)
70 | - [nginx信号量](nginx.md#nginx信号量)
71 | - [location](nginx.md#location)
72 | - [rewrite重写](nginx.md#rewrite重写)
73 | - [nginx防盗链](nginx.md#nginx防盗链)
74 | - [nginx之gzip压缩提升网站速度](nginx.md#nginx之gzip压缩提升网站速度)
75 | - [expires缓存提升网站负载](nginx.md#expires缓存提升网站负载)
76 | - [nginx反向代理](nginx.md#nginx反向代理)
77 | - [nginx实现负载均衡](nginx.md#nginx实现负载均衡)
78 | - redis
79 | - 用户积分排行榜
80 | - 事务
81 | - redis数据开发设计
82 | - 主从复制
83 | - 集群分片
84 | - 数据备份策略
85 | - 常见reds错误分析
86 | - 监控redis的服务状态
87 | - 可视化管理工具
88 | - [redis防止商品超发](redis.md#redis防止商品超发)
89 | - redis持久化
90 | - linux
91 | * 实现无密码登录
92 | * shell脚本实现代码发布
93 | * shell脚本实现服务监控
94 | * shell脚本实现日志定时备份
95 | * shell脚本实现数据库备份
96 | - [高并发大流量web解决思路及方案](senior.md)
97 | + [高并发web架构相关概念](senior.md#高并发web架构相关概念)
98 | + [高并发大流量web整体解决思路](senior.md#高并发大流量web整体解决思路)
99 | + [web服务器负载均衡](senior.md#web服务器负载均衡)
100 | + [cdn加速](senior.md#cdn加速)
101 | + [建立独立的图片服务器](senior.md#建立独立的图片服务器)
102 | + [动态页面静态化](senior.md#动态页面静态化)
103 | + [php并发编程实战](senior.md#php并发编程实战)
104 | + [mysql数据层的优化](senior.md#mysql数据层的优化)
105 | + [mysql缓存层的优化](senior.md#mysql缓存层的优化)
106 |
--------------------------------------------------------------------------------
/redis.md:
--------------------------------------------------------------------------------
1 | # redis信息整理
2 | * 事务
3 | * redis数据开发设计
4 | * 主从复制
5 | * 集群分片
6 | * 数据备份策略
7 | * 常见reds错误分析
8 | * 监控redis的服务状态
9 | * 可视化管理工具
10 | * [redis防止商品超发](#redis防止商品超发)
11 | * redis持久化
12 |
13 | ## redis防止商品超发
14 | #### 公用方法 function.php
15 | ```
16 | handler = new Redis();
41 | $this->handler->connect($conf['host'], $conf['port']); //连接Redis
42 | //设置密码
43 | if(isset($conf['auth'])){
44 | $this->handler->auth($conf['auth']); //密码验证
45 | }
46 | //选择数据库
47 | if(isset($conf['db'])){
48 | $this->handler->select($conf['db']);//选择数据库2
49 | }else{
50 | $this->handler->select(0);//默认选择0库
51 | }
52 | }
53 |
54 | //获取key的值
55 | public function get($name){
56 | return $this->handler->get($name);
57 | }
58 |
59 | //设置key的值
60 | public function set($name,$value){
61 | return $this->handler->set($name,$value);
62 | }
63 |
64 | //判断key是否存在
65 | public function exists($key){
66 | if($this->handler->exists($key)){
67 | return true;
68 | }
69 | return false;
70 | }
71 |
72 | //当key不存在的设置key的值,存在则不设置
73 | public function setnx($key,$value){
74 | return $this->handler->setnx($key,$value);
75 | }
76 |
77 | //将key的数值增加指定数值
78 | public function incrby($key,$value){
79 | return $this->handler->incrBy($key,$value);
80 | }
81 |
82 | }
83 | ```
84 |
85 | #### 抢购业务实现 index.php
86 | ```
87 | conf = $conf;
101 | if(empty($type))
102 | return '';
103 | if($type==self::V1){
104 | $this->way1(self::V1);
105 | }elseif($type==self::V2){
106 | $this->way2(self::V2);
107 | }else{
108 | return '';
109 | }
110 | }
111 |
112 | //抢购商品方式一
113 | protected function way1($v){
114 | $redis = new myRedis($this->conf);
115 | $keyNmae = getKeyName($v);
116 | if(!$redis->exists($keyNmae)){
117 | $redis->set($keyNmae,0);
118 | }
119 | $currAmount = $redis->get($keyNmae);
120 | if(($currAmount+self::INCRAMOUNT)>self::AMOUNTLIMIT){
121 | writeLog("没有抢到商品",$v);
122 | return;
123 | }
124 | $redis->incrby($keyNmae,self::INCRAMOUNT);
125 | writeLog("抢到商品",$v);
126 | }
127 |
128 | //抢购商品方式二
129 | protected function way2($v){
130 | $redis = new myRedis($this->conf);
131 | $keyNmae = getKeyName($v);
132 | if(!$redis->exists($keyNmae)){
133 | $redis->setnx($keyNmae,0);
134 | }
135 | if($redis->incrby($keyNmae,self::INCRAMOUNT) > self::AMOUNTLIMIT){
136 | writeLog("没有抢到商品",$v);
137 | return;
138 | }
139 | writeLog("抢到商品",$v);
140 | }
141 |
142 | }
143 |
144 | //实例化调用对应执行方法
145 | $type = isset($_GET['v'])?$_GET['v']:'way1';
146 | $conf = [
147 | 'host'=>'192.168.0.214','port'=>'6379',
148 | 'auth'=>'test','db'=>2,
149 | ];
150 | new sendAward($conf,$type);
151 | /***
152 | 通过ab工具压力测试模拟超发的情况,再结合日志打印的数据说明方法可以有效的防止超发
153 | ab -c 100 -n 200 http://192.168.0.213:8083/index.php?v=way2
154 | ab -c 100 -n 200 http://192.168.0.213:8083/index.php?v=way2
155 | **/
156 | ```
157 |
158 |
--------------------------------------------------------------------------------
/oop.md:
--------------------------------------------------------------------------------
1 | # php面相对象知识整理
2 | * [对象引用](#对象引用)
3 | * [访问控制private](#访问控制private)
4 | * [访问控制之继承](#访问控制之继承)
5 | * [对象遍历](#对象遍历)
6 |
7 | ## 访问控制private
8 | #### 私有属性内部调用
9 | ```
10 | name = $name;
21 | $this->age = $age;
22 | $this->status = $status;
23 | }
24 |
25 | /**
26 | * @desc 获取用户信息
27 | **/
28 | public function getUserInfo() {
29 | return $this->name . "今年" . $this->age . "岁," . '婚姻状态:' . $this->status;
30 | }
31 |
32 | //给私有属性变量赋值
33 | public function __set($key, $value) {
34 | if ($key == 'age') {
35 | $this->$key = $value + 2;
36 | } else {
37 | $this->$key = $value;
38 | }
39 | }
40 |
41 | //获取私有变量
42 | public function __get($key) {
43 | return $this->$key;
44 | }
45 |
46 | }
47 |
48 | $obj = new Person("巴八灵", 28, "已婚");
49 | echo $obj->getUserInfo();
50 | /** 运行结果
51 | 巴八灵今年28岁,婚姻状态:已婚
52 | **/
53 |
54 | ```
55 | #### 私有属性外部调用
56 | ```
57 | name . "今年" . $this->age . "岁," . '婚姻状态:' . $this->status;
71 | }
72 |
73 | //给私有属性变量赋值
74 | public function __set($key, $value) {
75 | if ($key == 'age') {
76 | $this->$key = $value + 2;
77 | } else {
78 | $this->$key = $value;
79 | }
80 | }
81 |
82 | //获取私有变量
83 | public function __get($key) {
84 | return $this->$key;
85 | }
86 |
87 | }
88 |
89 | $obj = new Person();
90 | $obj->name = '巴八灵';
91 | $obj->age = 28;
92 | $obj->status = "已婚";
93 | echo "age设置后的值" . $obj->age . PHP_EOL;
94 | echo $obj->getUserInfo();
95 |
96 | /** 运行后结果
97 | age设置后的值30
98 | 巴八灵今年30岁,婚姻状态:已婚
99 | **/
100 |
101 | ```
102 |
103 | ## 访问控制之继承
104 | #### 继承类属性权限设置
105 | ```
106 | name.',brand:'.$this->brand.',price:'.$this->price;
116 | }
117 |
118 | }
119 |
120 | class Bm extends Car{
121 |
122 | protected $price = "30w";
123 | public $name = '普通车2';
124 | protected $brand = "宝马";
125 |
126 | public function __construct(){
127 | $this->getInfo();
128 | }
129 |
130 | private function getInfo(){
131 | echo "name:".$this->name.',brand:'.$this->brand.',price:'.$this->price;
132 | }
133 |
134 | }
135 |
136 | $bm = new Bm();
137 | ```
138 | #### 结果说明
139 | ```
140 | Fatal error: Access level to Bm::getInfo() must be protected (as in class Car) or weaker in /Users/lidong/www/test/march/27/Car.php on line 29
141 | [Finished in 0.1s]
142 | ```
143 | >总结:子类的属性和方法的访问控制权限应该跟父类保持一致或者更加宽松。
144 |
145 | ## 对象遍历
146 | ```
147 | $value) {
165 | echo $key . '--' . $value . PHP_EOL;
166 | }
167 | /**
168 | 输出结果
169 | val1--value1
170 | val2--value2
171 | val3--value3
172 | **/
173 | ```
174 |
175 | ## 对象引用
176 | #### 对象赋值
177 | ```
178 | brand = '宝马';
194 | $car1->brand = '奥迪';
195 | echo $car1->brand.PHP_EOL;
196 | echo $car2->brand.PHP_EOL;
197 | ```
198 | 结果为:
199 | ```
200 | object(Car)#1 (1) {
201 | ["brand"]=>
202 | NULL
203 | }
204 | object(Car)#1 (1) {
205 | ["brand"]=>
206 | NULL
207 | }
208 | object(Car)#2 (1) {
209 | ["brand"]=>
210 | NULL
211 | }
212 | 奥迪
213 | 奥迪
214 | ```
215 | > 赋值后发现对象都为#1标示,再重新创建一个对象实例变量对象标示变为了#2,同一个标示指向相同的信息修改会影响
216 | #### 赋值引用对象
217 | ```
218 | brand = '奥迪';
228 | echo $car2->brand.PHP_EOL;
229 | echo $car1->brand.PHP_EOL;
230 |
231 | ```
232 | 运行结果为:
233 | ```
234 | object(Car)#1 (1) {
235 | ["brand"]=>
236 | NULL
237 | }
238 | object(Car)#1 (1) {
239 | ["brand"]=>
240 | NULL
241 | }
242 | 奥迪
243 | 奥迪
244 | ```
245 | > 赋值引用对象都指向同一个对象地址,修改赋值相互有影响
--------------------------------------------------------------------------------
/mysql.md:
--------------------------------------------------------------------------------
1 | # mysql知识整理
2 | - 基础知识
3 | - 普通查询语句
4 | - join
5 | - 子查询
6 | - union
7 | - union all
8 | - [触发器](#触发器)
9 | - 存储过程
10 | - 数据库优化
11 | - 应用系统sql优化
12 | - 分表策略
13 | - 分库策略
14 | - 索引
15 | - 普通索引,联合索引
16 | - 如何选择字段创建索引
17 | - 索引失效原因
18 | - 查询缓存
19 | - mysql服务器优化
20 | - 开启慢查询定位问题
21 | - mysql连接数
22 | - 操作系统与硬件优化
23 | - 系统架构整体优化
24 | - 负载均衡
25 | - 缓存
26 | - 分布式优化
27 | - 数据管理与维护
28 | - 数据备份
29 | - mysqldump备份数据库
30 | - mysldump备份数据表
31 | - mysqldump备份数据表结构
32 | - 数据恢复
33 | - mysql日志
34 | - mysql监控
35 | - mysql常用工具
36 |
37 |
38 |
39 | ## 触发器
40 | 触发器的定义:触发器(TRIGGER)是MySQL的数据库对象之一,从5.0.2版本开始支持。该对象与编程语言中的函数非常类似,都需要声明、执行等。但是触发器的执行不是由程序调用,也不是由手工启动,而是由事件来触发、激活从而实现执行。
41 |
42 | #### 触发器的基本语法
43 | ```
44 | CREATE TRIGGER trigger_name
45 | trigger_time
46 | trigger_event ON tbl_name
47 | FOR EACH ROW
48 | begin
49 | ……
50 | end
51 | ```
52 |
53 | 这里触发器的有两种:before,after
54 | 触发的事件类型为:update,insert,delete
55 | #### 创建两张表并插入一些数据演示下触发器的使用
56 | ```
57 | CREATE TABLE goods (
58 | id INT(11) UNSIGNED NOT NULL auto_increment,
59 | name VARCHAR(40) not null COMMENT '商品名称',
60 | stock SMALLINT(11) UNSIGNED NOT NULL COMMENT '商品库存',
61 | PRIMARY KEY(`id`)
62 | )ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT '商品表' ;
63 | ```
64 |
65 | ```
66 | INSERT INTO goods (`name`,`stock`) values ('iphonex',50),('小米2',30),('联想手机',40);
67 | ```
68 |
69 | ```
70 | CREATE TABLE goods_order(
71 | oid int(11) UNSIGNED NOT NULL auto_increment COMMENT '订单id,自增id',
72 | gid INT(11) UNSIGNED NOT NULL COMMENT 'goods表的商品id',
73 | nums SMALLINT(11) UNSIGNED NOT NULL COMMENT '订单购买数量',
74 | PRIMARY KEY(`oid`)
75 | ) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT '商品订单';
76 | ```
77 |
78 | #### 创建触发器当有新订单的时候减少goods表的库存数量
79 | ```
80 | CREATE TRIGGER t1
81 | AFTER
82 | INSERT
83 | ON goods_order
84 | FOR EACH ROW
85 | BEGIN
86 | UPDATE goods SET stock = stock-new.nums WHERE id=new.gid;
87 | END
88 | ```
89 |
90 | #### 运行创建触发器语句后显示触发器
91 | ```
92 | MySQL [test]> show triggers\G;
93 | *************************** 1. row ***************************
94 | Trigger: t1
95 | Event: INSERT
96 | Table: goods_order
97 | Statement: BEGIN
98 | UPDATE goods SET stock = stock-new.nums WHERE id=new.gid;
99 | END
100 | Timing: AFTER
101 | Created: NULL
102 | sql_mode: NO_ENGINE_SUBSTITUTION
103 | Definer: root@%
104 | character_set_client: utf8
105 | collation_connection: utf8_general_ci
106 | Database Collation: utf8_unicode_ci
107 | 1 row in set (0.00 sec)
108 | ```
109 |
110 | #### 查询当前数据库商品表和订单表的数据
111 | ```
112 | MySQL [test]> select * from goods;
113 | +----+--------------+-------+
114 | | id | name | stock |
115 | +----+--------------+-------+
116 | | 1 | iphonex | 50 |
117 | | 2 | 小米2 | 30 |
118 | | 3 | 联想手机 | 40 |
119 | +----+--------------+-------+
120 | 3 rows in set (0.00 sec)
121 |
122 | MySQL [test]> select * from goods_order;
123 | Empty set (0.00 sec)
124 | ```
125 |
126 | #### 向订单表中插入数据查看商品表库存是否自动减掉了
127 | ```
128 | MySQL [test]> insert into goods_order (`gid`,`nums`) values (1,2);
129 | Query OK, 1 row affected (0.00 sec)
130 |
131 | MySQL [test]> select * from goods;
132 | +----+--------------+-------+
133 | | id | name | stock |
134 | +----+--------------+-------+
135 | | 1 | iphonex | 48 |
136 | | 2 | 小米2 | 30 |
137 | | 3 | 联想手机 | 40 |
138 |
139 | MySQL [test]> select * from goods_order;
140 | +-----+-----+------+
141 | | oid | gid | nums |
142 | +-----+-----+------+
143 | | 1 | 1 | 2 |
144 | +-----+-----+------+
145 | ```
146 | 经过对照发现当添加一条商品,购买数量为2时商品表的库存数由50变为了48说明触发器运行成果,和我们所猜想的一致。
147 |
148 | #### 想订单表中插入超过库存总量的数据时测试效果
149 | ```
150 | MySQL [test]> insert into goods_order (`gid`,`nums`) values (2,32);
151 | ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(`test`.`goods`.`stock` - NEW.nums)'
152 |
153 | 插入超过库存数量的商品时sql报错,因为我们设置的字段是无符号的,如果当前字段是有符号此时,字段会更新为-2,这肯定是不符合我们要求的,此时需要修改t1触发器来,当超过库存总数量的时候我们减掉最大库存即可。
154 | ```
155 | #### 修改t1触发器
156 | ```
157 | DROP TRIGGER t1;
158 | CREATE TRIGGER t1
159 | BEFORE
160 | INSERT
161 | ON goods_order
162 | FOR EACH ROW
163 | BEGIN
164 | DECLARE rnum int;
165 | ##查询插入之前商品的库存
166 | SELECT stock into rnum from goods where id=new.gid;
167 | ##如果即购买订单的商品数量大于总库存,则设置为购买的数量为当前的商品库存数量
168 | if new.nums>rnum THEN
169 | SET new.nums = rnum;
170 | END IF;
171 | UPDATE goods SET stock = stock-new.nums WHERE id=new.gid;
172 | END
173 | ```
174 |
175 | #### 插入订单查看效果
176 | ```
177 | MySQL [test]> select * from goods;
178 | +----+--------------+-------+
179 | | id | name | stock |
180 | +----+--------------+-------+
181 | | 1 | iphonex | 48 |
182 | | 2 | 小米2 | 30 |
183 | | 3 | 联想手机 | 40 |
184 | +----+--------------+-------+
185 | 3 rows in set (0.00 sec)
186 |
187 | MySQL [test]> select * from goods_order;
188 | +-----+-----+------+
189 | | oid | gid | nums |
190 | +-----+-----+------+
191 | | 1 | 1 | 2 |
192 | +-----+-----+------+
193 | 1 row in set (0.00 sec)
194 |
195 |
196 | MySQL [test]> insert into goods_order (`gid`,`nums`) values (2,32);
197 | Query OK, 1 row affected (0.00 sec)
198 |
199 | MySQL [test]> select * from goods;
200 | +----+--------------+-------+
201 | | id | name | stock |
202 | +----+--------------+-------+
203 | | 1 | iphonex | 48 |
204 | | 2 | 小米2 | 0 |
205 | | 3 | 联想手机 | 40 |
206 | +----+--------------+-------+
207 | 3 rows in set (0.00 sec)
208 |
209 | MySQL [test]> select * from goods_order;
210 | +-----+-----+------+
211 | | oid | gid | nums |
212 | +-----+-----+------+
213 | | 1 | 1 | 2 |
214 | | 3 | 2 | 30 |
215 | +-----+-----+------+
216 | 2 rows in set (0.00 sec
217 |
218 | ```
219 | 从上门可以看出,当我们订单中购买数量大于商品库存总数的时候,商品库存只会扣除最大库存数
220 |
221 | #### 创建触发t2,实现当删除订单表时恢复商品库存数量
222 | ```
223 | CREATE TRIGGER t2
224 | AFTER
225 | DELETE
226 | ON goods_order
227 | FOR EACH ROW
228 | BEGIN
229 | UPDATE goods SET stock=stock+old.nums where id=old.gid;
230 | END
231 | ```
232 |
233 | 查询当前所有触发器
234 | ```
235 | MySQL [test]> show triggers\G;
236 | *************************** 1. row ***************************
237 | Trigger: t1
238 | Event: INSERT
239 | Table: goods_order
240 | Statement: BEGIN
241 | DECLARE rnum int;
242 | ##查询插入之前商品的库存
243 | SELECT stock into rnum from goods where id=new.gid;
244 | ##如果即购买订单的商品数量大于总库存,则设置为购买的数量为当前的商品库存数量
245 | if new.nums>rnum THEN
246 | SET new.nums = rnum;
247 | END IF;
248 | UPDATE goods SET stock = stock-new.nums WHERE id=new.gid;
249 | END
250 | Timing: BEFORE
251 | Created: NULL
252 | sql_mode: NO_ENGINE_SUBSTITUTION
253 | Definer: root@%
254 | character_set_client: utf8
255 | collation_connection: utf8_general_ci
256 | Database Collation: utf8_unicode_ci
257 | *************************** 2. row ***************************
258 | Trigger: t2
259 | Event: DELETE
260 | Table: goods_order
261 | Statement: BEGIN
262 | UPDATE goods SET stock=stock+old.nums where id=old.gid;
263 | END
264 | Timing: AFTER
265 | Created: NULL
266 | sql_mode: NO_ENGINE_SUBSTITUTION
267 | Definer: root@%
268 | character_set_client: utf8
269 | collation_connection: utf8_general_ci
270 | Database Collation: utf8_unicode_ci
271 | 2 rows in set (0.00 sec)
272 | ```
273 |
274 | 删除订单表id=1的数据
275 | ```
276 | MySQL [test]> delete from goods_order where id=1;
277 | ERROR 1054 (42S22): Unknown column 'id' in 'where clause'
278 | MySQL [test]> delete from goods_order where oid=1;
279 | Query OK, 1 row affected (0.00 sec)
280 |
281 | MySQL [test]> select * from goods;
282 | +----+--------------+-------+
283 | | id | name | stock |
284 | +----+--------------+-------+
285 | | 1 | iphonex | 50 |
286 | | 2 | 小米2 | 0 |
287 | | 3 | 联想手机 | 40 |
288 | +----+--------------+-------+
289 | 3 rows in set (0.00 sec)
290 |
291 | MySQL [test]> select * from goods_order;
292 | +-----+-----+------+
293 | | oid | gid | nums |
294 | +-----+-----+------+
295 | | 3 | 2 | 30 |
296 | +-----+-----+------+
297 | 1 row in set (0.00 sec)
298 | ```
299 | 由上门可以看出,删除id=1的数据后,订单表id=1的商品库存数由之前48增加到了50说明触发器成功执行
300 |
301 | #### 最后解释下FOR EACH ROW
302 | for each row表示的是执行的触发起的动作影响了多少行数据就执行多少行的数据
303 |
--------------------------------------------------------------------------------
/senior.md:
--------------------------------------------------------------------------------
1 | # 高并发大流量web解决思路及方案
2 | + [高并发web架构相关概念](#高并发web架构相关概念)
3 | + [高并发大流量web整体解决思路](#高并发大流量web整体解决思路)
4 | + [web服务器负载均衡](#web服务器负载均衡)
5 | + [cdn加速](#cdn加速)
6 | + [建立独立的图片服务器](#建立独立的图片服务器)
7 | + [动态页面静态化](#动态页面静态化)
8 | + [php并发编程实战](#php并发编程实战)
9 | + [mysql数据层的优化](#mysql数据层的优化)
10 | + [mysql缓存层的优化](#mysql缓存层的优化)
11 |
12 | ## 高并发web架构相关概念
13 | - QPS:每秒钟请求或查询的数量,在互联网领域,指每秒相应请求数(http请求)。
14 | - 峰值每秒的QPS:(总PV数*80%)/(6小时秒数*20%),80%的访问量集中在20%的时间
15 | - 并发连接数:系统同时处理的请求数量
16 | - 吞吐量:单位时间内处理的请求数量(通常由QPS与并发数决定)。
17 | - 响应时间:从请求发出到收到响应花费的时间。例如系统处理一个HTTP请求需要100ms,这个100ms就是系统的响应时间。
18 | - PV:综合浏览量(Page View)即页面浏览量和点击量,一个访客在24小时内访问的页面数量。
19 | - UV:独立访客(UniQue Visitor),即一定时间范围内相同访客多次访问网站,只计算为1个独立访客。
20 | - 带宽:计算带宽大小关注两个指标,峰值流量和平均页面大小。
21 | - 日网站带宽=PV/统计时间(一天换算到秒)*平均页面大小(单位KB)*8。
22 | - 压力测试:测试承受的最大并发,测试最大承受的QPS,需要注意的测试并发测试机需要与被测试机器分开,不要对线上服务器进行并发测试,观察ab测试的所在机器,以及被测试机器的前端机的CPU,内存,网络等都不超过最高限度的75%。
23 | - 并发量
24 | - 响应速度
25 | - 容错能力
26 | - 常用的性能测试工具:ab,wrk,http_load,Web Bench,Siege,Apache JMeter。
27 |
28 | ## 高并发大流量web整体解决思路
29 | - 流量优化
30 | - web资源防盗链防止第三方系统盗用图片,css,js等占用服务器流量和服务器带宽
31 | - 前端优化
32 | - 减少http请求:图片合并,js合并,css合并压缩,虽然文件可能大点但请求会减少
33 | - 添加异步请求:通过实际ajax调用接口获取数据
34 | - 启动浏览器的缓存和文件压缩(也可以启用nginx的压缩模块)
35 | - cdn加速:解决带宽不够用的问题,数据缓存到cdn的节点,访问的时候选择就近的节点,减少带宽加快访问速度
36 | - 建立独立的图片服务器:图片是很吃io的,可以将图片服务器与web服务器完全分离开,可以区分其它服务器单独搭建图片服务器不属于计算型的配置可以适当的调整,图片服务器还可以集群
37 | - 服务端的优化
38 | - 页面的静态化:动态的页面静态html,减少服务器的负载压力,页面静态化穿透,静态化有有效时间
39 | - 动态语言并发处理:异步处理,多线程,队列的异步处理
40 | - 数据库的优化:
41 | - 数据库的缓存:memcache,redis的缓存
42 | - mysql索引优化,mysql分库分表,mysql分区操作,mysql主从复制读写分离,mysql的负载均衡,mysql的主从热备
43 | - web服务器的优化:
44 | - 负载均衡:可以使用ningx的反向代理使用负载均衡,可以使用网络分层中的第四层lvs实现负载均衡
45 |
46 | ### web服务器负载均衡
47 | #### 负债均衡
48 | - 四层负载均衡:所谓四层负载均衡就是基于IP+端口的负载均衡
49 | - 七层负载均衡:所谓七层的负载均衡就是基于(URL)信息的负载均衡
50 |
51 | #### 七层负载均衡实现:
52 | 基于URL等应用层信息的负债均衡
53 | ningx的proxy是它一个很强大的功能,实现了7层负载均衡,功能强大,性能卓越,运行稳定,配置简单灵活,能够自动剔除工作不正常的后端服务器,上传文件可以使用异步模式上传,支持多种分配策略,可以分配权重,分配方式灵活。
54 |
55 | #### nginx负载均衡策略
56 | - IP Hash(内置)
57 | - 加权轮询(内置)
58 | - fair策略(扩展)
59 | - 通用hash(扩展)
60 | - 一致性hash(扩展)
61 |
62 | ##### IP Hash策略
63 | nginx内置的另一个负载均衡的策略,流程和轮询很相似,只是其中的算法和具体的策略有些变化,IP hash算法是一种变相的轮询算法
64 |
65 | ##### 加权轮训策略
66 | 首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器,当所有后端机器都down掉时,nginx会立即将所有机器的标志位清成初始状态,以避免造成所有的机器都处在timeout的状态
67 |
68 | ##### fair策略
69 | 根据后端服务器的响应时间判断负载情况,从中选出负载最轻的机器进行分流
70 |
71 | 通用hash、一致性hash策略,通用hash比较简单,可以以nginx内置的变量为key进行hash,一致性hash采用了内置的一致性hash环,支持memcache
72 |
73 | #### 四层负载均衡实现
74 | 通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器
75 |
76 | lvs相关术语:
77 |
78 | - DS:director server 目标服务器,即负载均衡器
79 | - RS:Real Server 真实服务器,即后端服务器
80 | - VIP:直接面向用户的IP地址,通常为公网IP地址
81 | - DIP:Director Server Ip主要用于内部主机通信的IP地址
82 | - RIP:Real Server IP 后端真实服务器的IP地址
83 | - CIP:Client IP
84 |
85 | lvs负载均衡三种方式:
86 |
87 | - NAT:修改目标IP地址为后端的RealServer的IP地址
88 | - DR:修改目标mac地址为后端的RealServer的mac地址
89 | - TUNNEL:较少使用,常用于异地容灾
90 |
91 |
92 | #### 四七层负载均衡优缺点
93 | - 四层比七层可以承载更大的并发量,使用大型站点小
94 | - 七层可以实现更为复杂的负载均衡控制,比如URL、基于session、动静分离等
95 | - 七层能够占用大量的CPU时间,承载的并发量
96 |
97 | ## cdn加速
98 | #### 什么是cdn?
99 |
100 | 节点:可以理解为真实服务器的镜像。
101 |
102 | 全称是Content Delivery Network,即内容分发网络尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。
103 |
104 | 在网络各处放置节点服务器所构成的现有的互联网基础之上的一层智能虚拟网络。
105 |
106 | cdn系统能够实时地根据网络流量和各节点的连接,负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。
107 |
108 | #### cdn的优势是什么?
109 | - 1.本地的cache加速,提高企业站点(尤其含有大量图片和静态页面站点)的访问速度
110 | - 2.跨运营商的网络加速,保证不同网络的用户得到良好的访问质量
111 | - 3.远程访问用户根据DNS负载均衡技术智能自动选择Cache服务器
112 | - 4.自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽,分担网络流量,减轻愿站点web服务器负载等功能。
113 | - 5.广泛分布的cdn节点加上节点之间的智能冗余机制,可以有效的预防黑客入侵
114 |
115 | #### cdn的工作原理是什么?
116 |
117 | 传统的访问:用户在浏览器输入域名发起请求,解析域名获取服务器ip地址,根据ip地址找到对应的服务器,服务器响应并返回数据。
118 |
119 | 使用cdn访问:用户发起请求,智能dns的解析(根据ip判断地理位置,接入网类型,选择路由最短和负载最轻的服务器),取得缓存服务器ip,把内容返回给用户(如果缓存中有),向源站发起请求,将结果访问给用户,将结果存入缓存服务器。
120 |
121 | #### cdn的适用场景?
122 | 站点或者应用中大量静态资源的加速分发,例如:css,js,图片和html
123 |
124 | #### cdn的实现方式?
125 | - BAT等实现的CDN服务
126 | - 使用LVS的4层负载均衡
127 | - 可用nginx,varnish,squid,apache trafficServer做七层负载均衡和cache
128 | 使用squid做反向代理或者nginx做反向代理
129 |
130 | ## 建立独立的图片服务器
131 | 独立的必要性?
132 |
133 | - 1.分担web服务器的I/O负载,将耗费资源的图片服务分离出来,提高服务器的性能和稳定性
134 | - 2.能够专门对图片服务器进行优化,为图片服务设置针对性的缓存方案,减少带宽成本,提高访问速度
135 |
136 | 为啥采用独立的域名?
137 |
138 | 原因:同一域名下浏览器的并发连接数是有限制的,突破浏览器连接的限制,由于cookie的原因,对缓存不利,大部分web cache都只缓存不带cookie的请求,导致每次的图片请求都不能够命中cache
139 |
140 | 独立后的问题?
141 |
142 | - 如何进行图片上传和图片同步
143 | - NPS共享方式
144 | - 利用FTP同步
145 |
146 | ## 动态页面静态化
147 | 相关概念:什么是动态语言静态化,为什么要静态化,静态化的实现方式。
148 |
149 | ## 动态语言的并发处理
150 | ### 什么是进程
151 | 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础
152 |
153 | 进程是一个“执行中的程序”
154 |
155 | #### 进程的状态的三态模型
156 | 多道程序系统中,进程在处理器上交替运行,状态不断发生变化。
157 |
158 | - 运行:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态的进程的数目小于等于处理器的数目,对于单处理机系统,处于运行状态的进程只有一个。在没有其它进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
159 | - 就绪:当一个进程获得了除处理机以外的一切所有资源,一旦得到处理机即可运行,则称此进程处于就绪状态。就绪状态可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
160 | - 阻塞:也称为等待或睡眠状态,一个进程正在等待某一事件发生(例如请求I/O而等待I/O完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。
161 |
162 | ### 什么是线程
163 | 由于用户的并发请求,为每一个请求都创建一个进程显然是行不通的,从系统资源开销方面或是响应用户请求的效率方面来看。因此操作系统中线程的概念便被引进了。
164 |
165 | 线程有时候被称为轻量级进程,是程序执行流的最小单元。
166 |
167 | 线程是进程中的一个实体,是被系统独立调度和分配的基本单位,线程自己不拥有系统资源,只拥有一点儿运行中必不可少的资源但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
168 |
169 | 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
170 |
171 | 线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派cpu的基本单位指运行中的程序的调度单位。
172 |
173 | #### 线程三状态
174 | - 就绪状态:线程具备运行的所有条件,逻辑上可以运行,在等待处理机。
175 | - 运行状态:线程占有处理机正在运行。
176 | - 阻塞状态:线程在等待一个事件(如某个信号量),逻辑上不可执行。
177 |
178 | ### 什么是协程
179 | 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协称调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切的开销,可以不要加锁的访问全局变量,所以上下文的切换非常快。
180 |
181 | ### 线程和进程的区别?
182 | - 1.线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间。
183 | - 2.进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源。
184 | - 3.线程是处理器调度的基本单位,但进程不是
185 | - 4.二者都可以并发的执行
186 | - 5.每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
187 |
188 | ### 线程和协程的区别?
189 | - 1.一个线程可以多个协程,一个进程也可以单独拥有多个协程
190 | - 2.线程进程都是同步机制,而协程则是异步
191 | - 3.协称能够保留上一次调用时的状态,每次过程重入的时,就相当于进入上一次调用的状态
192 |
193 | ### 什么是多进程?
194 | 同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这就是多进程
195 | 多开一个进程,多分配一份资源,进程间通讯不方便
196 |
197 | ### 什么是多线程?
198 | 线程就是把一个进程分为很多片,每一片都可以是一个独立的流程,与多进程的区别是只会使用一个进程的资源,线程间可以通讯
199 |
200 | ### 多个概念之间的区别?
201 | - 单进程单线程:一个人在一个桌上吃菜
202 | - 单进程多线程:多个人在一个桌子上吃菜
203 | - 多进程单线程:多个人每个人在自己桌子上吃菜
204 |
205 | ### 同步阻塞模型
206 | 多进程:最早的服务器端程序都是通过多进程,多线程来解决并发IO的问题一个请求创建一个进程,然后子进程进入循环同步堵塞地与客户端连接进行交互,收发处理数据。
207 |
208 | #### 步骤
209 | - 创建一个socket
210 | - 进入while循环,阻塞在进程accept操作上,等待客户端连接进入主进程在多进程模型下通过fork创建子进程。
211 |
212 | 多线程模式下可以创建子线程
213 |
214 | 子线程/线程创建成功后进入while循环,阻塞在recv调用上,等待客户端向服务器发送数据
215 |
216 | 收到数据以后服务器程序进行处理然后使用send向客户端发送响应
217 |
218 | 当客户端连接关闭时,子进程/线程退出并销毁所有资源。主进程/线程会回收掉此子进程/线程。
219 |
220 | 这中模型严重的依赖进程的数量解决并发问题。
221 |
222 | 启动大量的进程会带来额外的进程调度消耗
223 |
224 | ### 异步非阻塞模型
225 | 现在各种高并发异步IO的服务器程序都是基于epoll实现的
226 |
227 | IO复用异步非阻塞程序使用经典的Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket句柄的事件变化。
228 |
229 | #### Reactor模型:
230 | - add:添加一个socket到reactor
231 | - set:修改socket对应的事件,如可读可写
232 | - del:从reactor中移除
233 | - callback:事件发生后回掉指定的函数
234 |
235 | nginx:多线程Reactor
236 |
237 | swoole:多线程Reactor+多进程worker
238 |
239 | ## php并发编程实战
240 | - 1.php的swoole扩展、并行、高性能网络通信引擎,使用纯c语言编写提供了php语言的异步多线程服务器,异步tcp/udp网络客户端,异步mysql,异步redis,数据库连接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步dns查询。
241 | - 2.除了异步IO的支持之外,swoole为php多进程的模式设计了多个并发数据结构和IPC通信机制,可以大大简化多线程并发编程的工作
242 | - 3.swoole2.0支持了类似Go语言的协程,可以使用完全同步的代码实现异步程序
243 | - 4.消息队列
244 | - 5.应用解耦
245 | - 场景说明:用户下单后,订单系统需要通知库存系统。
246 | - 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败
247 | - 订单系统跟库存系统解耦
248 | - 引用队列
249 | - 用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功
250 | - 订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作
251 | - 6.流量削峰
252 | 应用场景:秒杀活动,流量瞬间激增,服务器压力大
253 | 用户发起请求后,服务器接收后,先写入消息队列。假如消息队列长度超多最大值,则直接报错或提示用户
254 | 控制请求量,缓解高流量
255 | - 7.日志处理
256 | 应用场景:解决大量日志的传输
257 | 日志采集程序将程序写入消息队列,然后通过日志处理程序的订阅消费日志。
258 | - 8.消息通讯
259 | 聊天室
260 | - 9.常见消息队列产品
261 | kafka,ActiveMQ,ZeroMQ,RabbitMQ,Redis等
262 | php的异步
263 | 消息队列
264 | - 10.接口的并发请求
265 | curl_multi_init
266 |
267 | ## mysql缓存层的优化
268 | 1.什么是数据库缓存
269 |
270 | mysql等一些常见的关系型数据库的数据都存储在磁盘当中,在高并发场景下,业务应用对mysql产生的增删,改,查的操作造成巨大的I/O开销和查询压力,这无疑对数据库和服务器都是一种巨大的压力,为了解决此类问题,缓存数据的概念应运而生。
271 | - 极大的解决数据库服务器的压力
272 | - 提高应用数据的响应速度
273 |
274 | 常见的缓存形式:内存缓存和文件缓存
275 |
276 | 2.为什么要使用数据库缓存
277 |
278 | - 缓存数据是为了让客户端很少甚至不访问数据库服务器进行数据的查询,高并发下,能最大程序地降低对数据库服务器的访问压力。
279 | - 用户请求-》数据查询-》连接数据库服务器并查询数据-》将数据缓存起来(html,内存,json,序列化数据)-》显示给客户端
280 | - 缓存方式的选择
281 | - 缓存场景的选择
282 | - 缓存数据的实时性
283 | - 缓存数据的稳定性
284 |
285 | 3.使用mysql查询缓存
286 |
287 | - 启用mysql查询缓存
288 | - 极大的降低cpu使用率
289 | - query_cache_type查询缓存类型,有0,1,2三个取值。0则不适用查询缓存。1表示始终使用查询缓存,2表示按需使用查询缓存。
290 |
291 | query_cahce_type=1
292 | select SQL_NO_CACHE * from my_table where condition;
293 | query_cache_type=2
294 | select SQL_CACHE * from my_table where condition;
295 | query_cache_size
296 |
297 | 默认情况下query_cache_size为0,表示为查询缓存预留的内存为0,则无法使用查询缓存
298 | SET GLOBAL query_cache_size = 134217728;
299 | 查询缓存可以看作是SQL文本和查询结果的映射
300 | 第二次查询的SQL和第一次查询的SQL完全相同,则会使用缓
301 | SHOW STATUS LIKE ‘Qcache_hits’查看命中次数
302 | 表的结构和数据发生改变时,查询缓存中的数据不再有效
303 |
304 | 情理缓存:
305 |
306 | - FLUSH QUERY CACHE;//清理查询缓存内存碎片
307 | - RESET QUERY CACHE;//从查询缓存中移出所有查询
308 | - FLUSH TABLES;//关闭所有打开的表,同时该操作将会清空查询缓存中的内容
309 |
310 | 4.使用Memcache缓存
311 |
312 | 对于大型站点,如果没有中间缓存层,当流量打入数据库层时,即便有之前的几层为我们挡住一部分流量,但是在大并发的情况下,还是会有大量请求涌入数据库层,这样对于数据库服务器的压力冲击很大,响应速度也会下降,因此添加中间缓存层很有必要。
313 |
314 | memcache是一套分布式的高速缓存系统,由liveJournal的BrandFitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的、需要频繁访问数据库的网站访问速度提升效果十分显著。
315 | memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像,视频、文件以及数据库检索的结果等。简单的说就是将数据调用到内存,然后从内存中读取,从而大大提高读取速度。
316 |
317 | 工作流程:先检查客户端的请求数据是否在memcache中,如有,直接把请求数据返回,不再对数据库进行任何操作;如果请求的数据不在memcache中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到memcached中。
318 |
319 | 通用缓存机制:用查询的方法名+参数作为查询时的key,value对中的key值
320 |
321 | 5.使用Redis缓存
322 |
323 | 与memcache的区别:
324 |
325 | - 性能相差不大
326 | - redis在2.0版本后增加了自己的VM特性,突破物理内存的限制,memcache可以修改最大可用内存,采用LRU算法
327 | - redis依赖客户端来实现分布式读写
328 | - memcache本身没有数据冗余机制
329 | - redis支持(快照,aof)依赖快照进行持久化aof增强了可靠性的同时,对性能有所影响
330 | - redis用户数据量较小的高性能操作和运算上
331 | - memcache用于在动态系统中减少数据库负载,提升性能;适合做缓存提高性能。
332 | - 可用于存储其他数据:session,session_set_save_handler
333 |
334 | ## mysql数据层的优化
335 |
336 | - 数据表数据类型优化:int,smallint.,bigint,enum,ip存储使用int类型ip2long转化存入
337 | - 索引不是越多越好,在合适的字段上创建合适的索引
338 | - 符合索引的前缀原则
339 | - like查询%的问题
340 | - 全表扫描优化
341 | - or条件索引使用情况
342 | - 字符串类型索引失效的问题
343 | - 优化查询数据过程中的数据访问,使用limit,尽量不要使用*,变复杂为简单,切分查询,分解关联查询*
344 | - 优化特定类型查询语句,优化count(),优化关联查询语句,优化子查询,优化group by和distinct,优化limit和union
345 | - 存储引擎的优化:尽量使用innodb
346 | - 数据库表结构的优化:分区操作(对用户透明)partion,分库分表(水平拆分,垂直拆分做副表)
347 | - 数据库服务器架构的优化:主从复制,读写分离,双主热备,负载均衡(lvs实现负载均衡,MyCat数据库中间件实现负载均衡)
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
--------------------------------------------------------------------------------
/arithmetic.md:
--------------------------------------------------------------------------------
1 | # php实现几种常见算法
2 | * 排序
3 | * [冒泡排序](#冒泡排序)
4 | * [快速排序](#快速排序)
5 | * [选择排序](#选择排序)
6 | * 查找
7 | * [二分法查找](#二分法查找)
8 | * [递归](#递归)
9 | * [顺序查找](#顺序查找)
10 | * 其它
11 | * [乘法口诀](#乘法口诀)
12 | * [寻最小的n个数](#寻最小的n个数)
13 | * [寻相同元素](#寻相同元素)
14 | * [抽奖](#抽奖)
15 | * [数组反转](#数组反转)
16 | * [随机打乱数组](#随机打乱数组)
17 | * [寻找最小元素](#寻找最小元素)
18 |
19 | ### 公共方法
20 | ```
21 | //创建数据
22 | function createData($num) {
23 | $arr = [];
24 | for ($i = 0; $i < $num; $i++) {
25 | $arr[$i] = rand($i, 1000);
26 | }
27 | return $arr;
28 | }
29 |
30 | //打印输出数组
31 | function printSortArr($fun, $num = 10) {
32 | $data = createData($num);
33 | $dataString = implode(',', $data);
34 | echo "原数据:{$dataString}" . PHP_EOL;
35 | $arr = $fun($data);
36 | echo "算法[{$fun}]数据打印" . PHP_EOL;
37 | foreach ($arr as $key => $value) {
38 | # code...
39 | echo $value . PHP_EOL;
40 | }
41 | }
42 |
43 | ```
44 |
45 | ### 冒泡排序
46 | ```
47 | $arr[$j]) {
62 | $temp = $arr[$j - 1];
63 | $arr[$j - 1] = $arr[$j];
64 | $arr[$j] = $temp;
65 | }
66 | }
67 | }
68 | return $arr;
69 | }
70 |
71 |
72 |
73 | printSortArr('mp_sort', 10);
74 | ```
75 |
76 | ##### 输出结果
77 | ```
78 | 原数据:504,480,612,677,613,395,506,129,479,605
79 | 算法[mp_sort]数据打印
80 | 129
81 | 395
82 | 479
83 | 480
84 | 504
85 | 506
86 | 605
87 | 612
88 | 613
89 | 677
90 | ```
91 |
92 | ### 快速排序
93 |
94 | ```
95 | 1){
104 | //定义一基准数,再定义一个小于基准数的数组,和一个大于基准数的数组,然后再递归进行快速排序
105 | $k = $arr[0];//定基准数
106 | $x = [];//小于基准数的数组
107 | $y = [];//大于基准数的数组
108 | $size = count($arr);
109 | for($i=1;$i<$size;$i++){
110 | if($arr[$i]>$k){
111 | $y[] = $arr[$i];
112 | }elseif($arr[$i]<=$k){
113 | $x[] = $arr[$i];
114 | }
115 | }
116 | $x = quickSort($x);
117 | $y = quickSort($y);
118 | return array_merge($x,array($k),$y);
119 | }else{
120 | return $arr;
121 | }
122 | }
123 |
124 | $arr = [2,78,3,23,532,13,67];
125 | print_r(quickSort($arr));
126 |
127 | ```
128 |
129 | ### 选择排序
130 | ```
131 | $arr[$j]) {
144 | $min = $j;
145 | }
146 | }
147 | if ($min != $i) {
148 | $temp = $arr[$min];
149 | $arr[$min] = $arr[$i];
150 | $arr[$i] = $temp;
151 | }
152 | }
153 | return $arr;
154 | }
155 |
156 | printSortArr('xz_sort', 10);
157 |
158 | ```
159 | #### 运行结果
160 | ```
161 | 原数据:845,435,918,889,232,62,162,617,729,540
162 | 算法[xz_sort]数据打印
163 | 62
164 | 162
165 | 232
166 | 435
167 | 540
168 | 617
169 | 729
170 | 845
171 | 889
172 | 918
173 | ```
174 |
175 | ### 二分法查找
176 |
177 | ```
178 | 1,'name'=>'湖北省','pid'=>0,'son'=>''],
235 | ['id'=>2,'name'=>'广东省','pid'=>0,'son'=>''],
236 | ['id'=>3,'name'=>'湖南省','pid'=>0,'son'=>''],
237 | ['id'=>4,'name'=>'武汉市','pid'=>1,'son'=>''],
238 | ['id'=>5,'name'=>'荆州市','pid'=>1,'son'=>''],
239 | ['id'=>6,'name'=>'宜昌市','pid'=>1,'son'=>''],
240 | ['id'=>7,'name'=>'咸宁市','pid'=>1,'son'=>''],
241 | ['id'=>8,'name'=>'仙桃市','pid'=>1,'son'=>''],
242 | ['id'=>9,'name'=>'潜江市','pid'=>1,'son'=>''],
243 | ['id'=>10,'name'=>'深圳市','pid'=>2,'son'=>''],
244 | ['id'=>11,'name'=>'广州市','pid'=>2,'son'=>''],
245 | ['id'=>12,'name'=>'珠海','pid'=>2,'son'=>''],
246 | ['id'=>13,'name'=>'佛山市','pid'=>2,'son'=>''],
247 | ['id'=>14,'name'=>'长沙市','pid'=>3,'son'=>''],
248 | ['id'=>15,'name'=>'岳阳市','pid'=>3,'son'=>''],
249 | ['id'=>16,'name'=>'株洲市','pid'=>3,'son'=>''],
250 | ['id'=>17,'name'=>'衡阳市','pid'=>3,'son'=>''],
251 | ];
252 |
253 | function recursive($arr,$pid=0){
254 | $tree = [];
255 | foreach($arr as $k=>$v){
256 | if($v['pid'] == $pid){
257 | $v['son'] = recursive($arr,$v['id']);
258 | $tree[] = $v;
259 | }
260 |
261 | }
262 | return $tree;
263 | }
264 | print_r(recursive($areaList));
265 | ```
266 | ### 顺序查找
267 | ```
268 | $v){
279 | if($v==$findNum){
280 | echo "查到了,键值为$k";
281 | $flag = true;
282 | break;
283 | }
284 | }
285 | if($flag==false){
286 | echo "未找到该数";
287 | }
288 |
289 | ```
290 | ### 乘法口诀
291 | ```
292 | '一','2'=>'二','3'=>'三','4'=>'四','5'=>'五','6'=>'六','7'=>'七','8'=>'八','9'=>'九',
307 | '10'=>'十','12'=>'十二','14'=>'十四','15'=>'十五','16'=>'十六','18'=>'十八','20'=>'二十','21'=>'二十一','24'=>'二十四','25'=>'二十五','27'=>'二十七','28'=>'二十八','30'=>'三十',
308 | '31'=>'三十一', '32'=>'三十二','35'=>'三十五','36'=>'三十六','40'=>'四十','42'=>'四十二','45'=>'四十五','48'=>'四十八','49'=>'四十九','52'=>'五十二','54'=>'五十四','56'=>'五十六',
309 | '63'=>'六十三','64'=>'六十四','72'=>'七十二','81'=>'八十一'
310 | ];
311 | for($i=1;$i<=9;$i++){
312 | for($j=1;$j<=$i;$j++){
313 | $num = $j*$i;
314 | if($j==$i){
315 | echo $word[$j].$word[$i].'得'.$word[$num]."\n";
316 | }else{
317 | echo $word[$j].$word[$i].'得'.$word[$num].' ';
318 | }
319 | unset($num);
320 | }
321 | }
322 | ```
323 | ### 寻最小的n个数
324 | ```
325 | $arr[$pos]) {
360 | //echo '$i:'.$i.'--$post:'.$pos.PHP_EOL;
361 | $pos = $i;
362 | }
363 | }
364 | return $pos;
365 | }
366 | $array = [1, 100, 20, 22, 33, 44, 55, 66, 23, 79, 18, 20, 11, 9, 129, 399, 145,88,56,84,12,17];
367 | //$min_array = get_min_array($array, 10);
368 | //print_r($min_array);
369 | $arr = [130,2,4,100,89,8,99];
370 | $num = get_max_pos($arr);
371 | echo $num;
372 | print_r($arr[$num]);
373 |
374 | ```
375 | ### 寻相同元素
376 | ```
377 | $arr2[$j]){
393 | $j++;
394 | }else{
395 | $sameArr[] = $arr1[$i];
396 | $i++;
397 | $j++;
398 | }
399 | }
400 | if(!empty($sameArr)){
401 | $sameArr = array_unique($sameArr);
402 | }
403 | return $sameArr;
404 | }
405 | $result = findCommon($arr1,$arr2);
406 | print_r($result);
407 | ```
408 | ### 抽奖
409 | ```
410 | 1,'name'=>'特等奖','v'=>1],
413 | ['id'=>2,'name'=>'二等奖','v'=>3],
414 | ['id'=>3,'name'=>'三等奖','v'=>5],
415 | ['id'=>4,'name'=>'四等奖','v'=>20],
416 | ['id'=>5,'name'=>'谢谢参与','v'=>71],
417 | ];
418 | function draw($arr)
419 | {
420 | $result = [];
421 | //计算总抽奖池的积分数
422 | $sum = [];
423 | foreach($arr as $key=>$value){
424 | $sum[$key] = $value['v'];
425 | }
426 | $randSum = array_sum($sum);
427 | $randNum = mt_rand(1,$randSum);
428 | error_log('随机数数:'.$randNum);
429 | $count = count($arr);
430 | $s = 0;
431 | $e = 0;
432 | for($i=0;$i<$count;$i++){
433 | if($i==0){
434 | $s = 0;
435 | }else{
436 | $s += $sum[$i-1];
437 | }
438 | $e += $sum[$i];
439 | if($randNum>=$s && $randNum<=$e){
440 | $result = $arr[$i];
441 | }
442 | }
443 | unset($sum);
444 | return $result;
445 | }
446 | print_r(draw($arr));
447 | ```
448 | ### 数组反转
449 | ```
450 | $arr[$i]){
548 | $min = $i;
549 | }
550 | }
551 | echo "数组中最小值的索引为:".$min.',最小值为:'.$arr[$min];
552 | ```
553 |
554 |
555 |
--------------------------------------------------------------------------------
/nginx.md:
--------------------------------------------------------------------------------
1 | # nginx相关配置说明
2 | - [nginx信号量](#nginx信号量)
3 | - [location](#location)
4 | - [rewrite重写](#rewrite重写)
5 | - [nginx防盗链](#nginx防盗链)
6 | - [nginx之gzip压缩提升网站速度](#nginx之gzip压缩提升网站速度)
7 | - [expires缓存提升网站负载](#expires缓存提升网站负载)
8 | - [nginx反向代理](#nginx反向代理)
9 | - [nginx实现负载均衡](#nginx实现负载均衡)
10 |
11 | ## nginx信号量
12 | 信号说明
13 |
14 | | 信号名称 | 作用 |
15 | | ------ | ------ |
16 | | TERM,INT | 快速关闭 |
17 | | QUIT | 从容关闭 |
18 | | HUP | 重新加载配置,用新的配置开始新的工作进程,从容关闭旧的工作进程 |
19 | | USR1 | 重新打开日志文件 |
20 | | USR2 | 平滑升级可执行程序 |
21 | | WINCH | 从容关闭工作进程 |
22 |
23 | #### hup信号优雅重启
24 | a.html
25 | ```
26 |
27 |
275 |
276 |
277 | ```
278 |
279 | #### 正常配置nginx不做防盗链处理
280 | ```
281 | server {
282 | listen 80;
283 | server_name dev.api.dd.com;
284 | root /Users/lidong/Desktop/wwwroot/dd_api/public;
285 | index index.php index.html index.htm;
286 | access_log /Users/lidong/wwwlogs/dev.api.dd.com_access.log;
287 | error_log /Users/lidong/wwwlogs/dev.api.dd.com_error.log;
288 | location ~ [^/]\.php(/|$) {
289 | fastcgi_pass 127.0.0.1:9000;
290 | fastcgi_index index.php;
291 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
292 | include fastcgi_params;
293 | }
294 |
295 | location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
296 | }
297 |
298 | try_files $uri $uri/ @rewrite;
299 | location @rewrite {
300 | rewrite ^/(.*)$ /index.php?_url=/$1;
301 | }
302 |
303 | }`
304 | ```
305 | #### 运行http://localhost/index.html结果
306 | 
307 |
308 | #### 配置限定的资源文件如果被第三方调用直接返回403
309 | ```
310 | server {
311 | listen 80;
312 | server_name dev.api.dd.com;
313 | root /Users/lidong/Desktop/wwwroot/dd_api/public;
314 | index index.php index.html index.htm;
315 | access_log /Users/lidong/wwwlogs/dev.api.dd.com_access.log;
316 | error_log /Users/lidong/wwwlogs/dev.api.dd.com_error.log;
317 | location ~ [^/]\.php(/|$) {
318 | fastcgi_pass 127.0.0.1:9000;
319 | fastcgi_index index.php;
320 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
321 | include fastcgi_params;
322 | }
323 |
324 | location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
325 | valid_referers none blocked dev.api.dd.com;
326 | if ($invalid_referer)
327 | {
328 | return 403;
329 | }
330 | }
331 |
332 | try_files $uri $uri/ @rewrite;
333 | location @rewrite {
334 | rewrite ^/(.*)$ /index.php?_url=/$1;
335 | }
336 |
337 | }
338 | ```
339 | #### 运行http://localhost/index.html结果
340 | 
341 |
342 | #### 配置限定的资源文件如果被第三方调用直接返回一张404的图片
343 | ```
344 | server {
345 | listen 80;
346 | server_name dev.api.dd.com;
347 | root /Users/lidong/Desktop/wwwroot/dd_api/public;
348 | index index.php index.html index.htm;
349 | access_log /Users/lidong/wwwlogs/dev.api.dd.com_access.log;
350 | error_log /Users/lidong/wwwlogs/dev.api.dd.com_error.log;
351 | location ~ [^/]\.php(/|$) {
352 | fastcgi_pass 127.0.0.1:9000;
353 | fastcgi_index index.php;
354 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
355 | include fastcgi_params;
356 | }
357 |
358 | location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
359 | valid_referers none blocked dev.api.dd.com;
360 | if ($invalid_referer)
361 | {
362 | rewrite ^/ http://dev.api.dd.com/404.jpeg;
363 | }
364 | }
365 |
366 | try_files $uri $uri/ @rewrite;
367 | location @rewrite {
368 | rewrite ^/(.*)$ /index.php?_url=/$1;
369 | }
370 |
371 | }
372 | ```
373 | #### 运行http://localhost/index.html结果
374 | 调用的图片显示302
375 | 
376 | 用一张源站的404替换显示
377 | 
378 |
379 | ## nginx反向代理
380 | 跨域:浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。
381 |
382 | 下表格为前后端分离的域名,技术信息:
383 |
384 | | 前后端 | 域名 | 服务器 | 使用技术 |
385 | | ------ | ------ | ------ | ------ |
386 | | 前端 | http://b.yynf.com | nginx | vue框架 |
387 | | 后端 | http://api.yynf.com | nginx | php |
388 |
389 |
390 | 两种方式解决跨域的问题:
391 |
392 | 解决方法一:
393 |
394 | 在php入口index.php文件加入header头代码,允许访问解决了js调用api跨域的问题。
395 |
396 | ```
397 | header("Access-Control-Allow-Origin: *");
398 | header("Access-Control-Allow-Headers: Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Mx-ReqToken,X-Requested-With,api-key");
399 | header("Access-Control-Allow-Method: GET, POST, OPTIONS, HEAD");
400 | header("Access-Control-Allow-Credentials: true");
401 | ```
402 |
403 |
404 | 解决方法二:
405 |
406 | 使用nginx的反向代理解决跨域:
407 |
408 | api的nginx配置不需要改变只需要改变前端的服务器的nginx配置即可:
409 | ```
410 | location /apis {
411 | rewrite ^.+apis/?(.*)$ /$1 break;
412 | include uwsgi_params;
413 | proxy_pass http://api.yynf.com;
414 | }
415 | ```
416 |
417 | proxy_pass url地址
418 |
419 | 让nginx监控/apis目录(这里自己定义只要跟nginx配置中保持一致即可),如果发现了这个目录就将所有请求代理到http://api.yynf.com这个请求中,当然也需要在js调用api的请求中多加一层请求结构:
420 | 前端代码中js请求地址
421 | - 旧的js请求api的地址 http://api.yynf.com/badmin/user/add
422 | - 新的js请求api的地址 http://api.yynf.com/apis/badmin/user/add
423 |
424 | 这样一来访问页面就会发现前端代码调用api地址都转向了http://api.yynf.com/apis/,利用将请求通过服务器内部代理实现了跨域问题。
425 | 代理解决跨域的优点:
426 | - 1.有效的隐藏实际api的请求地址和服务器的ip地址
427 | - 2.各司其职让前后端更方便管理,个自搭建自己的服务器保持一定的规范即可。
428 |
429 | ## nginx实现负载均衡
430 | 负载均衡:针对web负载均衡简单的说就是将请求通过负债均衡软件或者负载均衡器将流量分摊到其它服务器。
431 | 负载均衡的分类如下图:
432 | 
433 |
434 | 今天分享一下nginx实现负载均衡的实现,操作很简单就是利用了nginx的反向代理和upstream实现:
435 |
436 |
437 | | 服务器名称 | 地址 | 作用 |
438 | | ------ | ------ | ------ |
439 | | A服务器 | 192.168.0.212 | 负载均衡服务器 |
440 | | B服务器 | 192.168.0.213 | 后端服务器 |
441 | | C服务器 | 192.168.0.215 | 后端服务器 |
442 |
443 |
444 | ### A服务器nginx配置如下:
445 | ```
446 | 1 upstream apiserver {
447 | 2 server 192.168.0.213:8081 weight=1 max_fails=2 fail_timeout=3;
448 | 3 server 192.168.0.215:8082 weight=1 max_fails=2 fail_timeout=3;
449 | 4 }
450 | 5
451 | 6 server {
452 | 7 listen 80;
453 | 8 server_name api.test.com;
454 | 9
455 | 10 location / {
456 | 11 proxy_pass http://apiserver;
457 | 12
458 | 13 }
459 | 14
460 | 15 location ~ /\.ht {
461 | 16 deny all;
462 | 17 }
463 | 18 }
464 | ```
465 |
466 | ### B服务器配置如下:
467 | ```
468 | 1 server {
469 | 2 listen 8081;
470 | 3 server_name 192.168.0.213;
471 | 4 set $root_path '/data/wwwroot/Api/public/';
472 | 5 root $root_path;
473 | 6 index index.php index.html index.htm;
474 | 7 access_log /data/wwwlogs/access_log/api.8081.log;
475 | 8 try_files $uri $uri/ @rewrite;
476 | 9 location @rewrite {
477 | 10 rewrite ^/(.*)$ /index.php?_url=/$1;
478 | 11 }
479 | 12
480 | 13 location ~ \.php {
481 | 14 fastcgi_pass 127.0.0.1:9000;
482 | 15 fastcgi_index index.php;
483 | 16 include /usr/local/nginx/conf/fastcgi_params;
484 | 17 fastcgi_param PHALCON_ENV dev;
485 | 18 fastcgi_split_path_info ^(.+\.php)(/.+)$;
486 | 19 fastcgi_param PATH_INFO $fastcgi_path_info;
487 | 20 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
488 | 21 }
489 | 22 }
490 | ```
491 |
492 | ### C服务器配置如下:
493 |
494 | ```
495 | server {
496 | listen 8082;
497 | server_name 192.168.0.215;
498 | set $root_path '/data/wwwroot/Api/public/';
499 | root $root_path;
500 | index index.php index.html index.htm;
501 | access_log /data/wwwlogs/access_log/api.8081.log;
502 | try_files $uri $uri/ @rewrite;
503 | location @rewrite {
504 | rewrite ^/(.*)$ /index.php?_url=/$1;
505 | }
506 |
507 | location ~ \.php {
508 | fastcgi_pass 127.0.0.1:9000;
509 | fastcgi_index index.php;
510 | include /usr/local/nginx/conf/fastcgi_params;
511 | fastcgi_param PHALCON_ENV dev;
512 | fastcgi_split_path_info ^(.+\.php)(/.+)$;
513 | fastcgi_param PATH_INFO $fastcgi_path_info;
514 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
515 | }
516 | }
517 | ```
518 |
519 |
520 | 到期负载均衡搭建完成,测试的可以访问搭建的域名地址,然后在对应的后端服务器打印access的log日志进行查看请求是否在轮询服务器。
521 |
522 | 思考:负载均衡搭建是搭建成功了,但是也有问题
523 | - 1.这样的架构会出现session无法共享的问题?
524 | - 2.如果其中有一台后端服务器宕机了怎么处理?
525 | 这些问题后面会有文章进行说明
526 |
527 |
528 | ## nginx之gzip压缩提升网站速度
529 | ***
530 | ### 为啥使用gzip压缩
531 | 开启nginx的gzip压缩,网页中的js,css等静态资源的大小会大大的减少从而节约大量的带宽,提高传输效率,给用户快的体验。
532 |
533 | ### nginx实现gzip
534 | nginx实现资源压缩的原理是通过默认集成的ngx_http_gzip_module模块拦截请求,并对需要做gzip的类型做gzip,使用非常简单直接开启,设置选项即可。。
535 |
536 | gzip生效后的请求头和响应头
537 |
538 | ```
539 | Request Headers:
540 | Accept-Encoding:gzip,deflate,sdch
541 |
542 | Response Headers:
543 | Content-Encoding:gzip
544 | Cache-Control:max-age240
545 | ```
546 |
547 | gzip的处理过程
548 |
549 | 从http协议的角度看,请求头声明acceopt-encoding:gzip deflate sdch(是指压缩算法,其中sdch是google自己家推的一种压缩方式)
550 | 服务器-〉回应-〉把内容用gzip压缩-〉发送给浏览器-》浏览器解码gzip->接收gzip压缩内容
551 |
552 | #### gzip的常用配置参数:
553 | - gzip on|off 是否开启gzip
554 | - gzip_buffers 4k 缓冲(压缩在内存中缓冲几块?每块多大?)
555 | - gzip_comp_level [1-9] 推荐6 压缩级别,级别越高压缩的最小,同时越浪费cpu资源
556 | - gzip_disable 正则匹配UA是什么样的URi不进行gzip
557 | - gzip_min_length 200开始压缩的最小长度,小于这个长度nginx不对其进行压缩
558 | - gzip_http_version 1.0|1.1开始压缩的http协议版本(默认1.1)
559 | - gzip_proxied 设置请求者代理服务器,该如何缓存内容
560 | - gzip_types text/plain application/xml 对哪些类型的文件用压缩如txt,xml,html,css
561 | - gzip_vary off 是否传输gzip压缩标志
562 |
563 | #### nginx配置gzip
564 |
565 | 静态页面index.html
566 |
567 | ```
568 |
569 |
570 |
571 |
572 |
577 |