为什么?
让我从两个平凡的观察开始:
- 业务需求随时间变化。
- 程序员不是千里眼。
新产品发布,紧急锁定法规,向新市场扩展,经济因素,更新的数据保护法律:商业软件有很多潜在原因需要更新。
从这两个观察结果中,我们可以推断出程序员知道他们编写的代码将要发生变化,而不是知道这些变化将是什么,何时发生。
以一种易于修改的方式编写代码是一项需要数年才能熟练掌握的技能。
您可能已经熟悉了会再次困扰您的编程实践。新手程序员很快意识到,全局变量比它们值得的麻烦更多,并且在过去的十年中,曾经令人难以置信的单例模式一直是一个肮脏的词。
您的应用程序编码方式对适应新要求的难易程度具有很大影响。随着职业发展,您将学习使改编代码变得更容易的技术。一旦掌握了面向对象编程的基础知识,您就会想知道如果没有它,您将如何做!
如果您要求十个开发人员在相同的需求下生产软件,则将获得十个不同的解决方案。这些解决方案中的某些不可避免地会比其他解决方案更好。
考虑一个装在瓶子里的船和一个用乐高制成的模型船。两者都是模型船,但是很难将瓶子中的风帆装在瓶子中,并且几乎不可能重复使用这些零件。但是,对于乐高飞船,您可以轻松调换帆或使用相同的组件来建造火箭,房屋或汽车模型。
某些编程技术会导致“ 瓶中装”方法的出现,并使您的代码难以更改和适应。
Insphpect
Insphpect是一种工具,可以扫描您的代码以寻找编程实践,从而使瓶子设计成为可能。
它根据代码的灵活性对代码进行评分,并突出显示可以提高灵活性的领域。
Insphpect寻找什么?
当前,Insphpect寻找以下内容:
- 紧耦合
- 硬编码配置
- 单身人士
- 二传手注射
new
在构造函数中使用关键字- 服务定位器
- 遗产
- 静态方法
- 全球状态
- 具有多个角色的文件(例如,定义一个类并运行一些代码)
如果它检测到任何被识别为不灵活的内容,则突出显示代码,解释为什么突出显示问题,然后以0-100的评分对整个项目和单个班级评分(未检测到100个问题)。作为概念验证,对于某些检测,它能够自动生成一个补丁文件,该文件重新编写代码以完全消除不灵活性。
在这里看看样本报告。
Insphpect目前处于测试阶段,如果您可以在网站的“提供您的反馈”部分中进行检查并完成调查,那将确实对我的研究进度有所帮助。
背景
但是,那些不好的做法真的不好吗?
这是背景研究中比较困难的部分之一,您可以在Insphpect网站上详细了解如何进行此操作。
然后将它们绘制如下图所示:
每条水平线代表一篇文章,并且每篇文章的左(橙色)栏都是从5的建议-不惜一切代价避免这种做法(最左边)-1-将此做法优先于替代方案。
每篇文章的右侧(蓝色)栏是Jadad风格评分评估的严谨性。得分为7分意味着本文描述了这种做法,提供了代码示例,讨论了替代方法,提供了按需使用的代码示例,讨论了每种方法的优缺点,并提出了应使用哪种方法的建议。
在上述单例的情况下,将单例与替代方法进行比较,讨论优缺点的作者更有可能建议使用替代方法。
演练
当前,Insphpect允许通过Git存储库URL或ZIP文件上传代码。
因此,为了不指出他人工作中的缺陷,让我们看一下我自己的项目中的一个,以了解它的含义。
我们将使用https://github.com/Level-2/Transphporm作为示例项目。
这是一个很好的例子,因为它在另一个代码质量工具Scrutinizer上得分很高。
首先,https://github.com/Level-2/Transphporm
在首页顶部的文本框中输入git URL ,然后按“ Go”。这将需要几秒钟到几分钟的时间,具体取决于项目的大小,并将生成如下所示的报告:
进入报告页面后,您会在顶部看到一个摘要,总体评分为100分,其中100分非常好,0分非常差。
在摘要下,您将看到项目中所有班级的列表,每个班级都有自己的等级。
如果您的代码得分不高,请不要担心。不太可能。请记住,Insphpect是一种可识别代码灵活性的工具。在代码的某些部分(例如入口点),不保证灵活性。
对于Transphporm,它在七个类中突出显示了问题。
让我们来看看其中的一些。向下滚动到Transphporm\Parser\CssToXpath
并单击链接。您会看到该特定班级的分数以及已确定的问题列表。
在这种情况下,它已经确定了静态变量和静态方法。单击其中一条红线将显示该线被标记的原因的解释。
例如,单击第12行将解释为什么静态变量不如实例变量灵活。
尽管对报表上的静态属性引起的问题有更深入的说明,但作为快速回顾,静态变量具有一个在类的所有实例之间共享的值。
这本质上比实例变量灵活,因为使用实例变量允许每个实例具有不同的值。
例如,考虑以下内容:
class User {
public static $db;
public $id;
public $name;
public $email;
public function save() {
$stmt = self::$db->prepare('REPLACE INTO user (id, name, email) VALUES (:id, :name, :email)');
$stmt->execute([
'id' => $this->id,
'name' => $this->name.
'email' => $this->email
]);
}
}
因为$db
是静态的,所以此类的每个实例共享同一$db
实例,并且记录将始终插入到同一数据库中。
虽然这听起来很合理,但举一个真实的例子。
在现实世界
我们的客户之一是一家招聘机构。我们开发他们的网站大约两年后,他们接管了另一家较小的公司。他们想保留第二家公司的网站和品牌,因为在他们所处的细分市场中众所周知。
客户要求我们以下内容:
在第二家公司的网站上,您可以在添加作业时添加一个复选框,并将该作业也添加到我们的数据库中,以便查看我们网站的人也可以看到该作业,反之亦然。
一个相当简单的请求。对两个不同的数据库运行插入查询。
但是因为该网站使用了静态的全局数据库实例,所以这毫无困难!
该站点的开发人员在编写代码时就确信只需要一个数据库连接。他们错了。
请记住,您不是千篇一律的,因此无法预期将来可能需要什么灵活性。
解决方案
正如Insphpect所建议的那样,解决方案是使用实例变量:
class User {
private $db;
public $id;
public $name;
public $email;
public function __construct(\PDO $db) {
$this->db = $db;
}
public function save() {
$stmt = self::$db->prepare('REPLACE INTO user (id, name, email) VALUES (:id, :name, :email)');
$stmt->execute([
'id' => $this->id,
'name' => $this->name.
'email' => $this->email
]);
}
}
现在,一个User
实例可以与不同的数据库实例一起使用:
new User($database1);
new User($database2);
因为Transphporm\Parser\CssToXpath
我们可以做同样的事情,所以删除静态变量并考虑使其成为实例变量而不是静态变量。
在构造函数中使用new
让我们看一下其他类之一:Transphporm\Builder
。
分数为零,这很差。详细检查该报告,Insphpect已经三遍问了同样的问题:new
在构造函数中使用关键字。
Google编程教练Misko Hevery在解释为什么这是糟糕的编程实践方面做得很好,但这是 Insphpect输出的一个简单示例:
class Car {
private $engine;
public function __construct() {
$this->engine = new PetrolEngine();
}
}
在这里,无论何时Car
创建的实例,PetrolEngine
都会创建的实例。这使得它非常不灵活,因为无法Car
用不同的引擎类型构造。在此系统中建模的每辆汽车都必须有一个PetrolEngine
。
相反,我们可以使用依赖注入:
class Car {
private $engine;
public function __construct($engine) {
$this->engine = $engine;
}
}
不同的车可以用实例被创建PetrolEngine
,DieselEngine
,ElectricEngine
,JetEngine
或任何其他引擎中存在的项目类型。
要修复中的错误Transphporm\Builder
,当前具有硬编码类名的所有变量都应改用构造函数参数。
Insphpect还确定了其他问题,但是您可以自己尝试一下,看看您的项目进展如何。福州软件开发
幕后花絮
您可能想知道如何计算分数,以及为什么该类的分数为零。目前,一旦扫描了更多项目并提供了更多反馈,权重可能会发生变化。
分数旨在指示一个项目/班级与另一个项目/班级的比较。
项目的总体分数只是该项目所有班级的平均分数。之所以能够实现这一目标,是因为一个项目在1000个班级中有两个问题比一个项目在两个班级中有两个问题要好得多。
根据是否不利于整个类的灵活性或仅不利于方法的灵活性来对每种不良做法进行加权。
结论
Insphpect可用于识别代码区域,这些区域使将来的更改变得更加困难,并且为如何以更灵活的方式编写代码提供了建议。请记住,您不是千篇一律,也没有办法知道您的代码将如何进行更改!
Insphpect目前仍在开发中,使用它(并完成调查)的人越多,它将变得越好。
您的项目或收藏的图书馆如何评分?确保完成调查,因为它会为我的博士项目提供有价值的数据并帮助改善工具!
汤姆·巴特勒
Tom Butler是一名Web开发人员,研究软件最佳实践的博士学位,是英国的兼职大学讲师,他对编程最佳实践,关注点分离和“少即是多”的编码方法感兴趣。
它可以替代PHP mail()
函数吗?
在大多数情况下,它是PHP mail()
函数的替代方法,但是在许多其他情况下,该mail()
函数的灵活性不足以实现所需的功能。
首先,PHPMailer提供了一个面向对象的接口,而mail()
不是面向对象的。PHP开发人员通常不喜欢在$headers
使用该mail()
函数发送电子邮件时创建字符串,因为它们需要进行大量转义。PHPMailer使这变得轻而易举。使用该mail()
功能时,开发人员还需要编写肮脏的代码(转义字符,编码和格式)以发送附件和基于HTML的电子邮件,而PHPMailer使此工作变得轻松。
此外,该mail()
功能还要求本地邮件服务器发送电子邮件,而设置电子邮件并非总是如此。如果您具有身份验证,PHPMailer可以使用非本地邮件服务器(SMTP)。
其他优点包括:
- 如果无法发送电子邮件,它可以使用40多种语言打印各种错误消息。
- 它具有集成的SMTP协议支持以及基于SSL和TLS的身份验证。
- 它可以为非HTML电子邮件客户端发送电子邮件的替代纯文本版本。
- 它有一个非常活跃的开发人员社区,可确保其安全性和最新性。
流行的PHP内容管理系统(如WordPress,Drupal和Joomla)也使用PHPMailer。
安装PHPMailer
您可以使用Composer 安装PHPMailer:
composer require phpmailer/phpmailer
使用PHPMailer从本地Web服务器发送电子邮件
这是使用PHPMailer从本地Web服务器发送电子邮件的最简单示例:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require_once "vendor/autoload.php";
//PHPMailer Object
$mail = new PHPMailer(true); //Argument true in constructor enables exceptions
//From email address and name
$mail->From = "from@yourdomain.com";
$mail->FromName = "Full Name";
//To address and name
$mail->addAddress("recepient1@example.com", "Recepient Name");
$mail->addAddress("recepient1@example.com"); //Recipient name is optional
//Address to which recipient will reply
$mail->addReplyTo("reply@yourdomain.com", "Reply");
//CC and BCC
$mail->addCC("cc@example.com");
$mail->addBCC("bcc@example.com");
//Send HTML or Plain Text email
$mail->isHTML(true);
$mail->Subject = "Subject Text";
$mail->Body = "<i>Mail body in HTML</i>";
$mail->AltBody = "This is the plain text version of the email content";
try {
$mail->send();
echo "Message has been sent successfully";
} catch (Exception $e) {
echo "Mailer Error: " . $mail->ErrorInfo;
}
代码和注释应该足够清楚,以解释正在发生的一切。
发送带有附件的电子邮件
这是一个如何使用PHPMailer发送带有附件的电子邮件的示例:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require_once "vendor/autoload.php";
$mail = new PHPMailer;
$mail->From = "from@yourdomain.com";
$mail->FromName = "Full Name";
$mail->addAddress("recipient1@example.com", "Recipient Name");
//Provide file path and name of the attachments
$mail->addAttachment("file.txt", "File.txt");
$mail->addAttachment("images/profile.png"); //Filename is optional
$mail->isHTML(true);
$mail->Subject = "Subject Text";
$mail->Body = "<i>Mail body in HTML</i>";
$mail->AltBody = "This is the plain text version of the email content";
try {
$mail->send();
echo "Message has been sent successfully";
} catch (Exception $e) {
echo "Mailer Error: " . $mail->ErrorInfo;
}
在这里,我们将附加两个文件- file.txt
,与脚本位于同一目录,以及images/profile.png
,位于images
脚本目录的目录。
要将附件添加到电子邮件中,我们只需要addAttachment
通过传递文件路径作为参数来调用PHPMailer对象的函数。要附加多个文件,我们需要多次调用它。
故障排除
在我们的两个示例中,我们使用PHPMailer的Exception类进行调试,因此抛出的任何错误将有助于我们调试可能发生的任何问题。我们还将参数添加true
到PHPMailer构造函数中,以输出更高级别,更具描述性的异常。
根据我们使用的系统,我们可能会看到的最常见的错误可能与mail()
在后台运行该函数有关:
Mailer错误:无法实例化邮件功能。
如果我们需要更多有关错误的详细信息,我们还可以在catch子句中添加以下内容:
print_r(error_get_last());
通常,邮件功能的问题与缺少的邮件服务器设置有关,在这种情况下,该error_get_last
功能将返回如下内容:
Array (
[type] => 2
[message] => mail(): Failed to connect to mailserver at "localhost" port 25, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set()
[file] => OUR_PATH \vendor\phpmailer\phpmailer\src\PHPMailer.php
[line] => 863
)
这是我们可能最常遇到的问题,我们可以使用SMTP轻松解决它。
显示本地化的错误消息
$mail->ErrorInfo
可以返回43种不同语言的错误消息。
要以其他语言显示错误消息,请将language
目录从PHPMailer的源代码复制到项目目录。
例如,要以俄语返回错误消息,请使用以下方法调用将PHPMailer对象设置为俄语:
$mail->setLanguage("ru");
您也可以将自己的语言文件添加到language
目录中。
使用SMTP
您可以使用其他主机的邮件服务器发送电子邮件,但是为此,您首先需要进行身份验证。例如,要从Gmail的邮件服务器发送电子邮件,您需要具有一个Gmail帐户。
SMTP是邮件客户端用来将电子邮件发送请求发送到邮件服务器的协议。邮件服务器验证电子邮件后,会将其发送到目标邮件服务器。
这是从您的域的Gmail邮件服务器发送电子邮件的示例。您不需要本地邮件服务器即可运行代码。我们将使用SMTP协议:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once "vendor/autoload.php";
$mail = new PHPMailer(true);
//Enable SMTP debugging.
$mail->SMTPDebug = 3;
//Set PHPMailer to use SMTP.
$mail->isSMTP();
//Set SMTP host name
$mail->Host = "smtp.gmail.com";
//Set this to true if SMTP host requires authentication to send email
$mail->SMTPAuth = true;
//Provide username and password
$mail->Username = "name@gmail.com";
$mail->Password = "super_secret_password";
//If SMTP requires TLS encryption then set it
$mail->SMTPSecure = "tls";
//Set TCP port to connect to
$mail->Port = 587;
$mail->From = "name@gmail.com";
$mail->FromName = "Full Name";
$mail->addAddress("name@example.com", "Recepient Name");
$mail->isHTML(true);
$mail->Subject = "Subject Text";
$mail->Body = "<i>Mail body in HTML</i>";
$mail->AltBody = "This is the plain text version of the email content";
try {
$mail->send();
echo "Message has been sent successfully";
} catch (Exception $e) {
echo "Mailer Error: " . $mail->ErrorInfo;
}
Gmail需要通过SMTP进行TLS加密,因此我们进行了相应的设置。通过SMTP发送之前,您需要找出主机名,端口号,加密类型(如果需要),如果需要身份验证,则还需要用户名和密码。请注意,在Gmail上启用了双重身份验证后,您将无法通过用户名/密码使用其SMTP。相反,将需要其他配置。
通过本地邮件使用远程SMTP的一大优势是,如果您使用PHP的mail()
功能发送from
地址域设置为本地域名(服务器名称)以外的任何其他地址的电子邮件,则收件人的电子邮件服务器的攻击过滤器将对其进行标记作为垃圾邮件。例如,如果您从服务器发送一封电子邮件,其实际主机名example.com
的from
地址name@gmail.com
为name@yahoo.com
,则Yahoo的服务器会将其标记为垃圾邮件,或者向用户显示一条不信任该电子邮件的消息,因为该邮件的来源是该邮件example.com
,但该邮件本身仍然存在好像来自gmail.com
。尽管您name@gmail.com
是Yahoo的拥有者,但Yahoo无法找到答案。
使用POP3检索电子邮件
PHPMailer还允许SMTP之前的POP验证发送电子邮件。换句话说,您可以使用POP进行身份验证并使用SMTP发送电子邮件。遗憾的是,PHPMailer不支持使用POP3协议从邮件服务器检索电子邮件。仅限于发送电子邮件。
结论
如果您是PHP开发人员,则几乎不必避免以编程方式发送电子邮件。尽管您可以选择使用Mandrill或SendGrid之类的第三方服务,但有时这不是一个选择,而滚动自己的电子邮件发送库的可能性甚至更低。那就是PHPMailer及其替代品(Zend Mail,Swift Mailer等)出现的地方。
您可以在存储库Wiki或官方文档中了解此库的API 。
您是否对PHP库依赖感到困惑?观看我们的截屏视频,了解Composer如何帮助您进行管理。
纳拉扬·普鲁斯蒂
Narayan是一位网络宇航员。他是QNimate的创始人。他喜欢教学。他喜欢分享想法。不编码时,他喜欢踢足球。您经常会在QScutter课堂上找到他。
托尼诺·扬科夫(Tonino Jankov)
Tonino是一位Web开发人员和IT顾问,他从事开放源代码开发已有十多年了。他还是加密爱好者,Linux爱好者和温和的自由主义者。
为什么要使用PHP?
PHP仍然是Web上最广泛和最受欢迎的服务器端编程语言。它是由大多数Web主机安装的,具有简单的学习曲线,与MySQL数据库的紧密联系以及可缩短开发时间的出色的库集合。PHP可能并不完美,但是您的下一个Web应用程序肯定会考虑使用PHP。雅虎和Facebook都成功使用了它。
为什么要在本地安装PHP?
在开发PC上安装PHP可使您安全地创建和测试Web应用程序,而不会影响实时网站上的数据或系统。本文将PHP安装描述为Windows版本的Apache 2.2中的一个模块。Mac和Linux用户可能已经安装了它。
多合一套餐
有一些出色的多合一Windows发行版,它们在单个安装文件中包含Apache,PHP,MySQL和其他应用程序,例如XAMPP(包括Mac版本),WampServer和Web.Developer。尽管手动安装Apache和PHP将帮助您了解有关系统及其配置选项的更多信息,但使用这些软件包没有什么错。
PHP安装程序
尽管可以从php.net获得安装程序,但如果您已经配置并运行了Web服务器,我还是建议您手动安装。
手动安装
手动安装具有以下优点:
- 可以在几秒钟内完成备份,重新安装或移动Web服务器的操作(请参阅PC失败后的8条提示),并且
- 您可以更好地控制PHP和Apache配置。
步骤1:下载档案
从www.php.net/downloads.php下载最新的PHP 5 ZIP软件包
与往常一样,病毒扫描文件并使用诸如fsum之类的工具检查其MD5校验和。
步骤2:解压缩文件
我们将PHP文件安装到C:\php
,因此请创建该文件夹并将ZIP文件的内容提取到其中。
PHP可以安装在系统上的任何位置,但是您需要更改以下步骤中引用的路径。
步骤3:设定 php.ini
复制C:\php\php.ini-development
到C:\php\php.ini
。您需要在文本编辑器中更改几行(使用搜索查找当前设置)。如果适用,您将需要删除前导分号以取消注释这些设置。
定义扩展目录:
extension_dir = "C:/php/ext"
启用扩展。这将取决于您要使用的库,但是以下扩展名应该适合大多数应用程序:
extension=curl
extension=gd2
extension=mbstring
extension=mysql
extension=pdo_mysql
extension=xmlrpc
如果要使用PHP mail()
函数发送电子邮件,请输入SMTP服务器的详细信息(您的ISP的服务器应该合适):
[mail function]
; For Win32 only.
SMTP = mail.myisp.com
smtp_port = 25
; For Win32 only.
sendmail_from = my@emailaddress.com
步骤4:添加C:\php
到路径环境变量
为了确保Windows可以找到PHP,您需要更改path环境变量。打开设置,在搜索字段中输入“环境变量”,然后打开结果。选择“高级”选项卡,然后单击“环境变量”按钮。
向下滚动系统变量列表,然后单击“路径”,然后单击“编辑”按钮。单击“编辑文本”,然后添加;C:\php
到“变量值”行的末尾(记住分号)。
现在,单击“确定”,直到出门为止。您可能需要在此阶段重新启动。
步骤5:将PHP配置为Apache模块
确保Apache未运行(net stop Apache2.2
从命令行使用),然后confhttpd.conf
在编辑器中打开其配置文件。以下行应更改:
在第239行上,将index.php添加为默认文件名:
DirectoryIndex index.php index.html
在文件的底部,添加以下行(如有必要,更改PHP文件的位置):
# PHP5 module
LoadModule php5_module "c:/php/php5apache2_2.dll"
AddType application/x-httpd-php .php
PHPIniDir "C:/php"
保存配置文件并从命令行对其进行测试(“开始”>“运行”>“ cmd”):
cd Apache2bin
httpd -t
步骤6:测试PHP文件
index.php
在Apache网页根目录中创建一个名为htdocs或D:WebPages的文件,并添加以下代码:
<?php phpinfo(); ?>
确保Apache已成功启动,打开Web浏览器并输入地址http:// localhost /。如果一切顺利,将出现“ PHP版本”页面,显示所有配置设置。
祝你好运!
克雷格·巴克勒
Craig是一位自由的英国Web顾问,他于1995年为IE2.0建立了他的第一页。自那时以来,他一直倡导标准,可访问性和最佳实践HTML5技术。他为公司和组织(包括英国议会,欧洲议会,能源与气候变化部,微软等)创建了企业规范,网站和在线应用程序。他为SitePoint写了1000多篇文章.
在此之上使用的更高层协议层是应用程序层。在此级别上,各种应用程序使用不同的协议来连接和传输信息。我们有用于发送和接收电子邮件的SMTP,POP3和IMAP,用于聊天的IRC和XMPP,用于远程服务器访问的SSH等。
在这些协议中,最知名的协议已成为Internet使用的代名词,它是HTTP(超文本传输协议)。这就是我们每天用来访问网站的方式。它是由CERN的Tim Berners-Lee于1989年设计的。1.0版的规范于1996年发布(RFC 1945),1999年发布为1.1版。
HTTP规范由万维网联盟维护,可以在https://www.w3.org/standards/techs/HTTP上找到。
该协议的第一代(版本1和1.1)一直主导着网络,直到2015年HTTP / 2发布,业界(网络服务器和浏览器供应商)开始采用该协议。
HTTP / 1
HTTP是基于请求-响应结构的无状态协议,这意味着客户端向服务器发出请求,并且这些请求是原子的:任何单个请求都不知道先前的请求。(这就是我们使用Cookie的原因-例如,在一个用户会话中弥合多个请求之间的间隔,以便能够为登录的用户提供经过身份验证的网站版本。)
传输通常由客户端(即用户的浏览器)启动,服务器通常仅响应这些请求。
我们可以说HTTP的当前状态相当“愚蠢”,或者更好,是低级的,需要向浏览器和服务器提供许多“帮助”,以帮助他们进行有效的通信。在这个领域中进行更改并不是那么容易引入,因为许多现有网站的功能取决于向后兼容任何所引入的更改。为改进协议所做的任何事情都必须以无缝方式完成,并且不会破坏互联网。
在许多方面,当前模型已成为这种严格的请求-响应,原子,同步模型的瓶颈,并且进展主要采取黑客的形式,通常由Google,Facebook等行业领导者带头。正在以各种方式进行改进,以供访问者请求网页,并且当他们的浏览器从服务器接收到网页时,它会解析HTML并找到呈现网页所需的其他资源,例如CSS,图像和JavaScript。遇到这些资源链接时,它将停止加载其他所有内容,并从服务器请求指定的资源。在收到此资源之前,它不会移动一毫米。然后它请求另一个,依此类推。
这包括大量等待和大量往返,在此期间我们的访问者只能看到白色的屏幕或呈现一半的网站。这些浪费了几秒钟。在这些请求周期中,许多可用带宽就闲置在那里。
CDN可以缓解许多此类问题,但是即使它们只是黑客。
正如Mozilla的Daniel Stenberg(从事HTTP / 2标准化工作的人之一)所指出的那样,该协议的第一个版本很难充分利用基础传输层TCP的容量。
一直在努力优化网站加载速度的用户知道,这通常需要一些创造力。
随着时间的流逝,Internet带宽速度急剧增加,但是HTTP / 1.1时代的基础架构并未充分利用这一点。它仍然在诸如HTTP流水线之类的问题上苦苦挣扎-通过同一个TCP连接推送更多资源。浏览器中的客户端支持受到的拖累最大,Firefox和Chrome默认情况下将其禁用,或者根本不支持它,例如IE,Firefox 54+等。
这意味着即使很小的资源也需要打开新的TCP连接,随之而来的是所有的膨胀-TCP握手,DNS查找,延迟…,并且由于行头阻塞,一种资源的加载导致所有其他资源的加载受到阻碍。
Web上的一些优化魔术师必须依靠HTTP / 1模型来优化其网站,包括图像精灵,CSS和JavaScript串联,分片(将访问者对资源的请求分配到多个域或子域中)等。 。
改进是必须的,它必须以一种无缝的,向后兼容的方式解决这些问题,以免中断现有Web的运行。
斯派迪
2009年,Google宣布了一个项目,该项目将成为新一代协议SPDY(发音为speedy)的提案草案,为Chrome提供更多支持,并在随后的几年内将其推向所有Web服务。然后是Twitter和服务器供应商,例如Apache,nginx及其支持,Node.js,后来是Facebook,WordPress.com和大多数CDN提供商。
SPDY引入了多路复用 -通过单个TCP连接并行发送多个资源。默认情况下,连接被加密,并且数据被压缩。首先,在SPDY白皮书中对前25个站点进行的初步测试表明,速度从27%提高到60%以上。
SPDY版本3在生产中得到证明后,便成为超文本传输协议工作组httpbis在2015年制定的HTTP / 2初稿的基础。
HTTP / 2旨在通过以下方法解决与该协议的第一版有关的问题-延迟问题:
- 压缩HTTP标头
- 实现服务器推送
- 通过单个连接多路复用请求。
它还旨在解决行头阻塞。它传输的数据采用二进制格式,从而提高了效率,并且默认情况下需要加密(或者至少是主流浏览器强加的要求)。
标头压缩使用HPACK算法执行,解决了SPDY中的漏洞,并将Web 请求的大小减少了一半。
服务器推送是旨在通过在浏览器需要之前向访问者的浏览器提供资源来解决浪费的等待时间的功能之一。这减少了往返时间,这是网站优化的一大瓶颈。
由于所有这些改进,imagekit.io 可以在此示例页面上看到HTTP / 2带到表的加载时间的差异。
网站拥有的资源越多,节省加载时间就越明显。
如何查看网站是否通过HTTP / 2提供资源
在诸如Firefox或Chrome之类的主要浏览器中,我们可以通过打开“ 网络”选项卡并右键单击资源列表上方的地带,在检查器工具中检查网站对HTTP / 2协议的支持。在这里,我们可以启用协议项。
另一种方法是安装一个基于JavaScript的小工具,该工具使我们能够通过命令行检查HTTP / 2支持(假设我们已安装Node.js和npm):
npm install -g is-HTTP2-cli
安装后,我们应该可以像这样使用它:
is-HTTP2 www.google.com
✓ HTTP/2 supported by www.google.com
Supported protocols: grpc-exp h2 HTTP/1.1
实作
在撰写本文时,所有主流浏览器都支持HTTP / 2,尽管要求对所有HTTP / 2请求进行加密,而HTTP / 2规范本身并不需要。
伺服器
Apache 2.4通过其mod_HTTP2模块支持它,该模块现在应该已经可以量产了。需要通过--enable-HTTP2
在./configure
命令中添加参数来构建Apache 。我们还需要确保至少libngHTTP2
安装了该库的1.2.1版本。如果系统找不到它,我们可以./configure
通过添加来提供路径--with-ngHTTP2=<path>
。
下一步是通过将指令添加到Apache的配置中来加载模块:
LoadModule HTTP2_module modules/mod_HTTP2.so
然后,我们将添加Protocols h2 h2c HTTP/1.1
到虚拟主机块并重新加载服务器。Apache的文档在启用HTTP / 2时警告我们注意事项:
在Apache服务器上启用HTTP / 2会对资源消耗产生影响,如果站点繁忙,则可能需要仔细考虑其含义。
启用HTTP / 2之后的第一件事是服务器进程将启动其他线程。原因是HTTP / 2将接收到的所有请求交给其自己的Worker 线程进行处理,收集结果并将它们流式传输到客户端。
您可以在此处阅读有关Apache配置的更多信息。
nginx 从1.9.5版开始支持HTTP / 2,我们可以通过在虚拟主机规范中添加http2参数来启用它:
server {
listen 443 ssl http2 default_server;
ssl_certificate server.crt;
ssl_certificate_key server.key;
然后重新加载nginx。
不幸的是,在撰写本文时,服务器推送尚未正式实施,但已添加到计划于明年发布的开发路线图中。对于更喜欢冒险的用户,有一个非官方的nginx模块可以添加对HTTP / 2服务器推送的支持。
LiteSpeed和OpenLiteSpeed 还拥有对HTTP / 2的支持。
在服务器端激活HTTP / 2 之前的一项警告是,确保我们具有SSL支持。这意味着我们上面提到的所有虚拟主机片段(对于Apache和nginx)都需要进入SSL版本的虚拟主机块,侦听端口443。一旦安装了Apache或nginx,并配置了常规虚拟主机,获取LetsEncrypt SSL证书并将其安装在任何主要的Linux发行版上,只需几行代码即可。Certbot是一个命令行工具,可自动完成整个过程。
结论
在本文中,概述了HTTP / 2,这是第二代Web协议不断发展的新规范。
可以在此处找到新一代HTTP的完整实现列表。
对于技术欠佳的人来说,过渡到该新协议的最短路径可能是简单地将CDN实现到Web堆栈中,因为CDN最早是HTTP / 2的采用者。