简易HTTP协议解析

简易HTTP协议解析

       首先介绍一些必要的知识点。        TCP协议为操作系统底层协议,能够保证应用层获取到完整的、顺序一直的包序列。但TCP不提供具体的分包,需要上层协议自己解决。TCP发送给上层协议的数据是一个没有意义的字符串序列。如何解释这段序列,需要应用层定义,也就是应用层协议规范的内容。        应用层协议按格式一般可以分为文本协议和二进制协议。文本协议最常见的就是HTTP,二进制协议如websocket。无论是哪种协议,都需要对格式严格定义,以方便程序对字符串序列进行分包、拆包。          HTTP协议通过两种方式定义协议帧(一个HTTP请求或一个HTTP响应)结束标志。第一种是在http header中使用Content-Length头给出body长度,header与body使用\r\n\r\n分隔,这样我们就能够确定一个HTTP帧的开始与结束。另外一种是chunked编码的http帧,通过在header中使用Transfer-Encoding:chunked标志声明该编码方式,消息体由数量未定的块组成,并以最后一个大小为0的块为结束。每一个非空的块都以该块包含数据的字节数(字节数以十六进制表示)开始,跟随一个CRLF (回车及换行),然后是数据本身,最后块CRLF结束。在一些实现中,块大小和CRLF之间填充有白空格(0x20)。最后一块是单行,由块大小(0),一些可选的填充白空格,以及CRLF。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。消息最后以CRLF结尾。   格式如下: HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked 25 This is the data in the first chunk 1C and this is the second one 3 con 8 sequence 0   为简单起见,这里对HTTP服务端协议的解析,只考以Content-Length方式分帧的http帧,不考虑chunked编码的http协议解析。 流程图如下:   如上图所示,TCP提供的是字节流,所以会出现如下三种种情况: 缺包:当前接收到的字符不够一个完整的HTTP帧
Continue reading 简易HTTP协议解析

简单PHP性能记录工具

简单PHP性能记录工具

最近写了一个异步mysql客户端的封装,想与传统的串行方式做下性能对比。包括运行时间、内存使用情况等信息。在github和packagist上搜索并没有找到自己想要的,xhprof又太大了,结果也太复杂,不符合现在的需要。所以决定自己写一个package,来实现对php脚本运行时间和内存使用情况的监控,并生成报告。 项目地址:https://github.com/huyanping/php-timer   示例代码:   require dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; //初始化,设置内存单位 $timer = new \Jenner\Timer(\Jenner\Timer::UNIT_KB); //记录a状态 $timer->mark('a'); sleep(2); //记录b状态 $timer->mark('b'); sleep(3); //记录c状态 $timer->mark('c'); sleep(4); //记录d状态 $timer->mark('d'); //打印总体报告(不包含差值) $timer->printReport(); //获取总体报告,返回数组 $report = $timer->getReport(); //获取一个mark的报告 $a_report = $timer->getReport('a'); print_r($a_report); //打印a状态和b状态的差异信息,包含运行时间、使用内存等 $timer->printDiffReportByStartAndEnd('a', 'b'); //获取a状态和b状态的差异报告 $ab_diff_report = $timer->getDiffReportByStartAndEnd('a', 'b'); //打印第一个mark和最后一个mark之间的差异信息 $timer->printDiffReport(); //获取第一个mark和最后一个mark之间的差异信息 $diff_report = $timer->getDiffReport();
Continue reading 简单PHP性能记录工具

PHP异步并发访问mysql简单实现

PHP异步并发访问mysql简单实现

在实际的开发过程中,我们常常会遇到需要操作多张表,多个库的情况。有时因为一些限制我们不能进行连表(例如,异地数据库),所以只能用php串行访问后再在php里进行合并,有时还需要模拟mysql对合并后的结果进行排序、归并等。 这里产生的一个问题就是串行带来的访问时间问题。由于传统的串行访问方式,我们只能等到一条sql执行完毕后才可以执行下一条,所以执行时间是累加的。PHP官方手册提供了一种可以异步并发访问mysql的方式,详见:http://php.net/manual/zh/mysqli.poll.php,参考资料:https://svn.osgeo.org/mapguide/sandbox/rfc94/Oem/php/ext/mysqli/tests/mysqli_poll.phpt,使用此种方式,我们可以对Mysql进行异步并发访问,访问时间不再是串行累加,而是取决于执行时间最长的sql。 项目地址:https://github.com/huyanping/async-mysql-php show you the code: try{ $async_mysql = new \Jenner\Mysql\Async(); $async_mysql->attach( [‘host’ => ‘127.0.0.1’, ‘user’ => ‘root’, ‘password’ => ”, ‘database’ => ‘test’], 'select * from stu' ); $async_mysql->attach( [‘host’ => ‘127.0.0.1’, ‘user’ => ‘root’, ‘password’ => ”, ‘database’ => ‘test’], 'select * from stu limit 0, 3' ); $result = $async_mysql->execute(); print_r($result);
Continue reading PHP异步并发访问mysql简单实现

PHP文件缓存实现

PHP文件缓存实现

有些时候,我们不希望使用redis等第三方缓存,使得系统依赖于其他服务。这时候,文件缓存会是一个不错的选择。 我们需要文件缓存实现哪些共更能: 功能实现:get、set、has、increment、decrement、delete、flush 能够在较短的时间内返回数据 支持key过期   原理: 为了避免一个文件内的数据过大,造成读取文件的时候延迟较高,我们采用一个key-value一个文件的方式实现存储结构。 为了支持key过期,我们需要把expire数据写入到文件中,所以需要对写入的数据进行序列化处理 为了能够快速的定位到文件路径,我们采用hash算法一次计算出文件位置 实现: <?php /** * Created by PhpStorm. * User: Jenner * Date: 2015/5/15 * Time: 15:40 */ class LeoFileCache implements LeoCacheInterface { /** * 缓存目录 * @var */ private $cache_dir; /** * @param $cache_dir * @throws Exception */ public function __construct($cache_dir) { $this->cache_dir = $cache_dir;
Continue reading PHP文件缓存实现

php包管理机制-支持php5.3的方法

php包管理机制-支持php5.3的方法

composer作为目前主流的php包管理器,使得我们组合不同功能的php代码非常容易,开发也可以更专注某一个模块了。而composer要求我们的php版本不低于php5.3,虽然php5.3也能跑,但不保证不会有问题。而相信目前国内很多公司的php版本仍然停留在php5.3以下的时代,或者由于自身实现了autoload,很难与composer组合到一起,虽然对composer的功能很喜欢,但也只能望而却步。 我们接触最多的php开发,就是MVC了,我们一般只需要编写M、V、C的代码即可,这些框架一般都有提供。但如果框架如果不是很强大的话,例如我们希望在写错误日志的同时,发送报警,相信会有一些框架难以做到这一点。为此你不得不再编写一些helper工具类来帮你实现你想要的。类似的场景会有很多,系统复杂度越高,我们需要的就越多,而框架就有可能在这个时候显得力不从心。 解决上面的问题,我们需要能够灵活的向框架中注入不同的模块,而不影响现有的代码。如何实现呢?spl_autoload_register()函数能够帮我们做到这一点。   下面我假设一个场景,然后利用spl_autoload_register()函数完成我们的模块注入。 假设,我们有一个php5.2的项目,非常复杂,autoload自动加载会扫描很多目录,同时会做autoload缓存,以便提升autoload性能。 如果我们添加一个功能包,除了修改__autoload函数外,我们还可以使用spl_autoload_register,这个函数像是一个注册器,每次调用,都会在一个注册树上注册一个autload函数,当你需要使用一个尚未载入的class时,php会自动从注册树顶部开始调用你所注册的autoload函数,直到找到你要载入的类为止。 利用spl_autoload_register的这个功能,我可以首先要做的事情就是修改我们的__autoload函数,因为如果你使用了__autoload函数,则会清空注册树上的函数。我们必须把我们之前编写的autoload函数同样的注册到注册树上才可以与其他autoload共存。具体看如下示例。 首先假设我们系统的__autoload函数类似如下: function __autoload($class_name){ $filename = FRAMEWORK_DIR . '/' . $class_name . '.class.php'; if(file_exists($filename)){ require $filename; } } 我们只需要做如下修改即可: function myAutoload($class_name){ $filename = FRAMEWORK_DIR . '/' . $class_name . '.class.php'; if(file_exists($filename)){ require $filename; } } spl_autoload_register('myAutoload'); 做完这一步,你可以测试你的系统能否正常运行,至此我们完成了第一步。 下面我们需要注入我们想使用的包,假设我们在github上找到了一个不错的包,但是它需要命名空间支持,所以我们做了些修改,去掉了命名空间。我们希望把这个包注入到我们的系统中。 我们需要做的就是为这个包编写一个autoload函数,然后调用spl_autoload_register()函数,把它注册到注册树上。类似如下: function githubPackageAutoload($class_name){ $filename = GITHUB_PACKAGE_PATH . '/' . $class_name
Continue reading php包管理机制-支持php5.3的方法

PHP模拟SQL的GROUP BY算法

PHP模拟SQL的GROUP BY算法

github地址:https://github.com/huyanping/Zebra-PHP-ArrayGroupBy packagist地址:https://packagist.org/packages/jenner/array_group_by 为什么使用Zebra-PHP-ArrayGroupBy 在如下场景中,我们总是希望能够在php中使用类似mysql的groupby操作: SQL过于复杂,造成数据库运算效率低下 从数据库中读取出原始数据,在php中进行运算,增强代码重用率 其他非数据库场景的数组归并场景 Zebar-PHP-ArrayGroupBy能够做什么 对二维数组进行归并 归并的同时,支持对字段进行自定义处理 比SQL更灵活的自定义函数,你可以随意编写归并和字段合并函数 示例: $records = [ [‘order_date’ => ‘2014-01-01’, ‘price’ => 5], [‘order_date’ => ‘2014-01-02’, ‘price’ => 10], [‘order_date’ => ‘2014-01-03’, ‘price’ => 20], [‘order_date’ => ‘2015-01-04’, ‘price’ => 25], ]; $group_by_fields = [ ‘order_date’ => function($value){ return date(‘Y’, strtotime($value)); } ]; $group_by_value = [ ‘order_date’
Continue reading PHP模拟SQL的GROUP BY算法

PHP微信SDK——Zebra-Wechat

PHP微信SDK——Zebra-Wechat

Zebra-Wechat Zebra-Wechat是微信公众平台开放接口的PHP封装,简单易用,支持以下功能: 接收微信服务器推送信息,对推送信息类型进行识别 微信API客户端封装(用户管理、用户组管理、客服管理、自定义菜单管理、系统管理等) 微信跳转验证封装 微小店相关接口 博客地址:www.huyanping.cn 接收微信推送示例 use \Jenner\Zebra\Wechat\WechatServer; use \Jenner\Zebra\Wechat\Response\TextResponse; $token = ‘you wechat token’; $server = new WechatServer($token); $server->on(‘before’, function(WechatServer $server, $request){ //do something }); $server->on(‘after’, function(WechatServer $server, $result){ //do something }); $server->on(‘unknown_message’, function(WechatServer $server, $request){ //do something }); $server->on(‘unknown_event’, function(WechatServer $server, $request){ //do something }); $server->on(‘text’, function(WechatServer $server, $request){ $to_user =
Continue reading PHP微信SDK——Zebra-Wechat

composer之创建自己的包

composer之创建自己的包

composer的出现,使得PHPer可以像java一样更加方便的管理代码。在composer没有出现之前,人们大多使用pear、pecl管理依赖,但是局限性很多,也很少有人用(接触的大多phper基本不适用pear管理依赖)。composer不仅仅能够解决依赖的问题,也可以在一定程度上解决造轮子的问题。 废话不多说,这篇主要记录如何创建自己的package。 大概步骤如下: 在github上创建一个项目(项目名称可以随意) 编写composer.json copy代码文件并修改命名空间 在https://packagist.org/上递交自己的包 设置github的hook 编写composer.json 先看一个示例: { "name": "jenner/message_queue", "description": "php message queue wrapper", "license": "MIT", "keywords": [“message queue”], "version": "1.0.0", "authors": [ { "name": "Jenner", "email": "hypxm@qq.com" } ], "require": { "php": ">=5.3.0" }, "autoload": { "psr-0": { "Jenner\\Zebra\\MessageQueue": "src/" } } } 需要注意的几个字段说明如下: name:包名称,递交时packagist会检测报名字是否合法。必须是一个/分隔的字符串。当别人引入你的包时,vendor下会自动创建这个目录。例如org/package包,则会在vender下创建org/package目录。 autoload:包的加载方式,具体加载方式可以参考composer中文网说明。这里使用的是psr-0标准加载方式。composer会在src目录下根据命名空间执行自动加载。   copy代码修改命名空间 composer.json文件修改后,我们需要把要打包的源文件复制过来。这里我把所有的文件放在了src目录下,后面可能会有和src同级的tests等目录,而这些目录是不会被加载的。src目录下需遵循psr-0标准。命名空间和目录定义要一直。例如Namespcae/SubNamespace命名空间,则src下必须有Namespace/SubNamespace目录。 代码编写标准可以参考psr-0、psr-1标准
Continue reading composer之创建自己的包

翻译:Laravel Generators laravel代码生成器

翻译:Laravel Generators laravel代码生成器

使用自定义代码生成工具快速进行Laravel开发 这个Laravle包提供了一种代码生成器,使得你可以加速你的开发进程,这些生成器包括: generate:model – 模型生成器 generate:view – 视图生成器 generate:controller – 控制器生成器 generate:seed – 数据库填充器 generate:migration – 迁移 generate:pivot – 关联表 generate:resource -资源 generate:scaffold – 脚手架 安装 需要一个五分钟教程视频吗? Laravel 4.2 或者更低的版本 使用Composer安装这个包,编辑你项目的composer.json文件,在require中添加way/generators “require-dev”: { “way/generators”: “~2.0” } 然后,在命令行下执行composer update: composer update –dev 一旦这个操作完成,就只需要最后一步,在配置文件中加入服务提供者。打开app/config/app.php文件,添加一个新的记录到providers数组中. ‘Way\Generators\GeneratorsServiceProvider’ 这样就可以了,你已经安装完成并可以运行这个包了。运行artisan命令行则可以在终端上看到generate相关命令。 php artisan Laravel 5.0 或者更高版本 使用Composer安装这个包,编辑你项目的composer.json文件,在require中添加way/generators “require-dev”: { “way/generators”: “~3.0” } 由于在Laravel高版本中默认文件夹结构,需要3.0或者更高的版本,才能适应5.0版本以上的Laravel 然后,在命令行下执行composer update: composer update –dev 一旦这个操作完成,就只需要最后一步,在配置文件中加入服务提供者。打开app/config/app.php文件,添加一个新的记录到providers数组中. ‘Way\Generators\GeneratorsServiceProvider’
Continue reading 翻译:Laravel Generators laravel代码生成器

基于Redis的MessageQueue队列封装

基于Redis的MessageQueue队列封装

Redis的链表List可以用来做链表,高并发的特性非常适合做分布式的并行消息传递。 github地址:https://github.com/huyanping/Zebra-Message-Queue packagist地址:https://packagist.org/packages/jenner/message_queue 左进右出 $redis->lPush($key, $value); $redis->rPop($key); 以下程序已在生产环境中正式使用。 基于Redis的PHP消息队列封装 <?php /** * Created by PhpStorm. * User: huyanping * Date: 14-8-19 * Time: 下午12:10 * * 基于Redis的消息队列封装 */ namespace Zebra\MessageQueue; class RedisMessageQueue implements IMessageQueue { protected $redis_server; protected $server; protected $port; /** * @var 消息队列标志 */ protected $key; /** * 构造队列,创建redis链接 * @param $server_config *
Continue reading 基于Redis的MessageQueue队列封装