-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 43.2 KB
/
content.json
1
{"meta":{"title":"Erieniu","subtitle":"昨日种种,譬如昨日死;以后种种,譬如今日生。","description":null,"author":"Erien","url":"http://yoursite.com","root":"/"},"pages":[],"posts":[{"title":"我的大学四年","slug":"我的大学四年","date":"2019-10-31T04:59:53.000Z","updated":"2019-10-31T12:28:33.613Z","comments":true,"path":"2019/10/31/我的大学四年/","link":"","permalink":"http://yoursite.com/2019/10/31/我的大学四年/","excerpt":"","text":"如题 Timeline 大一 大二 大三 大四 P.S. FAQ 来自各处聊天记录 如题#博客里好像欠了好多债没有写,不过没关系了,有时间再来补吧,大学四年也这样飘过去了,让我好好想想我大学四年做了什么。 Timeline#大一# 2016.8 上大学前的暑假,学完了线代。大概是高三综合征还在,也没钱去旅游,就挑了一门课学,虽然后来我忘得差不多了,但多少有个印象。 2016.12 过了四级,裸考,飘过,不谈了。 2017.4 开始搞数学建模,纯粹兴趣,至今没有任何奖。因为数模,开始接触Mathematica,matlab。 2017.5 还是python好用,当年玩得一手好numpy和matplotlib,现在也不行了。 2017.6 再次裸考飘过六级。 同月,抓得一手好requests和BeautifulSoup,教务系统上大概没有比我抢课快的人了. 大二# 2017.9 随便看了下Java,设计模式,Spring这些,可惜的是,Java没有坚持下来,不然我现在应该会舒服不少。 2017.10 我遇到了我这辈子最大的宝贝,Golang,时至今日回头看,在这个选择上,我确实是幸运的。 2017.12 微信小程序上线快一年了,我也写下了我的第一行小程序代码。 2018.3 《我最爱的舞队》微信小程序正式上线,后端Django in py,是我看的第一个后端框架。 这个DAU8000的东西,让我开始明白一个真正的项目会需要什么。 同月,恶补C语言,加入了工作室 2018.6 工作室内用php做后端开发,于是Laravel成了世界上最好的语言。 2018.8 认识了docker,这也是我无比幸运的选择,事实证明,在我开始学习docker后不久,k8s云化大规模出现,大量的生产环境提倡容器化部署。 2018.10 开始有项目,有了不小的第一桶金。 大三# 2018.12 海投寒假实习,真·海投。 2019.1 收到了人生第一个offer,这份工作改变了我对单测的看法,影响了我许多编码风格。 开始进军leetcode。 2019.2 公司里MQ的使用成了我当时最新鲜的事。 2019.3 春招,投几个大厂,兜兜转转最后只有鹅厂收留了我。 2019.6 从Mobvista离职,加入Tencent。我记得我接到offer call那天,我在画一幅A2的二级减速箱装配图。 2019.8 规范,轮子,自动化,监控这些我从没了解过的领域。 大四# 2019.10 tke, hadoop, spark, kafka, flink接踵而至。 同月,通过留用答辩,收到oc,签两方。 P.S.# 专业不对口,也没有修双专业 成绩不好,平均70来分吧,时间都没怎么花在专业课上 大三寒假开始,没怎么上课了 大学还加了两年社团 开心的事应该是做了自己喜欢做的事吧,不开心的也有很多 我只是应届生茫茫大海中的一个人,很平凡,只是大家问起来了,我也当做个记录吧。 FAQ 来自各处聊天记录# 我不知道要干嘛 & 怎么选方向 。。那我更不知道你要干嘛了,兴趣驱动吧,计算机最美好的事就是把想做的事写成代码 想拿好offer要注意什么 要多做项目要努力找实习套努力复习专业课 比如我想从零开始学,要从哪开始? 从计算机基础课开始,C语言,数据结构,然后是你感兴趣的方向,比如前端的东西。 算法很重要吗? 重要,特别是对某些方向。 计算机读研好吗? 挺好,不过我帮不上忙,我就一本科生。 怎么去找实习机会?怎么样才能有实习机会? 如何找得到实习,基本靠简历和基础知识。简历基本靠项目,大家在校期间要自己尝试去做一些项目,尽量完整,涉及到各方面,这样面试官才有东西问,比如说用了什么技术,做了什么优化,效果怎样,还有什么改进空间等等。要对基础知识要有了解,基本的组件比如数据库的一些使用,一些基本算法等等。 我是从大三寒假开始找实习的,这时候应该都会是第一份实习,所以要多投一些企业,海投一下,不要眼高手低,这一份工作对之后的春招的简历有重大的提升,春招会有更大的筹码。春招就可以在自己简历上多写一段实习经历,这样会比写其他项目更容易得到面试机会。","categories":[],"tags":[]},{"title":"ElasticSearch学习(1)","slug":"ElasticSearch学习-1","date":"2019-07-05T12:18:42.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/07/05/ElasticSearch学习-1/","link":"","permalink":"http://yoursite.com/2019/07/05/ElasticSearch学习-1/","excerpt":"","text":"","categories":[],"tags":[]},{"title":"后端弟弟认识的zookeeper","slug":"后端弟弟认识的zookeeper","date":"2019-05-19T14:01:13.000Z","updated":"2019-10-31T12:21:58.569Z","comments":true,"path":"2019/05/19/后端弟弟认识的zookeeper/","link":"","permalink":"http://yoursite.com/2019/05/19/后端弟弟认识的zookeeper/","excerpt":"","text":"前言#第一次听说zookeeper,是activeMQ和kafka里的一个任务调度协调中心。 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。(via百度百科) 角色#从简介看得出,zk的功能是维持一个分布式集群的秩序的存在,在zk的模型里,有三种角色「Leader」「Follower」「Observer」。 一个Leader为整个任务调度中心的管理者,负责整个zk中心的管理、决策、状态储存. 一个Follower就是一个普通的子节点,负责zk中主要业务的操作,包括接受数据,处理数据,处理由leader发起的事务(投票)。 Observer其实可以理解为是一个follower的slave节点,可以接收数据,处理数据的任务将转发给leader,无法进行事务的处理,本质上是为了提高整个zk中心的数据处理速度。 状态#当一个非单节点zk由我们的配置文件启动时,会进入初始化选举leader流程。每个非observer节点会进入looking状态,可以以自身为主发起一个选举请求并全域广播,每个节点负责统计自己选举的票数,当票数过半时当选,当选后执行leader功能更新状态为leading/following并全域广播。 所有的非leader节点会定时向leader发送心跳包以维持连接和确认leader的运行状态。 如果此时leader挂掉或者由于连接过多阻塞了其他连接,无法接受心跳包的时候,所有节点会重新进入looking状态发起选举,但只有空闲状态下的节点能够发起选举(下面会解释空闲)。此时选举只有超过半票才能够当选(以保证大部分节点和主节点丢失连接而非偶然),当新节点上任后,同样会进行全域广播更新状态,但之后还会进行数据更新,向全域各个节点广播自己最大的zxid(事务id,用于区分事务先后),由其他节点来选择更新或者回退数据。 CAP原理#Consistency一致性,Availability可用性,Partition tolerance分区耐受性。一般而言,CA之间不能同时成立,即在分布式架构中,无法同时保证整个系统的高可用性和强一致性。在满足一致性的前提下,系统读写过程需要加锁或者添加事件id,这时便会降低整个系统的效率或者产生冗余的数据。如果优先,满足高可用性,那势必不能对资源进行加锁操作,从而不满足强一致性。 以上的说法是在满足分区在P原则的前提下进行的,zk对每个节点采用数据拷贝的模式,即所有的数据节点的数据都是相同的 数据改动更新#在zk中,CAP的类似实现成为ZAB协议(Zookeeper Atomic Broadcast原子广播协议) 当有客户端连接到对外提供服务的节点并传入修改请求时,非leader节点会将请求转发给leader节点,收到请求后leader会发起一个全局提议并全局广播给所有follower节点(observer节点无投票权),其中,leader和follower之间的通信也是通过消息队列进行的,所以由leader发起的所有提起都是自带顺序的。follower节点从队列中拿出提议并通过向leader节点发送ack确认请求表示确认该次提议,或在本地直接标记为丢弃该请求。当leader受到超过半数的ack确认后立即对该提议进行commit写入数据,然后向全局所有节点发送commit通知更新请求 当一个提议提出但还未提交的时候leader宕机 开始选举流程,参与选举的节点必须为空闲的follower,即没有未处理(分发/确认/丢弃)的提议,若此时原leader节点恢复工作,它将加入follower阵营中,由于它宕机前的提议尚未处理完成,他将没有参选资格。选举完成后,leader必定不是原leader,所以原leader的提议将会回退,本次修改请求失效。 当一个提议被提交后leader宕机 开始选举流程,若此时leader恢复,他有资格参与选举,选举完成后由于提议commit已经分发,本次修改请求有效,对外界无影响。 当收到一个修改请求但为提出提议时leader宕机 开始选举流程,原leader有未处理请求,即使恢复也无法参选,由于未分发,选举完成它本身必产生回退,本次修改请求失效。","categories":[],"tags":[]},{"title":"php第三方包管理","slug":"php第三方包管理","date":"2019-05-11T16:45:26.000Z","updated":"2019-10-31T12:21:58.569Z","comments":true,"path":"2019/05/12/php第三方包管理/","link":"","permalink":"http://yoursite.com/2019/05/12/php第三方包管理/","excerpt":"","text":"前言#在编写程序的时候,我们会用到别人的第三方包,俗称轮子。如何正确引入一个第三方的包,且正确合理的放置,是一个值得规范的事情。 传统的包引入#include "the/path/of/file.php"; require "the/path/of/file.php"; include_once "the/path/of/file.php"; require_once "the/path/of/file.php"; function_name(); ?> 优点:我们比较熟悉包内结构,文件位置缺点:我们必须清楚文件结构;引入大量的inlude,require语句;不能实现懒加载 新的包引入方式#__autoload()魔术方法:当运行时调用了当前没有声明过的类时,自动运行该函数。 <?php function __autoload($class_name){ require "./new_package/class2.php"; } (new hello())->sayHello(); 我们可以在我们文件中重新定义这个函数,并添加我们函数中的逻辑去实现文件的加载,这种方式属于懒加载,一定程度上能节省资源。__autoload()函数必须传入参数(尽管我们也许不会使用)在原本autoload中,同一个文件中只能支持同时存在一个autoload函数,重复定义会引起panic。 autoload的改良#在php5.5后,引入了spl改良版的autoload系列函数,使用spl_autoload_register或者set_include_path和spl_autoload的组合可以很快引入某路径下的类。 <?php // //spl_autoload_register(function ($class_name){ // if ($class_name === 'hello'){ // include "./new_package/class2.php"; // } //}); set_include_path("./new_package/"); //这里需要将路径放入include spl_autoload("class2"); (new hello())->sayHello(); 优点:懒加载,重新定义/抽象了require和include缺点:仍无法解决大量的引入代码问题;可能产生循环引用 包管理工具Composer#composer是php项目中的一个开源第三方包管理工具,是一个让人眼前一亮的工具,它将autoload的思想发挥到了新的层次。 一个使用composer来管理第三方包的项目通常会在项目根目录下包含vender文件夹,里面放有我们的第三方包。 composer使用方法: composer init//初始化一个目录成为composer管理项目 composer require "[email protected].*" //如果已经存在别人的composer.json文件 composer install //升级版本 composer update //删除 composer remove "xxxx" composer通过生成一个总的引入类来引入所有的第三方依赖,我们只需要在整个项目的一个地方引入require "./vender/autoload.php";这样一个文件,就会去自动执行整个需要文件的执行。 composer.json中的autoload和autoload_dev说明了整个项目中的依赖关系的命名关系 "autoload": { "psr-4": { "App\\\\": "app/" }, "classmap": [ "database/seeds", "database/factories" ] }, 我们可以直接使用\\App\\Hello来代表在app目录下的Hello类,composer会自动实现命名空间到文件目录的转换。 这篇博客中有更详细的命名映射方法。 命名空间 composer引入的版本号# 表达式 意义 实例 含义 数字 指定版本 1.2.3 下载1.2.3版本 ~符号 维持小版本 ~1.2.3 下载1.3.0前的版本 ^符号 维持大版本 ^1.2.3 下载2.0.0前的版本 *符号 任意版本 1.2.* 下载1.2中任意一个版本 运算符 运算符含义 >=1.2.3 下载大于1.2.3的版本 @符号 选择分支 1.2.3@dev 下载dev分支中的1.2.3版本 多个条件支持,和|分割,代表and or逻辑运算,如composer require xxx ^1.2.0@dev,!=1.2.3 注意#不要随意改动composer.lock文件中的内容注意将vender文件夹从版本控制中去除","categories":[],"tags":[]},{"title":"2019春招面经总结 | PHP","slug":"2019春招面经总结1","date":"2019-05-05T03:59:06.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/05/05/2019春招面经总结1/","link":"","permalink":"http://yoursite.com/2019/05/05/2019春招面经总结1/","excerpt":"","text":"PHP#PHP-FPM机制#","categories":[],"tags":[]},{"title":"2019春招面经总结 | 目录篇","slug":"2019春招面经总结","date":"2019-04-24T17:40:10.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/04/25/2019春招面经总结/","link":"","permalink":"http://yoursite.com/2019/04/25/2019春招面经总结/","excerpt":"","text":"前言#主要面的岗位是php和golang开发,偶尔全栈。And,这点知识是不够秋招的。 PHP#PHP-FPM机制#PHP垃圾回收机制#PHP依赖注入#PHP类加载机制# GOLANG#GO垃圾回收机制#ARRAY和SLICE异同#SLICE扩容方法#GOROUTINE底层实现#CHANNEL底层实现和信号处理流程#CHANNEL有无缓冲的使用场景#MUTEX底层实现#什么是锁的自旋#进程线程协程在GO中的调用#GRPC和PROTOBUF# 数据结构#八大排序对比#手写堆排#二叉树LCA问题#动态规划#二叉树遍历#BITMAP和大文件处理# 计算机网络#TCP/UDP对比#COOKIE/SESSION区别#HTTP状态码#HTTP1/HTTP2/HTTPS区别和关系#TCP三次握手四次挥手#TIME_WAIT相关# LINUX#常用命令#SED命令#查看CPU和内存情况#软硬链接区别#如何重启服务#如何抓包#NOHUP命令#常用应用# MYSQL#MYSQL的锁及实现#MYSQL常用2大引擎的区别/适用场景#MYSQL 7个JOIN的区别#联合索引#常见的SQL优化方案#ORDERBY速度比较#AUTO_INCREMENT达到最大之后会怎么样# REDIS#REDIS 5种基本数据类型#REIDS STRING类型底层实现#STRING和HASH性能比较#REDIS常用场景#REDIS过期策略#REDIS持久化方案#为什么用了REDIS还需要MYSQL# MONGO#MONGO的特点#MONGO适用场景#为什么会只用MONGO# 其他#DOCKER化原因#SUPERVISOR#RABBITMQ中间件#NGINX基本使用#GIT基本操作#GIT REBASE和MERGE区别#设计模式(手写了单例)# 后记#如果还有再慢慢修改,九月见。","categories":[],"tags":[]},{"title":"算法学习:Dynamic Programming","slug":"Programming","date":"2019-04-09T06:35:55.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/04/09/Programming/","link":"","permalink":"http://yoursite.com/2019/04/09/Programming/","excerpt":"","text":"背景# 动态规划(Dynamic Programming)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。 动态规划背后的基本思想非常简单。——wikipedia 最近陆续参加大厂的春招,发现许多大厂的笔试都会放出至少一道dp算法,可见dp算法在实际业务中的应用十分广泛。然后,我又不会做,所以还是写篇blog来记录一下学习过程。 例题#以下是非常经典的dp题目: 小明有1元,3元,8元,18元,67元的某种货币硬币各若干枚,现在他需要出门购买一份价值277元的礼物,请问他最少需要带多少枚硬币? 约定:为方便,设f(x)=n;其中n为最后数量,x为价值。 显然,我们的第一反应是从面值大的开始拿,先拿67的,拿不下了再换18的……以此类推,最后我们会拿 67*4 + 8*1 + 1*1 刚好拿完我们的277元,f(277)=6。显然这个结果来的太顺利了,如果我们现在的情况如下: 小明有1元,18元,67元的某种货币硬币各若干枚,现在他需要出门购买一份价值80元的礼物,请问他最少需要带多少枚硬币? 那完了,我们从大的开始拿需要 67*1 + 1*13 ,此时f(80)=14,但是我们把各种情况代进去就会发现其实 18*4 + 1*8 这种情况下 f(80)=12 才是最优情况。事实上,在运用贪心算法解决问题的时候,我们必须首先证明该算法是最优算法(最安全)。 解决思路# dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems. ——Wikipedia dp问题的本质在于去寻找各个子问题状态中的最优解,其中在大部分情况下,子问题可以理解为下一步(next step)。for一个sample: 我们面对这个80块,我们可以在下一步产生三种状态: 67*1 + 13 18*1 + 62 和 1*1 + 79 ,这就是上一个问题的三个子问题。这个时候我们发现,我们不再需要关心上一个问题(如何凑80元)是怎么解决的了,现在我们知道,f(80) f(13)+1 f(62)+1 f(79)+1四个问题一定在最优解情况下相等,这就是dp问题的无后效性。 最后,在我们不断地“查询”子问题状态的时候,整个问题的选择将会变成一棵树,我们只需要查询树中从任一叶到根的最短路径的长度即可得出问题的解。 状态储存#给出一个再熟悉不过的计算n=5的斐波那契的代码(in Golang) func Fib(n int) int { if (n == 0){ return 0 } if (n == 1){ return 1 } return Fib(n-1) + Fib(n-2) } func main(){ Fib(5) } 这也是一个很明显的子问题可以画成一棵树的问题,但实际问题在于: Fib(5) 花费掉我608ns, Fib(40)花费掉我1.063243715s,Fib(45)花费掉我11.183974111s。显然,根据树的数据结构,我们计算递归子问题的时间随着子问题深度的变大而变得十分庞大,当算法要求给出的总数达到一定数量级之后,我们便永远不可能通过不断地递归调用而寻找子问题中的最优解。 我们的时间花费在了哪里呢? 很显然,我们的时间花费在了大量重复的计算上,很容易就能想到,这棵树有多少叶子节点,我们便计算了多少次 Fib(0) 的结果,往上推,这一系列重复的动作是从某几根重复的树枝延伸出来的,这个时候,我们需要将已经计算过的结果储存起来。 在进行运算的过程中,我们注意到,整个斐波那契函数的形参是由大变小的。所以,如果我们想要在计算中使用已经储存的结果,那么我们需要有小到大计算斐波那契函数的结果。 var tmp []int func Fib(n int) int { //有结果优先返回 if (len(tmp) >= n){ return tmp[n] } if (n == 0){ tmp = append(tmp, 0) return 0 } if (n == 1){ tmp = append(tmp, 1) return 1 } rs := Fib(n-1) + Fib(n-2) tmp = append(tmp, rs) return rs } func main(){ Fib(45) } 打扰了!上面代码的运行时间27.549µs,多次测试也稳定在30µs以内。差别巨大,至此为止,这个斐波那契函数我们基本可以认为是时间复杂度O(n)的算法。 解决问题#回来帮小明解决买东西的问题,我们仅仅需要将80元以下所有的情况都计算保存,并在计算下一个状态的时候考虑能否用大额硬币替换小额硬币,就能够把该问题通过一个时间复杂度O(n)的算法解决。 var tmp []int func GetNum(n int){ //给一个足够大的初始值,可以是最大值 num := 80 if (n-1 >= 0){ if (tmp[n-1]+1 < num){ num = tmp[n-1] + 1 } } if (n-18 >= 0){ if (tmp[n-18]+1 < num){ num = tmp[n-18] + 1 } } if (n-67 >= 0){ if (tmp[n-67]+1 < num){ num = tmp[n-67] + 1 } } //写入tmp if (len(tmp) <= n){ tmp = append(tmp, num) } } func main(){ //压入初始值tmp[0] tmp = append(tmp, 0) var i int //生成整个tmp for i=1; i<=80; i++ { GetNum(i) } fmt.Println(tmp[80]) } 一般化#实际题目中,会将总价x、货币数量以及面值,通过STDIN传入,并通过TestCase处理。下面是一般化题目: 小明有N种不同面值的某种货币硬币各若干枚, 现在他需要出门购买一份价值M元的礼物, 请问他最少需要带多少枚硬币? 题目输入的第一行给出两个数字分别为总价和货币面值的数量, 从后一行开始,每一行为一个面值,请输出最少携带的硬币。 用例: 输入: 80 3 1 18 67 输出: 12 func GetNum(n int, price []int, tmp []int) []int{ num := 2^32 for i:=0; i<len(price); i++{ if (n-price[i] >= 0){ if (tmp[n-price[i]]+1 < num){ num = tmp[n-price[i]] + 1 } } } if (len(tmp) <= n){ tmp = append(tmp, num) } return tmp } func main(){ //获取输入 var n,m int var price []int fmt.Scan(&m) fmt.Scan(&n) for i:=0; i<n; i++ { x := 0 fmt.Scan(&x) price = append(price, x) } sort.Ints(price) var tmp []int tmp = append(tmp, 0) for j:=1; j<=m; j++ { tmp = GetNum(j, price, tmp) } fmt.Println(tmp[m]) } 呼~长舒一口气 参考资料# 什么是动态规划?动态规划的意义是什么? 使用斐波那契数列引入了动态规划的概念 动态规划 - Wikipedia","categories":[],"tags":[]},{"title":"Docker部署相关","slug":"Docker部署相关","date":"2019-03-29T12:32:55.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/03/29/Docker部署相关/","link":"","permalink":"http://yoursite.com/2019/03/29/Docker部署相关/","excerpt":"","text":"Docker#Docker是由Golang开发的轻量级虚拟容器技术,主要目的是解决在生产环境中的环境、依赖配置问题和单机多部署的性能提升问题。 环境打包#在docker环境构建时,我们通过以下命令构建新的容器 docker run {imageName:Version/Tag} {command} 我们可以通过docker打包解决环境差别问题,下面以php-fpm环境为例:菜鸟教程 选择目录并新建dockerfile`FROM ubuntu:16.01MAINTAINER ERIEN [email protected] ADD http://nginx.org/download/nginx-1.15.0.tar.gzRUN tar zxf nginx-1.15.0.tar.gzRUN mkdir /usr/local/nginxCOPY ./nginx-1.15.0 /usr/local/nginx` 这样子我们就完成了在一个docker文件中配置nginx,只要在docker中启动这个文件就可以完成构建。 单机多部署#很容易可以想到,在同一台主机上通过docker可以简单部署多个相同的服务来模拟分布式,发挥一台主机的性能优势。在以前,会采用虚拟机的方式去进行部署。虚拟机和docker差别如下很容易看出来,docker绕过了系统层面的交互,使得每个容器能够直接地和docker引擎(类似中间件)进行通信,从而避免了多次请求底层系统的内存开销。在性能方面,docker的启动时间简直令人发指,一个基本的LNMP的docker服务的启动仅仅需要7秒左右,但如果这个效果放在虚拟机中,可能会翻好几倍。在储存方面,docker在大部分的语言、数据库镜像的处理上,实现了简易版的体积,例如在数据库方面,数据储存的切片并不会直接储存在docker的“包”里,而是由docker层面提供一个直接和系统交互的切片空间储存,所以尽管在大型服务上线很久之后,我们查看docker的体积也仅仅占几十M左右。 (待更新)","categories":[],"tags":[]},{"title":"HTTP1&HTTP2","slug":"HTTP1-HTTP2","date":"2019-03-26T11:24:54.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/03/26/HTTP1-HTTP2/","link":"","permalink":"http://yoursite.com/2019/03/26/HTTP1-HTTP2/","excerpt":"","text":"HTTP1#HTTP(Hypertext Transfer Protocol)超文本传输协议它是用来在Internet上传送超文本的传送协议。它是运行在TCP/IP协议簇之上的HTTP应用协议,它可以使浏览器更加高效,使网络传输减少。任何服务器除了包括HTML文件以外,还有一个HTTP驻留程序,用于响应用用户请求。您的浏览器是HTTP客户,向服务器发送请求,当浏览器中输入了一个开始文件或点击了一个超级链接时,浏览器就向服务器发送了HTTP请求,此请求被送往由URL指定的IP地址。驻留程序接收到请求,在进行必要的操作后回送所要求的文件。 在HTTP1.0版本中,前端需要在向后端发起请求的时候添加keep-alive头部来知会后端此次数据传输需要开启长连接,但是因为数据传输的不同,并非所有的传输都要建立长连接,所以对每次请求都发起一个新的请求显然是一种浪费的表现。在HTTP1.1中,HTTP协议默认支持长连接,但是默认的支持长连接可能会造成带宽/服务器资源的浪费。所以在HTTP1.1中,header信息和body信息并非同步被发送到服务器,HTTP1.1会先接受header信息并处理返回,前端再根据返回的结果决定是否继续传输数据(1xx同意/4xx拒绝)。 同时,在早起的HTTP1.0协议中,协议并不认为同一服务器上包含一个以上的ip地址,显然在现在有虚拟机分割的情况下,HTTP1.1协议加入和Host头部,为所有的request和response信息提供一个地址。 HTTP2#HTTP/2 (原名HTTP/2.0)即超文本传输协议 2.0,是下一代HTTP协议。是由互联网工程任务组(IETF)的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。是自1999年http1.1发布后的首个更新。HTTP 2.0在2013年8月进行首次合作共事性测试。在开放互联网上HTTP 2.0将只用于https://网址,而 http://网址将继续使用HTTP/1,目的是在开放互联网上增加使用加密技术,以提供强有力的保护去遏制主动攻击。 HTTP2主要新内容# 多路复用 头部压缩 流式数据 二进制数据形式 服务端推送 多路复用#在HTTP1.x中,浏览器对后端发起的请求一般采用管道(HTTP2使用的是二进制传送,二进制传送的单位是帧和流),可以理解为队列, 头部压缩#随着页面的发展,会有越来越多的静态资源会随着网页页面的加载而被请求,如图片、css、字体等等,但是其实我们每一次请求这一类资源都是使用(基本)相同的请求头部,甚至是多余的(比如重复的cookie,timestamp等等),这个时候,header压缩就显得十分之有必要了。一方面,头信息使用 gzip 或 compress 压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,产生一个索引号,之后就不发送同样字段了,只需发送索引号。(p.s. HTTP2的头部压缩使用HPACK算法) 流式数据#HTTP2使用的是二进制传送,二进制传送的单位是帧和流,不需要像HTTP1一样按包为传输单位,这再次提高了长连接的效率 服务端推送#在tcp/ip连接建立之后,服务端可以在未经请求的情况下,自行向客户端推送内容。","categories":[],"tags":[]},{"title":"php中的gc","slug":"php中的gc","date":"2019-03-22T03:46:53.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2019/03/22/php中的gc/","link":"","permalink":"http://yoursite.com/2019/03/22/php中的gc/","excerpt":"","text":"##php中的垃圾回收 ###前言:引用计数PHP中每一个变量的定义都会定义在在一个容器中,容器中提供两个额外的属性,is_ref和 refcount。分别代表该变量是否为引用变量,以及该变量被引用的次数;易知,refcount为1的时候is_ref为false,此时全局没有对该对象的引用,只有本身定义。 在复合类型(Compound Types) 如Array、Object的定义中,以键值对的形式包含多个基本类型定义的容器 以下例子来自手册: <?php $a = array( 'meaning' => 'life', 'number' => 42 ); xdebug_debug_zval( 'a' ); ?> //内部定义形式 a: (refcount=1, is_ref=0)=array ( 'meaning' => (refcount=1, is_ref=0)='life', 'number' => (refcount=1, is_ref=0)=42 ) ###isset和unset函数isset()函数用于检测某个变量是否被定义(是否存在变量的地址),isset()函数会通过检测容器的refcount参数去检测函数的定义。\u0010unset()用于释放一个变量,类似c语言中的free(),但是也不同,当函数对某个变量调用的时候,不会直接释放该内存,而是将该变量容器中的refcount属性减一。 ###垃圾回收与变量生命周期在旧版的php中(<=5.3),不存在主动探测的可能为垃圾变量的动作,只有当变量中的refcount减少的时候,才会产生垃圾周期并检测变量的引用数是否为零,从而发现垃圾。但是这种方法在每一个变量的每一次引用减少时都会被全局调用,在性能上有极大的浪费,所以在新版本(php>=5.4)中,引入新的检测算法来执行垃圾回收。在新的算法中,引入了变量数的阈值,这个值在编译好的php中是10000,为变量缓冲区的大小。当到达这个阈值的时候,将开始执行以下程序: 首先,所有可能根变量都放在一个根缓冲区,并标记为可疑垃圾; 然后,模拟删除每一个可疑变量(将引用次数减一)并确保每个变量会且只会被执行一次该操作; 其次,模拟恢复每一个引用计数大于零的变量(该操作也会对同一变量执行一次); 最后,真正删除没能恢复的变量,这些变量就为垃圾。 PHP手册-垃圾回收机制","categories":[],"tags":[]},{"title":"Golang中go关键字和channel的使用","slug":"Golang中go关键字和channel的使用","date":"2018-09-17T14:24:19.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2018/09/17/Golang中go关键字和channel的使用/","link":"","permalink":"http://yoursite.com/2018/09/17/Golang中go关键字和channel的使用/","excerpt":"","text":"###Golang中的 go 关键字","categories":[],"tags":[]},{"title":"js异步调用和阻止","slug":"js异步调用和阻止","date":"2018-07-26T08:14:04.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2018/07/26/js异步调用和阻止/","link":"","permalink":"http://yoursite.com/2018/07/26/js异步调用和阻止/","excerpt":"","text":"js的异步调用精髓和阻止方法#异步调用# JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序。JavaScript引擎用单线程运行也是有意义的,单线程不必理会线程同步这些复杂的问题,问题得到简化。 为了弥补js单线程机制,引入异步调用机制 在event loop中存在一个类似带有出口的死循环机制,在完成function主体部分后开始被调用。 XXX: function(){ //do sth... Success: function() { /* callback body do sth before Add sth to TODO */ } } 异步操作会检测回调任务队列总中的任务,完成后将之后新的检测添加到个循环中由于callback结束时间无法准确给出,所以js中程序调用的先后级只规定在function主体中。 优点:将剩下待处理的交给callback,延后function结束时间,类似创建监视器。缺点:下一个function运行所需的数据仍在回调加载中,会给出错误。 js异步机制 回调阻止#对于上面的缺点,一般有两种方法规避。 将下一个进行的function写入上一个function的callback中引入Promise对象 主要讲Promise对象 Promise.all Promise规范如下: 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致。then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。 function getImg(url) { var p = Promise(); var img = new Image(); //当img生成时会触发onload函数,在onload函数中将Promise对象的状态设为完成 img.onload = function() { p.resolve(this); }; //出错时设为拒绝调用下一步 img.onerror = function(err) { p.reject(err); }; img.url = url; //返回整体程序的完成程度 return p; }; 以上代码可以写为 function getImg(url) { return Promise(function(resolve, reject) { var img = new Image(); img.onload = function() { resolve(this); }; img.onerror = function(err) { reject(err); }; img.url = url; }); }; 总体:通过Promise状态决定下一步调用","categories":[],"tags":[]},{"title":"新centOS主机配置mysql和nginx","slug":"新centOS主机配置mysql和nginx","date":"2018-07-25T06:54:04.000Z","updated":"2019-10-31T12:21:58.569Z","comments":true,"path":"2018/07/25/新centOS主机配置mysql和nginx/","link":"","permalink":"http://yoursite.com/2018/07/25/新centOS主机配置mysql和nginx/","excerpt":"","text":"新centOS主机配置mysql和nginx# mysql#//获取版本安装repo 这里安装mysql57 [user]# wget http://repo.mysql.com/mysql57-community-release-el7-8.noarch.rpm [user]# rpm -ivh mysql57-community-release-el7-8.noarch.rpm //安装后续服务端 用yum神器 [user]# yum install mysql-server //一路 yes到complete为止 //启动 mysql获取密码 [user]# service mysqld start [user]# grep "password" /var/log/mysqld.log //改密码开放远程权限 [user]# mysql -u root -p mysql> alter user user() indentified by 'NEW PASSWORD'; mysql> update mysql.user host = '%' where user = 'root'; Nginx# //要求sudo安装 yes到底 [user]# sudo yum install nginx //启动 [user]# sudo systemctl nginx","categories":[],"tags":[]},{"title":"php+nginx配置laravel","slug":"php+nginx配置laravel","date":"2018-07-25T06:53:16.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2018/07/25/php+nginx配置laravel/","link":"","permalink":"http://yoursite.com/2018/07/25/php+nginx配置laravel/","excerpt":"","text":"Laravel框架入口函数在 /public/index.php通过设置nginx.conf将root入口设置为/public 即可 server { listen 80; server_name example.com; root /example.com/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.html index.htm index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \\.php$ { fastcgi_split_path_info ^(.+\\.php)(/.+)$; fastcgi_pass unix:/var/run/php/php7.1-fpm.sock; fastcgi_index index.php; include fastcgi_params; } location ~ /\\.(?!well-known).* { deny all; } }","categories":[],"tags":[]},{"title":"go中cookie维持","slug":"go中cookie维持","date":"2018-07-25T06:52:41.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2018/07/25/go中cookie维持/","link":"","permalink":"http://yoursite.com/2018/07/25/go中cookie维持/","excerpt":"","text":"使用cookieJa对response中的cookie进行存储 import "net/http/cookiejar" func main() { var client http.Client jar, err := cookiejar.New(nil) if err != nil { panic(err) } client.Jar = jar client.Post(...) // 在这里登陆 client.Get() // 后续请求client会自动将cookie加入 }","categories":[],"tags":[]},{"title":"高版本macOS中安装软件权限注意事项","slug":"高版本macOS中安装软件权限注意事项","date":"2018-07-25T06:52:00.000Z","updated":"2019-10-31T12:21:58.569Z","comments":true,"path":"2018/07/25/高版本macOS中安装软件权限注意事项/","link":"","permalink":"http://yoursite.com/2018/07/25/高版本macOS中安装软件权限注意事项/","excerpt":"","text":"#奇妙旅程 ###mysql莫名其妙写入失败!删除后brew不能使用需要brew updateupdate需要 chmod /usr/local高版本的根本不允许好吗!sudo都能 operation not permitted! ###原因 MAC /usr/bin/目录下 Operation not permitted的解决真凶的解决办法和原理但是破坏原有机制不是好办法 ###解决方法跳过这个步骤 卸载brew后重装chown: /usr/local: Operation not permitted问题解决纯属记录 ###新状况虚拟环境识别不出_mysql包原因大概是mysql8有点新不支持 找不到解析包libmysqlclient.20.dylib其实 /usr/local/Cellar/mysql/8.0.11/lib 下有一个文件叫 libmysqlclient.21.dylib目前找不到别的方法,名字强行改一下竟然 好了。。。。。终结","categories":[],"tags":[]},{"title":"搭建个人博客","slug":"搭建个人博客","date":"2018-07-25T06:50:40.000Z","updated":"2019-10-31T12:21:58.569Z","comments":true,"path":"2018/07/25/搭建个人博客/","link":"","permalink":"http://yoursite.com/2018/07/25/搭建个人博客/","excerpt":"","text":"Django 搭建个人博客# 第一次使用Django搭建网页,和PHP有相似的地方,后端渲染(数据绑定)返回前端页面。但是相比较于PHP,django有更为明显和直接的优点: 强大的模板渲染 自身的数据库模型 代码整体迁移能力整体 更大型的网站架构和插件 环境:macos + python3.6 + django2.0 + pycharm2018.2使用Pycharm进行Django项目的初始化,免除了 $ sudo python3 django-admin.py startproject blog 这样一个过程 首先新建我们所需要用到的文章应用(app) $ sudo python3 manage.py startapp article 在setting文件中添加article应用,并设置好一篇文章的数据库储存方式。这里我采用的是 class Article(models.Model): title = models.CharField(u'标题', max_length=50) id = models.AutoField(primary_key=True) pub_date = models.DateTimeField(u'发布日期',auto_now_add=True, editable=False) edit_date = models.DateTimeField(u'最后修改日期', auto_now=True, editable=False) content = models.TextField(blank=True, null=True) 这样的一个数据格式,通过主键ID拿到文章数据。首先设置root账户以及绑定后台对Article模型的管理 $ sudo python3 manage.py createsuperuser 并完成后续,同时注册模型 #/article/admin.py from django.contrib import admin from .models import Article admin.site.register(Article) django为我们提供了一个很好的后台管理系统,这里我用来做文章的发布和修改。 整个博客有两个页面index和article分别是主页和文章的详细内容。主页通过 def index(request): Article_list = Article.objects.order_by('-pub_date') template = loader.get_template('index/index.html') context = { 'list': Article_list, } return HttpResponse(template.render(context, request)) 拉取文章列表并通过 {% for article in list %} #do sth.. {% endfor %} 进行读取。在article页面通过传入的参数 def article(request, id_): article_ = get_object_or_404(Article, id=id_) article_.content = markdown.markdown(article_.content, extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) template = loader.get_template('article/article.html') context = { 'article': article_, } return HttpResponse(template.render(context,request)) 拿到文章内容或抛出404这里可以看到,我在文章详情页模板中使用了Markdown语法,是通过python中的 Markdown 解析库实现的。在数据库中存放的是带有Markdown语法的文本,通过Markdown解析成为html语言后插入。这里有两点需要注意的: django对html文本加载的阻止 html标签的样式 一般的框架都有防止xss注入的过程,关闭django的阻止可以让html代码成功加载 {% autoescape off %} {{ article.content }} {% endautoescape %} 然后,Markdown解析库并不会在生成html代码的时候给出css样式,所以 ⌘ + Alt + I 打开Chrome的开发者工具对照每个html的类名将你喜欢的css样式写进相应的css文件中 写markdown语法要有很好的对照,推荐 Cmd Markdown编辑阅读器 除了注册才能保存其他我觉得都很好。有很好的编辑栏 万事预备只欠东风,准备一下就可以放到服务器上 制作数据库迁移文件 关闭DEBUG模式 修改可访问域名 制作迁移文件 $ sudo python3 manage.py makemigrations 然后在setting文件中DEBUG改为False下面allowed_host改为[‘*’]任意域名模式移动到服务器后进行数据库合并 $ sudo python manage.py migrate 最后由于关闭了debug模式,常规下是无法读取静态文件的,开启端口监听的时候选择不安全模式。 $ sudo python manage.py runserver 0.0.0.0:80 --insecure 就可以啦!","categories":[],"tags":[]},{"title":"Ubuntu搭建php+apache细节","slug":"Ubuntu搭建php+apache细节","date":"2018-07-25T04:31:28.000Z","updated":"2019-10-31T12:21:58.565Z","comments":true,"path":"2018/07/25/Ubuntu搭建php+apache细节/","link":"","permalink":"http://yoursite.com/2018/07/25/Ubuntu搭建php+apache细节/","excerpt":"","text":"#Ubuntu搭建php+apache细节 Ubuntu搭建微博框架EMicroblog,其中在Linux下搭建php和apache需要注意一些事情,本文以Ubuntu为例。97516719@qq.com 安装环境#Mysql Apache PHP#$ sudo apt-get install mysql-server mysql-client $ sudo apt-get install apache2 $ sudo apt-get install php7.0 以上安装过程中会有相应提示,其中mysql安装会提示设置root账户密码 安装apache php module#$ sudo apt-get install libapache2-mod-php7.0 重启apache#$ sudo /etc/init.d/apache2 restart 通过浏览器访问 http://localhost来查看apache是否有用。 至此环境已经部署完毕 有兴趣的还可以继续安装phpMyAdmin 权限分配#Linux内核中十分注重权限分配,对于有文件/文件夹读写需求的apache服务组www-data,我们需要分配文件夹权限。 $ cd /var/www $ sudo chmod 777 html 其中/html为apache初始项目组。如果项目文件夹下有多层目录,将上述第二行命令递归调用 $ sudo chmod -R 777 html","categories":[],"tags":[]}]}