邢栋博客

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

关于负载均衡
负载均衡的工作方式

1.http重定向
当http代理(比如浏览器)向web服务器请求某个url后,web服务器可以通过http响应信息中的location标记来返回一个新的url。这意味着http代理需要继续请求这个新的url,完成自动跳转。
缺点:吞吐率限制
优点:不需要额外的技术支持

2.dns负载均衡
dns负责提供域名解析服务,当访问某个站点时,实际上首先需要通过该站点域名的dns服务器来获取域名指向的ip地址,这一过程,dns服务器完成了域名到ip地址的映射,同样,这样映射也可以是一对多的,这个时候,dns服务器便充当了负载均衡调度器
dig google.cn 查看dns的配置
缺点:dns记录缓存更新不及时、策略的局限性、不能做健康检查
优点:可以寻找最近的服务器,加快请求速度
适用场景:多机房部署的时候


3.反向代理负载均衡
在用户的请求到达反向代理服务器时(已经到达网站机房),由反向代理服务器根据算法转发到具体的服务器。常用的apache,nginx都可以充当反向代理服务器。反向代理的调度器可以根据扮演的是用户和实际服务器中间人的角色。
工作在http层(七层)
缺点:代理服务器成为性能的瓶颈,特别是一次上传大文件
有点:配置简单、策略丰富、维持用户回话、可根据访问路径做转发。
适用场景:请求量不高的,简单负载均衡。后端较大的应用。

4.ip负载均衡
工作在传输层(四层)
通过操作系统内修改发送来的ip数据包,将数据包的目标地址修改为内部实际服务器地址,从而实现请求的转发,做到负载均衡。lvs的nat模式。
缺点:所有数据进出还是过负载机器,网络宽带成为瓶颈。
优点:内核完成转发,性能高。
使用场景:对性能要求高,但对宽带要求不高的应用。视频和下载等大宽带的应用,不适合使用。

5.数据链路层的负载均衡
工作在数据链路层(二层)
在请求到达负载均衡器后,通过配置所有集群机器的虚拟ip和负载均衡器相同,再通过修改请求的mac地址,从而做到请求的转发。与ip负载均衡不一样的是,在请求访问服务器 之后,直接返回客户。而无需经过负载均衡器。LVS DR(Direct Routing)模式。
缺点:配置复杂
优点:由集群机器直接返回,提高了出口宽带。
适用场景:大型网站使用最广的一种负载均衡方法



负载均衡中维护用户的session会话
1.把同一个用户在某一个会话中的请求,都分配到固定的某一台服务器上去,常见的负载均衡算法有ip_hash法。
2.session数据集中存储。session数据集中存储就是利用数据库或者缓存来存储session数据,实现了session和应用服务器的解耦。
3.使用cookie代替session。


负载均衡常见的策略
1.轮询
能力比较弱的服务器导致能力较弱的服务器最先超载

2.加权轮询
这种算法解决了简单轮训调度算法的缺点:传入的请求按照顺序被分配到集群中服务器,但是会考虑提前为每台服务器分配的权重。

3.最少连接数
根据后端服务器当前的连接数情况,动态的选取其中当前积压连接数量最少的一台服务器处理当前的请求,尽可能的提高后端服务的利用效率,将请求合理的分流到每一台服务器

4.加权最少连接数

5.源ip_hash
这种方式通过生成请求源ip的哈希值,并通过这个hash值来找到正确的真实服务器,这意味着对于同一主机来说他对应的服务器总是相同。

6.随机
通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问,实际效果接近轮询的结果。


php的IteratorAggregate简单事例
<?php
    //IteratorAggregate Generators

    //事例(一)
    class Language implements IteratorAggregate{
        private $names;
        public function __construct(){
            $this->names = explode(',','PHP,JS,JAVA,GO');
        }
        public function getIterator(){
            return new ArrayIterator($this->names);
        }

    }

    $langs = new Language();

    foreach($langs as $lang){
        echo $lang.PHP_EOL;
    }

    //事例(二) 返回一个外部迭代器,可以对数据本身及相关逻辑做更统一的封装
    class Test implements IteratorAggregate{
        private $data;
        public function __construct(array $data){
            $this->data = $data;
        }
        public function getIterator(){
            foreach($this->data as $key=>$value){
                yield $key=>$value;
            }
        }
    }

    $testArr = array(
        'xd'=>100,
        'action'=>123,
        'xingd'=>126
    );
    $test = new Test($testArr);

    foreach($test as $key=>$value){
        echo $key.'-'.$value.PHP_EOL;
    }
数据结构以及常用数据结构的定义

数据:描述客观事物的符号,如文本、图片、视频
数据元素:组成数据的,有一定意义的基本单位
数据项:一个数据元素可以由若干个数据项组成
数据对象:性质相同的数据元素的组合
数据结构:数据结构是计算机用来组织和存储数据的方式。具体定义:数据结构是指相互之间存在着一种或者多种关系的数据元素的集合和该集合中数据元素的关系组成

数据结构:
逻辑结构 
1.线性结构 (线性表、栈、队、串、数组)
2.非线性结构  树结构和图结构
物理(存储)结构
1.顺序结构
2.链式结构
3.索引结构
4.散列结构
数据运算
1.插入运算
2.删除运算
3.修改运算
4.查找运算
5.排序运算

时间复杂度
1.一般情况下,算法的基本操作重复执行的次数是模块n的某一个函数T(n)
注:如果一个循环次数n次的for语句,如果里面有3条赋值语句,那么总执行次数就是3n,所以这个函数T(n)就是循环次数,T(n)=3n,语句的执行次数(频度)*单位执行时间 就是总执行时间

2.从T(n)中找到同数量级(同阶)函数记作f(n),例如T(n)= 3n的同数量级的函数f(n)=n
注:如果T(n)=n^3+n^2+n,则 f(n)=n^3

3.这个时候我们能够计算出时间复杂度了 T(n)=O(f(n))
用T(n)/f(n),然后求极限值--------  (n^3+n^2+n)/n^3 = 1+1/n+1/n^2  ,求极限值 是一个常数,那么时间复杂度就是 O(f(n)),也就是O(n^3)

简单推导时间复杂度
1.用常数1取代执行时间中的所有加法常数
2.在修改后的执行次数函数中,只保留最高阶项
3.如果最高阶存在且不是1,则去掉他的系数
4.得到的结果就是大O阶
简单说就是 最高阶的执行次数


线性表的特点
定义:零个或多个数据元素的有限序列
除首尾外,均只有一个直接前驱和直接后继

一个数组元素可以有若干数据项组成
线性表有顺序存储和链式存储两种数据结构 
堆栈、队列、串、数组等都是线性表

单链表
head -> A -> B -> C -> D -> E
data-next 
节点包括 1、data数据域,存放节点的值 2、next指针域,存放节点的直接后继的地址
插入节点:头插/尾插,也叫前插和后插,其实就是head所指向节点不一样,当然他们的时间复杂度也不一样
删除节点:先找到要删除的节点位置,将父节点的next指向其子节点,然后删除该节点
查询节点:从head开始一级一级往下找
编辑节点:从head开始一级一级往下找,找到后编辑内容
节点排序:。


双链表
head <-> A <-> B <-> C <-> D <-> E
pre-data-next 
节点包括 1、data数据域,存放节点的值 2、pre指针域,存放节点的直接前驱的地址 3、next指针域,存放节点的直接后继的地址

队列QUEUE
队列是只允许在一端进行插入,则在另外一端进行删除的运算受限的线性表
1.允许删除的一端成为队头(Front)
2.允许插入的一端为队尾(Rear)
3.队列中没有元素成为空队列
4.队列亦称作先进先出的线性表,简称FIFO表

堆栈
先进后出



它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做树是是因为它看起来像一颗倒挂的树
特点
1.每个节点有零个或者多个子节点
2.没有父节点的节点成为根节点
3.每一个非根节点有且只有一个父节点
4.除了根节点外,每个子节点可以分为多个不相符的子树

无序树:树中任意节点的子节点之间没有顺序关系,这种树叫做无序树,也叫做自由树
有序数: 树中任意节点的子节点之间有顺序关系,成为有序树
二叉树:每个节点最多包含两个子树的树称为二叉树 完全二叉树:对于一颗二叉树,假设其深度d(d>1).除了第d层外,其他各层的节点,数目均已达最大值,且第d层所有节点从左向右连续的紧密排列,这样的二叉树称为完全二叉树
满二叉树:对于上述的完全二叉树,如果去掉其第d层的所有节点,那么剩下的部分就构成一个满二叉树(此时该满二叉树的深度为d-1)
霍夫曼树:带全路径最短的二叉树称为霍夫曼树或者最优二叉树



遍历二叉树
    A
  B    C
D E   F
  G H  I
1.先序遍历 ABCDEFGHI  首先访问根结点,然后遍历左子树,然后遍历右子树。简称 根-左-右
2.中序遍历 DBAGECHFI  首先遍历左子树,然后访问根节点,然后遍历右子树。简称 左-根- 右
3.后序遍历 DBGEHIFCA   首先遍历左子树,然后遍历右子树,最后访问根节点。简称 左-右-根

二叉排序数
二叉排序树又称二叉查找数,也成二叉搜索树,它或者是一颗空树;或者是具有下列性质的二叉树:
1.若左子树不空,则左子树上所有节点的值均小于它的根节点的值
2.若右子树不空,则右子树上所有节点的值均小于他的根节点的值
3.左、右子树也分别为二叉排序树



快速排序
通过一趟排序,将待排序部分记录分隔成独立的两部分,其中一部分记录的关键字均比另外一部分记录的关键值小,在分别对这两部分记录进行下一趟排序,以达到整个序列有序。
快速排序就是比较适合用递归来处理的。


简单选择排序
排序思想:设所排序序列的记录个数为n。i取1,2,。。。。n-1,从所有n-i+1个记录中找到排序码最小的记录,与第i个记录交换。执行n-1趟后完成了记录序列的排序。

直接插入排序
每次从无序表中取出第一个元素,把他插入到有序表的合适位置,使有序表依然有序。第一趟比较前两个数,然后把第二个数按大小插入到有序表中;第二趟把第三个数据与前两个数从前向后扫描,第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后完成了整个排序过程。






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就要缓存不同的版本


优惠券
最新微语