邢栋博客

邢栋博客,Action博客,记录工作和生活中的点点滴滴

php实现单链表
<?php
    class node{
        //初始化变量,包括存储的内容和下一个数据的指针
        public $id = 0;
        public $data = '';
        public $next = null;
        //构造函数,设置存储内容的数据
        public function __construct($id,$nodedata){
            $this->id = $id;
            $this->data = $nodedata;
        }
    }


    class singleLink{
        public $head = '';
        public $size = 0;
        public function  insert($id,$value,$prenodeid = 0){

            $node = new node($id,$value);
            //如果是空链表,直接添加
            if($this->size == 0){
                $this->head = $node;
            }elseif($prenodeid == 0){
                //如果不是空链表,且并没有指定在某一个节点前添加,则在当前节点前添加
                $node->next = $this->head;
                $this->head = $node;
            }else{
                //在某一个节点后添加新节点
                $currentnode = $this->head;
                while($currentnode->next != null) {
                    if ($currentnode->next->id == $prenodeid) {
                        $node->next = $currentnode->next;
                        $currentnode->next = $node;
                        break;
                    }
                    $currentnode = $currentnode->next;
                }
            }

            $this->size++;

            return $this;
        }

        public function edit($id,$value){
            $flag = false;
            $current = $this->head;
            while(@$current->id != null){
                if($current->id == $id){
                    $current->data = $value;
                    $flag = true;
                    break;
                }
                $current = $current->next;
            }
            return $flag;
        }


        public function get($id=0){
            $current = $this->head;
            while(@$current->id != null){
                if($id != 0 && $current->id == $id){
                    $node = $current;
                    break;
                }else{
                    $node[] = array($current->id,$current->data);
                }
                $current = $current->next;
            }
            return $node;
        }


        public function delete($id){
            $flag = false;
            $current = $this->head;
            while(@$current->id != null){
                if($current->next->id == $id){
                    $current->next = $current->next->next;
                    $this->size--;
                    $flag = true;
                    break;
                }

                $current = $current->next;
            }
            return $flag;
        }

    }


    $linklist = new singleLink();
    $linklist->insert(1,'hello1');
    $linklist->insert(2,'hello2');
    $linklist->insert(3,'hello3');
    $linklist->insert(4,'hello4');
    $linklist->insert(5,'hello5');
    $linklist->insert(6,'hello6',2);
    $linklist->insert(7,'hello7');

    $linklist->delete(5);

    $linklist->insert(8,'hello8')->insert(9,'hello9')->insert(10,'hello10');

    echo "<pre>";
    print_r($linklist);
    print_r($linklist->get());
php使用mongo的GridFS存储文件
<?php
    //php使用mongo的GridFS存储文件
    $conn = new MongoClient();
    $db = $conn->photos;
    $collection = $db->getGridFS();

    //存储文件
    $id = $collection->storeFile('./logo22.png');

    //存储文件二进制流
//    $data = file_get_contents('./logo22.png');
//    $id = $collection->storeBytes($data,array('param'=>'logo图片'));

    //保存
    //$id = $collection->storeUpload('upfile');
    //相当于
    //$id = $collection->storeFile($_FILES['upfile']['tmp_name']);

    //读取
    $logo = $collection->findOne(array('_id'=>$id));
    header('Content-type:image/png');//输出图片头
    //var_dump($logo);
    echo $logo->getBytes();

回顾mongo索引

测试索引

新建50万条数据
for(var i=0;i<500000;i++){db.myusers.insert({"i":i,"username":"user"+i,"age":Math.floor(Math.random()*120),"created":new Date()});}
db.myusers.find({username:"user111"}).explain(true)
{
............
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 238,//-------------
"totalKeysExamined" : 0,
"totalDocsExamined" : 500000,//-------------
"executionStages" : {
"stage" : "COLLSCAN",//-------------
"filter" : {
"username" : {
"$eq" : "user111"
},
..............
}

增加索引
db.myusers.createIndex({"username":1})
好处:提高查询效率
坏处:数据增删改时会变慢
限制:每个集合最多64个索引
db.myusers.find({username:"user111"}).explain(true)
{
.................
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 0, //-------------
"totalKeysExamined" : 1,
"totalDocsExamined" : 1,//-------------
"executionStages" : {
"stage" : "FETCH",//-------------
"nReturned" : 1,
"executionTimeMillisEstimate" : 0,
..................
}


复合索引
索引--{"age":1,"username":1}
db.myusers.find({"age":21}) 高效
db.myusers.find({"age":21}).sort({"username":-1})高效
db.myusers.find({"age":{"$gte":21,"$lte":30}})高效
db.myusers.find({"age":{"$gte":21,"$lte":30}}).sort({"username":-1})一般


特殊索引
创建一个固定集合,最大1000字节,最多100个文档,超过后会自动删除最老的文档
db.createCollection('my_collection',{'capped':true,'size':1000,'max':'100'})

TTL索引
在lastUpdated字段建立TTL索引,当服务器时间比该字段存储的日期类型时间晚86400秒之后,文档立即删除。
Mongodb每分钟对ttl索引执行一次清理
db.my_collection.createIndex({"lastUpdated":1},{"expireAfterSecs":86400})


地理空间索引
2dsphere,用于球体表面,使用GeoJSON格式(geojson.org)

{'name':'Sjm','loc':{'type':'Point','coordinates':[116.34,40.02]}}
线
{'name':'River','loc':{'type':'Line','coordinates':[[0,1],[0,2],[1,2]]}}

{'name':'Beijing','loc':{'type':'Polygon','coordinates':[[2,1],[2,2],[4,2]]}}

创建索引
db.world.createIndex({'loc':'2dsphere'})
查询
var littleVillage={
'type':'Polygon',
'coordinates':[[-70,30],[-71,40],[-70,38],[-71,40]]
}
与littleVillage有交集的区域的文档:
db.world.find({'loc':{'$geoIntersects':{'$geometry':littleVillage}}})
完全包含在littleVillage区域的文档
db.world.find({'loc':{'$within':{'$geometry':littleVillage}}})
littleVillage区域附近的文档

db.world.find({'loc':{'$near':{'$geometry':littleVillage}}})


温习memcache

memcached 启动参数

memcached -m 64 -vv

-d 守护进程
-p 指定端口号 默认 11211
-m 指定最大使用内存大小 默认 64m
-t 线程数 默认4
-l 连接的ip地址 ,默认是本机
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数 默认 1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags 默认是48
-vv 详细信息 还打印客户端命令/响应
-I 重写每个数据页尺寸。调整数据项最大尺寸
-R 
通过限制某个连接可以连接提交的命令数,以避免其他连接等待时间过久。某连接提交的命令数一旦达到该阀值,服务器将暂时拒绝其后续的请求,则优先处理其他连接的请求。该限制默认是20,即每个连接可以提交的命令数

-B 绑定协议,ascii,binary,auto (default)


php扩展 memcached memcache
memcached 拥有更多的方法和支持,支持二进制协议,在性能方面更好。 Memcached::setOption


每个chunk size 大小80字节,共有13107个chunk (1024*1024/80=13107)。等于1MB,即1个page
数据存入时,会按照大小分配到不同的chunk中。50字节的数据,会放到 slab class1 中的chunk,浪费30字节

内存分配方式
-m 参数指定预留的内存空间总大小
切分成若干个页(page),默认每页1M,分配给slab class
不同的slab class 中再切割成不同大小的chunk
page被分配到 slab class 后,page就不再移动
每个 slab class 就像一个缓存系统。有自己的lru。



统计命令
stats
进程信息、连接数、请求数、网络流量、LRU
stats slabs
显示各个slab的信息,包括chunk的大小、数目、使用情况等
stats items
显示各个slab中item的数目,及最老item最后一次访问距离现在的秒数等
stats setting
显示进程启动的参数信息


Memcached添加item的逻辑
计算item的大小,选取合适的slab class。如果其对应的slab class为申请过page,则申请一个page的内存,并将该item存入其中一个chunk当中。即便达到预设的内存限制,也会申请一个page内存空间。

如果其对应的slab class出现过,则在该范围的page当中优先选择过期或被删除的chunk进行存储,其次选择未使用过的。

如果其对应的slab class出现过,但是其所属的所有page都已经存储满了,那么会申请一个新的page,这个page被等分为N个规定大小的chunk,存储到第一个chunk。

如果其对应的slab class出现过,但是其所属的所有page都已经存储满,并且memcached已不能再分配新的内存空间。将根据LRU算法(近期最少使用的),清除某个item并将新项存储到该位置。


http的request和response介绍
http请求
http request

1.request line
GET   /dir/1.html      HTTP/1.1
请求方法  资源位置 协议版本

2.HTTP HEADERS
通用header 请求header 实体 header

3.Content


request method
HTTP/1.1规范中的8个请求方法
1.GET      url长度有限制
2.POST 
3.HEAD
4.PUT  //201
5.DELETE
6.TRACE
7.OPTIONS
8.CONNECT



request headers
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
参数为 Content Type
q指定优先级[0-1],为不接受,默认为1
如果不指定*/*,则其他类型优先级为0

Accept-Encoding: gzip, deflate 

Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

Authorization:
Basic
%%%%%%%%%%%%%%%%%

401 Unauthorized

Cookie:
version=1;skin=new;


Cache-Control: 
max-age=0

Host:
www.xingdong365.com

If-Match:
"aetaghdsfsdaf"

If-Modified-Since:
Sun,11 May 2018 08:30:44 GMT

If-None-Match:
"aetaghdsfsdaf"

If-Range:
"aetaghdsfsdaf"

If-Unmodified-Since:
Sun,11 May 2018 08:30:44 GMT

Referer:
http://xingdong365.com

Range:
bytes=0-499,1000-

Upgrade:
HTTP/2.0

Via:
192.168.0.26,example.com

X-Requested-With: //是否是ajax请求
XMLHttpRequest  

X-Forwarded-For:  //代理服务器转发
client1,proxy1,192.168.0.128



http response 

1.Status line
HTTP/1.1 200 OK

100-199:参考信息
200-299:成功
300-399:重定向
400-499:客户端错误
500-599:服务器错误

200 OK 一切ok
GET /index.html HTTP/1.1

201 Created 已创建 通常伴随 Location Header 返回 
PUT /a-new-doc.html HTTP/1.1

206 Partial Content 片段内容 
GET /large.zip HTTP/1.1
Range:bytes0-500

301 Moved Permanently 永久重定向 已永久移动到其他位置,配合location使用
seo适用,无结尾/请求目录时也会自动产生此响应
GET /beauty HTTP/1.1

302 Found 找到了 临时跳转
按HTTP规范,客户端此时应使用和导致产生此响应的请求方法 同样的方法再次请求Location指定位置的资源
在实践中,绝大多数浏览器都一律使用get请求location中指定的资源

304 Not Modified 未修改,无变动(用缓存中的)
Date:..........
ETag:"........"
GET /beauty HTTP/1.1
If-None-Match:"......."
If-Modified-Since:............

400 Bad Request 请求错误 打开的方式不对
401 Unauthorized 未被授权 浏览器收到此响应弹出一个输入用户名 密码的对话框
403 Forbidden 禁止访问
404 not found
405 Method not Allowed  
访问方法不对 服务器禁止以所请求的方法访问,同时一般会通过allow告知允许的方法 
Allow:GET,POST
406 Not Acceptable 无法接受
当请求中的accept系列header中列出的条件无法满足时,会产生此响应
GET / HTTP/1.1
Accept:application/json
408 Request timeout 请求超时 服务器一直没遇到 connection:close 会产生此响应并关闭连接

500 Internal Server Error 服务器错误
502 Bad Gateway 网关错误 代理服务器从上游服务器收到一个无效响应时,会给客户端返回此响应
503 service Unavailable 服务暂时不可用
504 Gateway Timeout 网关超时。代理服务器无法在限定时间内从上游服务器收到一个响应 


2.HTTP headers
通用header 响应header 实体header

ETag:"aetaghdsfsdaf"
Location:http://xingdong365.com
Refresh:3;url=http://xingdong365.com
Set-Cookie:........
Vary:Accept-Language,User-Agent

3.Content
<html>....</html>


http性能优化 performance

缓存
cache-control If-Modified-Since  ETag

cache-control 缓存策略
 max-age=600,no-cache="Set-Cookie"

no-cache="xxx" 缓存,但在发回客户端先做检查,传值则表示不缓存指定的header
no-store 不缓存任何内容,在ie中=no-cache
max-age=120 缓存最大有效期,单位秒(age response header)
max-stale=600 在缓存过期后还可以继续保存600秒,不赋值则表示可一直有效
no-transform 禁止缓存代理修改内容
only-if-cached 禁止缓存代理访问应用服务器,仅在有缓存时返回内容
public 任何客户端(代理服务器或浏览器)均可缓存
private 仅限私有客户端(一般是浏览器)可缓存内容
must-revalidate 必须重新验证缓存有效性(默认行为),此指令目的在于显示指明
proxy-revalidate 代理服务器需要重新验证缓存有效期,浏览器不需要
s-maxage 指定public 客户端上的maxage,private可忽略


第一次请求:服务器响应
Last-Modified : 时间A
第二次请求,附加header,检查是否从上次修改时间点后有过新的修改
If-Modified-Since:时间A

ETag:If-None-Match

Vary:Accept-Encoding
告知缓存代理服务器,客户端请求中发送了不同的Accept-Encoding就要缓存不同的版本

php实现http401授权
<?php

//unset($_SERVER['PHP_AUTH_DIGEST']);

$username = 'xingdong'; //用户名
$userpass = '123456'; //面膜
$secret = 'xingdong365'; //秘钥

$realm = '401test';

$opaque = md5($secret.$_SERVER['HTTP_USER_AGENT'].$_SERVER['REMOTE_ADDR']);

if (!isset($_SERVER['PHP_AUTH_DIGEST']) || empty($_SERVER['PHP_AUTH_DIGEST'])) {
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.$opaque.'"');
    die;
}

$needed_parts = array(
    'nonce'    => 1,
    'nc'       => 1,
    'cnonce'   => 1,
    'qop'      => 1,
    'username' => 1,
    'uri'      => 1,
    'response' => 1
);

$data = array();
$keys = implode('|', array_keys($needed_parts));

preg_match_all('/('.$keys.')=(?:([\'"])([^\2]+?)\2|([^\s,]+))/', $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
    $data[$m[1]] = $m[3] ? $m[3] : $m[4];
    unset($needed_parts[$m[1]]);
}

//检测用户名
if ($data['username'] != $username){
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.$opaque.'"');
    die('Invalid username.');
}

$password = md5($username.':'.$realm.':'.$userpass);

$response = md5($password.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']));



if ($data['response'] != $response) {
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.$opaque.'"');
    die('Invalid password.');
}


echo "success";

SELECT和EPOLL模式
SELECT和EPOLL模式

select模式
1.应用层首先初始化FD_SET(填入需要检测的socket集合),然后调用select函数
2.内核对FD_SET包含的所有socket进行了逐个检测,如果某个socket有状态发生,则填入内容分配一个数组,当所有socket都检查完成后,再将该数组copy到FD_SET中,然后返回应用层

3.select调用返回,应用层从返回的FD_SET中提取有状态发生的socket,并根据socket值映射客户端上下文(可以通过map或hash_map实现映射),然后处理收到的数据

epoll模式
1.应用层调用 epoll_wait检测有事件发生的连接
2.内核对epoll注册事件的socket进行跟踪,一旦某个socket有事件发生,便将其保存到一个内部数组。当接到应用层调用epoll_wait时,直接将该集合copy到epoll_wait数组返回给应用层即可,不需要象select模式对每个worker进行逐一检查。

3.epoll_wait调用返回,所有有事件发生的连接被填入一个epoll_event数组,应用层可根据epoll_event中的用户自定义变量 直接映射客户端上下文(不需要借助hash表),然后处理收到的数据

php代码执行过程简述
php代码的执行过程

扫描->解析->编译->执行->输出

1.扫描(scanning) 将index.php内容变成一个个语言片段(token)
<?php

$code =<<<'PHP_CODE'
<?php
//这是注释
echo "hello world\n";
$data = 1+1;
eval("echo 'Inception lvl 1...\n';");
echo $data;
PHP_CODE;
echo "<pre>";
print_r(token_get_all($code));

echo token_name(319);//输出 T_ECHO  ,260 T_EVAL(eval不是函数)

/*
 *   [16] => Array
        (
            [0] => 319  //tokenid,也就是词的id
            [1] => echo  //词具体的内容
            [2] => 5 //所在的位置,行数
        )
 *
 */

?>

2.解析(parsing) 将一个个语言片段变成有意义的表达式
3.编译(complication) 将表达式编译成中间码(opcode)  //可以使用vld扩展或者parsekit扩展查看
4.执行(execution) 将中间码一条条的执行
如果执行的是某内置函数,会调用对应的函数;
如果调用的是扩展,则会将控制权交给这些扩展,扩展执行完成后将结果返回给 zend engine
全部执行完成后zend engine将结果返回给php内核,内核再返回给sapi,最后返回给服务器,通过服务器httpresponse返回到浏览器

5.输出(output buffer) 将要输出的内容输出到缓冲区


优惠券
最新微语