SegmentFault 黄总最新的文章
2018-01-26T17:07:29+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
Nginx CORS跨域
https://segmentfault.com/a/1190000013007649
2018-01-26T17:07:29+08:00
2018-01-26T17:07:29+08:00
黄总
https://segmentfault.com/u/janhuang
8
<h2>场景</h2>
<p>我们的资源来自网络的四面八方,所以难免需要用上跨域,业界也有非常多跨域的解决方案,这次我是来说说跨域与状态码之间的一个问题。</p>
<h3>问题</h3>
<p>当我们的 URL 地址返回的状态码是 400、403、404、500 的时候,跨域的资源是不会跟随返回的,也就是说,即便是 Nginx 上配置了 <code>add_header</code> 关键字,也不会随着内容返回而返回。</p>
<p>举个例子说:</p>
<pre><code>add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods *;</code></pre>
<p>当我们在请求对应地址的时候,理应是会返回已经配置好的头部信息,但是我们来看看最终的结果。</p>
<blockquote>200</blockquote>
<pre><code class="text">HTTP/1.1 200 OK
Server: openresty/1.11.2.2
Date: Fri, 26 Jan 2018 08:46:39 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 558
Last-Modified: Tue, 28 Mar 2017 01:13:24 GMT
Connection: keep-alive
ETag: "58d9b8b4-22e"
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Accept-Ranges: bytes</code></pre>
<p>内容无误。</p>
<blockquote>404</blockquote>
<pre><code class="text">HTTP/1.1 404 Not Found
Server: openresty/1.11.2.2
Date: Fri, 26 Jan 2018 08:47:18 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 175
Connection: keep-alive</code></pre>
<p>神奇了,这里404状态码下面居然自定义的响应头消失了。</p>
<h3>原因与解决方式</h3>
<p>留意 <a href="https://link.segmentfault.com/?enc=sQQGPW%2BGCh%2Bltgdl1njmNw%3D%3D.bvgyuwjy4UO0Auz4DDz34tRd1NNfYDWGb2slfjw4gC6Xx17XfixO1iypybAibHcLbOK7vhlwDEx0Tzf9x76IWg%3D%3D" rel="nofollow">Nginx 文档</a>上说的:</p>
<blockquote>Adds the specified field to a response header provided that the response code equals 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0). The value can contain variables.</blockquote>
<p>意思就是说,add_header 只会追加到以上响应状态码的响应头上面。</p>
<p>因为咱们的 API 有各种的状态码返回,那么其他状态码下,该怎么办? 大家留意文档上有一个参数。</p>
<pre><code class="text">Syntax: add_trailer name value [**always**];
Default: —
Context: http, server, location, if in location
This directive appeared in version 1.13.2.</code></pre>
<p>你会发现有个 <code>[always]</code> 参数,那么这个参数,就是让你的配置头,应用在所有的影响上面去。</p>
<p>将参数添加进去:</p>
<pre><code class="text">add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods * always;</code></pre>
<p>重启 nginx 服务器后重试一下.</p>
<blockquote>200</blockquote>
<pre><code class="text">HTTP/1.1 200 OK
Server: openresty/1.11.2.2
Date: Fri, 26 Jan 2018 09:01:36 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 558
Last-Modified: Tue, 28 Mar 2017 01:13:24 GMT
Connection: keep-alive
ETag: "58d9b8b4-22e"
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *
Accept-Ranges: bytes</code></pre>
<p>200请求没变化,一切正常。</p>
<blockquote>404</blockquote>
<pre><code class="text">HTTP/1.1 404 Not Found
Server: openresty/1.11.2.2
Date: Fri, 26 Jan 2018 09:02:12 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 175
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: *</code></pre>
<p>现在 404 也正确了。我们的跨域也正是配置完成。</p>
<h4>关于 OPTIONS 请求</h4>
<p>当我们前端发起跨域请求的时候,会事先发起一次 OPTIONS 请求,以用来查询该接口是否支持跨域和对应的请求方法。</p>
<p>在配置方面可以这么做。</p>
<pre><code class="text">if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Allow-Credentials true;
return 204;
}</code></pre>
<p>当然我这里的 <code>*</code> 这么用是不好的,你需要对应域名去配置。</p>
<p>另外PHP方面我们也提供了一个 CORS 的扩展库,可以直接在fastd中使用。</p>
<p>github: <a href="https://link.segmentfault.com/?enc=FGOOldQknf6s7%2FBrbK0IIg%3D%3D.%2BdKspCgXucWIoqR%2FguOIdvxGnf00HCfvsJ5Mg2FnzEsKugzfVWwEt5KuzV76zlL%2B" rel="nofollow">cors-provider</a></p>
FastD 最佳实践六: 为应用添加调用链监控 Zipkin
https://segmentfault.com/a/1190000011165398
2017-09-14T12:54:59+08:00
2017-09-14T12:54:59+08:00
黄总
https://segmentfault.com/u/janhuang
3
<p>zipkin是一个开放源代码分布式的跟踪系统,由Twitter公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。它的理论模型来自于Google Dapper 论文。</p>
<h4>为什么要使用 zipkin</h4>
<p>随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请求可能最终需要调用很多次后端服务才能完成,当整个请求变慢或不可用时,我们是无法得知该请求是由某个或某些后端服务引起的,这时就需要解决如何快读定位服务故障点,以对症下药。于是就有了分布式系统调用跟踪的诞生。而zipkin就是开源分布式系统调用跟踪的佼佼者</p>
<h4>安装 zipkin</h4>
<pre><code>wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'
java -jar zipkin.jar</code></pre>
<h4>安装 Molten</h4>
<p>此处提供一个 C 扩展,为php提供对应的数据收集。地址: <a href="https://link.segmentfault.com/?enc=DCCja2YTcTTGMrHb2u2PwA%3D%3D.soAS3UKPuIhAd5Ew4Y23CIEtUyS8Ycq0gMvWqOWc0zG978dA6hD7rb3Z7N%2Bd2%2Bfa" rel="nofollow">点击我</a></p>
<p>安装参考: <a href="https://link.segmentfault.com/?enc=o%2FNGKGzp8EyEAovEUBa%2F6w%3D%3D.OXW%2FHoByaCLGXsVC5Q0mY0uwKU9RQ7Jrrr9bpNZ1XLE6oKZoGzBotiHolbCFT9GjylxFulSgyNr1apzCble%2B1A%3D%3D" rel="nofollow">molten安装</a></p>
<p>添加到 php.ini</p>
<blockquote><p>可以通过 php --ini 查看 php.ini 文件位置</p></blockquote>
<pre><code>extension=molten.so
molten.enable=1
molten.sink_type=4
molten.tracing_cli=1
molten.sink_http_uri=http://127.0.0.1:9411/api/v1/spans
molten.span_id_format=random
molten.sampling_rate=1</code></pre>
<p>通过 <code>php -m | grep molten</code> 查看是否正确安装。</p>
<h4>结合 FastD PHP 框架</h4>
<p>在 FastD(3.2版本新特性) 中,已经集成了相关调用链的基础类和配置,通过简单配置即可达到日志收集的效果。</p>
<p>调整配置文件: <code>app.php</code></p>
<pre><code class="php"><?php
return [
//code ...
'services' => [
//code ...
\FastD\ServiceProvider\MoltenServiceProvider::class,
],
//code ...
];</code></pre>
<p>在 <code>services</code> 选项中,追加: <code>\FastD\ServiceProvider\MoltenServiceProvider::class</code> 即可。</p>
<p>完成配置后,通过正常访问操作,查看效果.</p>
<p><img src="/img/bVU0My?w=2878&h=1558" alt="图片描述" title="图片描述"><br>日志:</p>
<p><img src="/img/bVUTma?w=2846&h=1502" alt="图片描述" title="图片描述"></p>
<h2>总结</h2>
<p>在应用上线后,我们需要急切并清楚明白应用的运行情况,调用情况,及请求响应情况,在第一时间去发现并解决问题。开发的工作往往是相对简单的,但是对于完整的体系和监控,那是相当的重要。</p>
<p>有了以上系统常规监控、日志集中分析、应用调用链监控,我们的业务就可以变得更加透明,清晰,可控。或许系统不足够完善,但是比起没有,那是要强得多。</p>
<p>相关文章:</p>
<p><a href="https://segmentfault.com/a/1190000011082379">FastD 最佳实践四: 构建系统可视化监控</a><br><a href="https://segmentfault.com/a/1190000011136820">FastD 最佳实践五: 构建ELK日志分析</a></p>
FastD 最佳实践五: 构建ELK日志分析
https://segmentfault.com/a/1190000011136820
2017-09-12T19:14:11+08:00
2017-09-12T19:14:11+08:00
黄总
https://segmentfault.com/u/janhuang
2
<p>过去咱们开发中,对日志这个环节其实并不太重视,直到有一天,应用出现异常,这个时候才想起来“日志”,但很可惜,为时已晚。</p>
<p>咱们做运维和开发,除了救火,还需要防火,因此一些防范的意识也是非常重要的。</p>
<h3>效果图</h3>
<p><img src="/img/bVUTma?w=2846&h=1502" alt="图片描述" title="图片描述"></p>
<h3>安装ELK</h3>
<p>安装 <a href="https://link.segmentfault.com/?enc=Wxpff%2Fw9eh9SFUha2bfPsA%3D%3D.G%2FKQnggGXhQqabnwEQudAsyYfIZyaByCodWbNtnIMsg%3D" rel="nofollow">ELK</a> 是相对简单的,但是后期也需要对其进行优化,适当考虑运维人力,如果觉得个人可以折腾的不放可以尝试。</p>
<p>如果已经存在 ELK 环境,可以直接跳过,进行框架日志配置环节。</p>
<p>点击前往: <a href="https://link.segmentfault.com/?enc=EChaavGsPPxbSV4IbfUCSQ%3D%3D.fQttJd%2BMhOQheGWdWQUbJzPrIEgksMA60T2p2xqPnKWlDi5lpcC1HcMP8QOlAbMt" rel="nofollow">中文地址</a></p>
<h5>先决条件: Java 8</h5>
<pre><code>sudo add-apt-repository -y ppa:webupd8team/java
sudo apt-get update
sudo apt-get -y install oracle-java8-installer</code></pre>
<h5>简单安装 elasticsearch</h5>
<p>下载地址: <a href="https://link.segmentfault.com/?enc=%2BsNTQM6T4Zw3WFAAH%2BwWtA%3D%3D.%2BhXa9GD%2FyxOYkt6%2BOgKDj4J3%2BDqAG1UQUiSAKqsZFEyg71PJYoOT9DzY8YCcbycE" rel="nofollow">elasticsearch</a></p>
<p>下载 zip 或者其他都可以。</p>
<p>解压压缩包。执行:</p>
<blockquote><p>运行前请配置好系统参数。有一定要求,如果启动失败,仔细留意错误信息,对应调整即可。</p></blockquote>
<pre><code>bin/elasticsearch</code></pre>
<p>验证:</p>
<pre><code>curl http://localhost:9200/</code></pre>
<h5>安装 kibana</h5>
<p>下载地址: <a href="https://link.segmentfault.com/?enc=C4PQ4TwGJXuHg9QT%2Bbfuyw%3D%3D.5gKEVQtRqNeQ7ucEfQFAdywKK1YSLte2vcGXVQcKe4oNNBqPzs60njJoTycDN6zl" rel="nofollow">kibana</a></p>
<p>设置 elasticsearch url 地址,也就是刚才的 localhost:9200 (默认)</p>
<p>运行: <code>./bin/kibana</code></p>
<p>打开: <a href="https://link.segmentfault.com/?enc=CuyyPgjhiCeKGaaOpRn11Q%3D%3D.hoaY4QyUCENssssYF9xsP5Y1B43iDnxyw0vYgDq2lhg%3D" rel="nofollow">http://localhost:5601</a> 访问面板</p>
<h5>安装 logstash</h5>
<p>下载地址: <a href="https://link.segmentfault.com/?enc=cpVFzNDIvIYrOKxNLnRB6A%3D%3D.QBbm0WLzPigIxDTQEPen3cap9Fq5WRuaETmZda2Mzx0BVf3VsulRD4R51oIP3YOO" rel="nofollow">logstash</a></p>
<p>配置文件: <code>config/logstash.conf</code></p>
<p>可参照:</p>
<pre><code class="config">input {
file {
type => "fastd"
path => ["log file path"]
}
}
filter {
json {
source => "message"
}
}
output {
# stdout { codec => rubydebug }
elasticsearch {
action => "index"
hosts => "127.0.0.1:9200"
index => "fastd"
}
}</code></pre>
<p>运行: <code>bin/logstash -f config/logstash.conf</code></p>
<p><code>stdout</code> 作为标准输出,可以通过 <code>stdout { codec => rubydebug }</code> 对采集数据进行调试。</p>
<h3>处理 php 应用日志 (FastD 3.2新特性)</h3>
<p>庆幸我们有这么一个需求,也能将工作经验总结并形成最终的解决方案分享给大家。</p>
<p><strong>!!3.1 版本处理方案</strong></p>
<p>新建格式日志文件。然后往下按照步骤进行处理即可(命名空间自定义,添加到日志配置中的第四个参数中)。</p>
<pre><code class="php"><?php
namespace Logger\Formatter;
use Monolog\Formatter\LogstashFormatter;
/**
* Class StashFormatter.
*/
class StashFormatter extends LogstashFormatter
{
public function __construct()
{
parent::__construct(app()->getName(), get_local_ip(), null, null, self::V1);
}
}
</code></pre>
<p>配置 <code>app.php</code></p>
<pre><code class="php"><?php
return [
// code ...
'log' => [
[
\Monolog\Handler\StreamHandler::class,
'info.log',
\FastD\Logger\Logger::INFO,
\FastD\Logger\Formatter\StashFormatter::class,
],
],
// code ...
];</code></pre>
<p>日志会随着配置进行生成,结果如下:</p>
<pre><code>{"@timestamp":"2017-09-12T15:47:37.189080+08:00","@version":1,"host":"10.1.81.60","message":"sprintf(): Too few arguments","type":"fast-d","channel":"fast-d","level":"ERROR","msg":"sprintf(): Too few arguments","code":0,"file":"/Users/janhuang/Documents/htdocs/me/fastd/fastd/vendor/fastd/routing/src/RouteDispatcher.php","line":78,"trace":["#0 [internal function]: FastD\\Application->FastD\\{closure}(2, 'sprintf(): Too ...', '/Users/janhuang...', 78, Array)","#1 /Users/janhuang/Documents/htdocs/me/fastd/fastd/vendor/fastd/routing/src/RouteDispatcher.php(78): sprintf('Middleware %s i...')","#2 /Users/janhuang/Documents/htdocs/me/fastd/fastd/vendor/fastd/routing/src/RouteDispatcher.php(60): FastD\\Routing\\RouteDispatcher->callMiddleware(Object(FastD\\Routing\\Route), Object(FastD\\Http\\ServerRequest))","#3 /Users/janhuang/Documents/htdocs/me/fastd/fastd/src/Application.php(142): FastD\\Routing\\RouteDispatcher->dispatch(Object(FastD\\Http\\ServerRequest))","#4 /Users/janhuang/Documents/htdocs/me/fastd/fastd/src/Application.php(205): FastD\\Application->handleRequest(Object(FastD\\Http\\ServerRequest))","#5 /Users/janhuang/Documents/htdocs/me/fastd/fastd/tests/app/web/index.php(15): FastD\\Application->run()","#6 {main}"]}</code></pre>
<p><strong>忽略上述日志内容,程序看得懂即可</strong></p>
<p>配置 logstash 推送到 elasticsearch.</p>
<p><code>config/logstash.conf</code>, 需要根据业务场景进行配置,现在显示最简单的配置。</p>
<pre><code class="config">input {
file {
type => "fastd"
path => ["log file path"]
}
}
filter {
json {
source => "message"
}
}
output {
elasticsearch {
action => "index"
hosts => "127.0.0.1:9200"
index => "fastd"
}
}</code></pre>
<p>配置无误后,开启日志采集</p>
<pre><code>./bin/logstash -f path/to/logstash.conf</code></pre>
<p>每当日志刷新新增的时候,agent 就会将日志采集推送到 elasticsearch. 如果觉得 ELK 会有卡顿或者性能问题,可能尝试结合 kafka 进行优化。</p>
<p>打开: <code>http://127.0.0.1:5601/</code>,即可看到刚才 php 应用产生的日志。</p>
<h3>总结</h3>
<p>如果 php 应用发生问题,异常而又无法第一时间发现的时候,日志可能是一个很好的切入点,当机器越来越多,应用越来越广泛的时候,日志可能会大量消耗人力,这个时候我们不得不去想办法,一方面解决人力问题,一方面需要提高应用质量。</p>
FastD 最佳实践四: 构建系统可视化监控
https://segmentfault.com/a/1190000011082379
2017-09-08T18:43:36+08:00
2017-09-08T18:43:36+08:00
黄总
https://segmentfault.com/u/janhuang
4
<h3>原有监控系统</h3>
<p><img src="/img/remote/1460000011082384" alt="Alt text" title="Alt text"></p>
<p>整个系统以 Graphite (carbon + whisper) 为核心, kong 通过 <a href="https://link.segmentfault.com/?enc=O3XTAD2nAbpa3PkI8FJE7Q%3D%3D.zoMit2wefq%2B3Rqfx3%2FtLUpQHUGg1k0%2FZ9f6GiCJfX922xa%2B2G0Ma1YRmmbBJ%2FGF7" rel="nofollow">statsd plugin</a> 将服务调用信息发送至 <a href="https://link.segmentfault.com/?enc=Y6waP%2B7Ri6CTQDjwiI%2FoUg%3D%3D.Xb3QGBOPQhMmODl45PkdBg0y2bfQfUZQBqROsGwvDU4%3D" rel="nofollow">statsd</a>, 而 statsd 则将统计信息通过 Web API 保存至<a href="https://link.segmentfault.com/?enc=UVKU9m89BzuRAG4%2FZuVXmA%3D%3D.O2AlxlfBf%2F%2Bcw4Gtd2s3LxMjVYDk3a9ZZ9nznR5OFAA%3D" rel="nofollow">Graphite</a> . 最终在 <a href="https://link.segmentfault.com/?enc=fu320Ro%2BudBktT4N%2F4Ryuw%3D%3D.MWaXD4u6MLM4EutFu7%2B6kvgjuvieNFOC4Apcva59rhw%3D" rel="nofollow">Grafana</a> 中通过 <a href="https://link.segmentfault.com/?enc=5GYtcAYFr%2By%2Fp4x8pLjgBg%3D%3D.UWA0nlbnLiUejfAtZvxhzqcoB15hQz7JXyJLsgi6ebGeAgE3lFyllItl2iE%2BUkRd" rel="nofollow">Graphite Data Source</a> 获取统计信息并输出图表到面板. </p>
<p>这是网上找到的, 对 Grafana 的描述</p>
<blockquote><p>Grafana 是一个开源的指标量监测和可视化工具。常用于展示基础设施的时序数据和应用程序运行分析。Grafana 的 dashboard 展示非常炫酷,绝对是运维提升逼格的一大利器。<br>grafana 的套路基本上跟 kibana 差不多,都是根据查询条件设置聚合规则,在合适的图表上进行展示,多个图表共同组建成一个 dashboard,熟悉 kibana 的用户应该可以非常容易上手。另外 grafana 的可视化功能比 kibana 强得多,而且 4 以上版本将集成报警功能。</p></blockquote>
<p>在之前的监控中, 只能统计到 kong 的调用信息, 整个结构的复杂度高, 而实现的功能却比较简单. 搞了这么大一套鬼东西, 只能查看 kong 的监控. 虽然能通过 <a href="https://link.segmentfault.com/?enc=2QdwUB5GGpbNvVJdwv%2B83w%3D%3D.AAmQU7fFq%2FWEADomJibbpNub8%2B4Az5touiTevksc%2BSn28Lu0VFRLcPp04%2Fl57RkfJeNaAbMa8xrUG4d9xc66cA%3D%3D" rel="nofollow">Zabbix Plugin</a> 在 Grafana 中查看 zabbix 的监控数据, 但是支持度有限, zabbix 的性能也... 叔恶死 ...</p>
<h3>迭代</h3>
<p>开源的时序性数据库不多, 其中比较出名的有 Graphite 跟 influxdb.</p>
<p>这是维基百科上对 influxdb 的介绍</p>
<blockquote><p>InfluxDB 是一个由 InfluxData 开发的开源时序型数据库[note 1]。它由 Go 写成,着力于高性能地查询与存储时序型数据。InfluxDB 被广泛应用于存储系统的监控数据,IoT 行业的实时数据等场景。</p></blockquote>
<p>通过了解, influxdb 相比 Graphite 有这些优势:</p>
<ul>
<li><p>提供 telegraf 作为 agent 采集服务器信息, 并有非常丰富的插件用户采集 Nginx/Redis/PHPFPM/Elasticsearch 等的状态信息. 真正提供了采集/存储/可视化, 屌屌屌.</p></li>
<li><p>可扩展能力 (待实践)</p></li>
<li><p>方便而强大的查询语言</p></li>
<li><p>高效存储 (待验证)</p></li>
</ul>
<p>而其中, Telegraf 也提供了 Statsd Server 功能, 解决了 Statsd 官方推荐的 <a href="https://link.segmentfault.com/?enc=uzKGUTXe9DeBuCgk21o8QA%3D%3D.nYtYNOvt%2FZhxIJbhpD%2Bf3cn4ZQG7h0y1HNzSTCLgqcFINuAmUiXouJv1z3%2FieoKYj78ZiH52jZTKPBuR6uoToQ%3D%3D" rel="nofollow">influxdb backend 插件只支持 influxdb 0.9 的情况</a> .</p>
<h3>基于 Influxdb + Telegraf + Grafana 搭建的监控系统</h3>
<p><img src="/img/remote/1460000011082385" alt="Alt text" title="Alt text"></p>
<p>在这一版的监控系统中, 我们将利用 Telegraf 或者直接提交至 Influxdb, 来采集三种信息:</p>
<ul>
<li><p>在每台机器上安装 Telegraf 用于采集服务器及其安装的软件的状态和统计信息</p></li>
<li><p>在网关所处的机器上, 启用 Telegraf 提供的 Statsd Server 功能, 在 Kong 中, 启用 Statsd 插件, 将调用日志提交至 Statsd.</p></li>
<li><p>在服务中创建计划任务定时提交业务数据提交至 Influxdb</p></li>
</ul>
<p>Influxdb 提供了一个 Web API 用于管理, 类似于 Mysql, Influxdb 也提供了 命令行的 Client 用于管理.</p>
<p>同时, 需要部署 Grafana 用于可视化面板. 部署 <a href="https://link.segmentfault.com/?enc=heTjE4MHJjzypBsYRUOqiw%3D%3D.rq1ooIj7WmKiAc%2BucLjRbLM54tIK%2B%2FunvgufvDJPGW3fZLPmCx4oz5H%2FAGJdluYq" rel="nofollow">Chronograf</a> 用于管理 Influxdb. Chronograf 提供了 Influxdb 的 Web Admin 功能(在 Influxdb 0.9时代是内置在 Influxdb中的), 以及比较丰富的图表功能, 但是不能跟 Grafana 比. 因此我们只把它用来管理 Influxdb.</p>
<h3>Telegraf</h3>
<p>telegraf 内置了很多 <code>Input Plugin</code>, 用途是什么呢?<br>回想到如果是你自己去做一个监控, 能够做到记录每分钟 CPU 的空闲率是多少, 要怎么做?</p>
<ul>
<li><p>搞一个数据库, 用来放数据的</p></li>
<li><p>写一个脚本, 用来获取 CPU 的相关数据, 加上时间戳, 然后保存到数据库</p></li>
<li><p>创建一个定时任务, 一分钟运行一次脚本</p></li>
<li><p>写一个简单的程序, 从数据库查到数据, 然后根据时间戳, 绘制成图表.</p></li>
</ul>
<p>在你的脚本里面, 你可以采集任何你采集得到的数据, 然后怼到数据库. 而 <code>Input Plugin</code> 就是写好了的脚本. 只需要在配置文件中开启, 就可以采集到对应的数据. telegraf 内置的 <code>Input Plugin</code> 有这些:</p>
<ul>
<li><p>Nginx</p></li>
<li><p>MySQL</p></li>
<li><p>PHP FPM</p></li>
<li><p>redis</p></li>
<li><p>Net</p></li>
<li><p>Netstat</p></li>
<li><p>MongoDB</p></li>
<li><p>PostgreSQL</p></li>
<li><p>Zipkin</p></li>
<li><p>Zookeeper</p></li>
<li><p>Elasticsearch</p></li>
<li><p>Apache</p></li>
<li><p>Docker</p></li>
<li><p>...</p></li>
</ul>
<p>oh shit! 我要的都有!</p>
<p>而且, 前面讲到, telegraf 内置了一个 Statsd Server (<a href="https://link.segmentfault.com/?enc=MgMgqXfcoKr9hAEEc4wGiA%3D%3D.r8CWVwhcnobZfcBhu87zbverWFgUk58DZBVzbo%2BRx%2FdggS2Grn8Z6Q%2FbegzDZwBytilUPQQNFrmoyLwKon%2FiJw%3D%3D" rel="nofollow">Service Inputs</a>), 从而解决了 Kong 调用监控的问题. 然而这不是全部, telegraf 还提供了:</p>
<ul>
<li><p>HTTP Listener</p></li>
<li><p>TCP Listener</p></li>
<li><p>UDP Listener</p></li>
<li><p>Webhooks Listener</p></li>
</ul>
<p>shit..</p>
<p>这只是 <code>Input</code>, Output 还支持 <code>Graphite</code>, <code>Elasticsearch</code>, <code>Datadog</code> 等等.. shit..</p>
<h3>安装部署</h3>
<p>需要准备一台机器用于安装数据库及 UI. 安装完成后启动服务, 并在需要监控的每台机器上安装 agent. 根据需要配置好 <code>input plugin</code>.</p>
<p>每个 <code>telegraf</code> 只能提交到一个 <code>database</code>. telegraf 的每个 <code>input</code> 项都会有一个 <code>host</code> 的 <code>tag</code>, 它的值默认是机器的 <code>hostname</code>, 可以在 telegraf 的配置文件中修改.</p>
<p>根据资源编排, 以及 Grafana 的面板模板变量, 将机器与数据库的关系定位:</p>
<ul>
<li><p>以每台机的 IP 作为 <code>hostname</code>, 或是以 <code>{分组名}+{组内编号}</code>.</p></li>
<li><p>一个分组一个数据库</p></li>
<li><p>根据分组需要开启 <code>input plugin</code></p></li>
<li><p>固定一个台机采集所有 mysql, redis 等服务</p></li>
</ul>
<p>好了, 那么开工!</p>
<h4>安装 Influxdb</h4>
<pre><code>cd /usr/local/src
wget https://dl.influxdata.com/influxdb/releases/influxdb-1.3.2.x86_64.rpm
yum localinstall influxdb-1.3.2.x86_64.rpm
# 启动
/etc/init.d/influxdb start
# 检查 8086 端口
curl -i 'http://127.0.0.1:8086'</code></pre>
<h4>安装 Chronograf</h4>
<pre><code>cd /usr/local/src
wget https://dl.influxdata.com/chronograf/releases/chronograf-1.3.6.1.x86_64.rpm
yum localinstall chronograf-1.3.6.1.x86_64.rpm
# 启动
/etc/init.d/chronograf start
# 检查 8888 端口
curl -i 'http://127.0.0.1:8888'</code></pre>
<p>如果 <code>8888</code> 已被占用, 需要指定端口运行</p>
<pre><code>nohup chronograf --port=8889 > /dev/null 2>&1 &</code></pre>
<h4>安装 Grafana</h4>
<pre><code>cd /usr/local/src
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.4.3-1.x86_64.rpm
yum localinstall grafana-4.4.3-1.x86_64.rpm
# 启动
/etc/init.d/grafana-server start
# 检查 3000 端口
curl -i 'http://127.0.0.1:3000'</code></pre>
<h4>安装 Telegraf</h4>
<p>需要在每一台机器上安装 Telegraf 作为 agent, 采集跟上报数据到 Influxdb. 包括安装 Influxdb 的机器</p>
<pre><code>cd /usr/local/src
wget https://dl.influxdata.com/telegraf/releases/telegraf-1.3.5-1.x86_64.rpm
yum localinstall telegraf-1.3.5-1.x86_64.rpm</code></pre>
<p>安装完成后, 默认配置已经有采集系统信息的了, 需要增加几项 <code>input</code></p>
<h5>database</h5>
<p>如果配置的 <code>database</code> 不存在, 将自动创建.</p>
<pre><code>[[outputs.influxdb]]
urls = ["http://10.1.0.1:8086"]
database = "servers_xxxx"
retention_policy = ""
write_consistency = "any"
timeout = "5s"</code></pre>
<h5>nginx</h5>
<p>需要在 nginx 上启用 status, 我们固定使用 1200 端口.</p>
<pre><code>server {
listen *:1200 default_server;
server_name _;
location /nginx_status
{
stub_status on;
access_log off;
}
}</code></pre>
<p>然后修改 <code>/etc/telegraf/telegraf.conf</code></p>
<pre><code> [[inputs.nginx]]
urls = ["http://127.0.0.1:1200/nginx_status"]</code></pre>
<h5>PHP FPM</h5>
<p>需要启用 fpm 的status</p>
<pre><code>pm.status_path = /status</code></pre>
<p>然后修改 <code>/etc/telegraf/telegraf.conf</code></p>
<pre><code> [[inputs.phpfpm]]
urls = ["fcgi://127.0.0.1:7006/status"]</code></pre>
<h5>Net</h5>
<pre><code> [[inputs.net]]
interfaces = ["eth0", "eth1"]</code></pre>
<h5>Netstat</h5>
<pre><code> [[inputs.netstat]]</code></pre>
<p>配置完成后, 需要对配置进行测试, 完成后启动再启动</p>
<pre><code>telegraf -config /etc/telegraf/telegraf.conf -test
/etc/init.d/telegraf start</code></pre>
<h3>配置 Grafana 面板</h3>
<p>在 Grafana 中, 需要先配置数据源 (Data Source), 然后创建 Dashboard, 在 Dashboard 中创建 Panel 也就是各种统计组件. 最终完成一个面板的配置.</p>
<h4>配置数据源</h4>
<p><img src="/img/remote/1460000011082386" alt="Alt text" title="Alt text"><br>配置数据源需要注意几个地方:</p>
<ul>
<li><p>type, 选择 Influxdb,</p></li>
<li><p>name, 固定 <code>server_{name}</code></p></li>
<li><p>url, Influxdb 的地址</p></li>
<li><p>access, 固定 proxy. 此外还有 direct. 前者是经由 Grafana 所在机器代理访问 Influxdb, 后者是在浏览器直接访问 Influxdb.</p></li>
<li><p>database, 目标机器的 Telegraf 做配置的 database</p></li>
</ul>
<p>配置完成后, 点及 <code>Add</code> 两次, 如果显示 <code>test success</code> 即为成功.</p>
<h4>创建面板</h4>
<p><img src="/img/remote/1460000011082387" alt="Alt text" title="Alt text"><br><img src="/img/remote/1460000011082388" alt="Alt text" title="Alt text"><br><img src="/img/remote/1460000011082389" alt="Alt text" title="Alt text"></p>
<p>可以选择切换到手动编辑 SQL 模式.<br><img src="/img/remote/1460000011082390" alt="Alt text" title="Alt text"></p>
<p>然后保存, 这样就创建好了第一个面板了.</p>
<h3>查询语言</h3>
<p>具体需要查看<a href="https://link.segmentfault.com/?enc=amKydbbPQdF59KPO8hIgOw%3D%3D.l4Ou8EFL%2BMzgd4Cq9%2Fog16zsQs2yJYrHPzFZKtjYYE3d4DWeumiBuQJswpQ2VlJZR3Ajg49NoVu49E4R5rM17A%3D%3D" rel="nofollow">官方文档</a></p>
<p>Influxdb 使用的查询语言是一种类 SQL 的查询语言</p>
<blockquote><p>InfluxDB’s SQL-like query language for interacting with data in InfluxDB.</p></blockquote>
<p>Influxdb 是一种时序型的数据库, 跟关系型数据库(以 mysql 为例)的区别, 我理解就是数据库自动维护着 <code>created_at</code></p>
<h4>结构的异同</h4>
<ul>
<li><p>都有 database, 并且都需要创建才能使用</p></li>
<li><p>mysql 有 table, Influxdb 有 measurement, 两者的角色差不多</p></li>
<li><p>measurement 下有 tag, tag 下才是 field.</p></li>
<li><p>measurement + tag = serie</p></li>
</ul>
<h4>查询语句的异同</h4>
<ul>
<li><p>CRUD 中 Influxdb 只有 C R D</p></li>
<li><p>Influxdb 对正则表达式的支持. 例如 <code>SELECT "value" FROM /kong_sms_request_status_*/</code></p></li>
<li><p>tag 跟 field 都可以用于 where 查询</p></li>
<li><p>Influxdb 有更丰富的聚合查询</p></li>
</ul>
<p>当然, 两种类型的数据库的主要用途不同, 对比只是用于方便上手, 并非对比两者优劣.</p>
<h5>示例</h5>
<pre><code>> create database "demo"
> show databases
name: databases
name
----
_internal
demo
> use "demo"
Using database demo
> insert hello,tag_alpha=2 value=3
> insert hello,tag_alpha=2 value=3
> insert hello,tag_alpha=4 value=5
> insert hello,tag_alpha=4 value=6
> show measurements;
name: measurements
name
----
hello
> select * from hello
name: hello
time tag_alpha value
---- --------- -----
1503037127485600991 3 3
1503037249575451262 2 4
1503037384953683603 4 5
1503037626342109770 4 6
> select * from hello where tag_alpha='2'
name: hello
time tag_alpha value
---- --------- -----
1503037249575451262 2 4
> select * from hello where tag_alpha='4' and value=6
name: hello
time tag_alpha value
---- --------- -----
1503037626342109770 4 6</code></pre>
<h3>效果</h3>
<p>给一张公司内部署之后的面板图<br><img src="/img/remote/1460000011082391" alt="Alt text" title="Alt text"></p>
<h4>友情链接</h4>
<p><a href="https://link.segmentfault.com/?enc=GYPbaJnB%2BAiOLvcbcW2%2FyA%3D%3D.%2FjNtzJa%2FKldj8v1HsZpvg1djyCzTxP2Hbm%2B8xXH28a6TrsBCDkeQwZne5sqa9kBVoK2CgaupACjCYugQ421UQQ%3D%3D" rel="nofollow">RunnerLee</a>: fastD 贡献者之一</p>
FastD 最佳实践三: 构建API网关
https://segmentfault.com/a/1190000010676390
2017-08-15T17:43:06+08:00
2017-08-15T17:43:06+08:00
黄总
https://segmentfault.com/u/janhuang
1
<p>构建完成 API 服务,配置中心之后,架构图大致如下:</p>
<p><img src="/img/remote/1460000010676395" alt="" title=""></p>
<h4>我们为何需要网关</h4>
<p>引用 <a href="https://link.segmentfault.com/?enc=xdUCZ%2BNXW9fhYrssjD6vTg%3D%3D.r09lrEp7cPePPUUL%2Fd88jTlwGUst6z2iuy7u312OlSOUEUTFu%2By2tZOuLe9czrU4hC8nWKYPXCG%2Fxi%2F4C8ATug%3D%3D" rel="nofollow">别人</a> 的一句话:</p>
<blockquote><p>我们总是听到编排这个词,所以我喜欢这张幻灯片 – 它展示了一个乐队,然后有个指挥家,下面一堆人(微型服务)演奏自己的乐器。这个指挥家(API网关)可以以某种方式来协调我们的架构如何处理请求。</p></blockquote>
<p>我们需要将业务或服务放置在网关背后,由网关统一处理请求入口,本身由多个入口的处理变成了一个入口,由网关进行统一调度。</p>
<blockquote><p>有一个很nice的事情,就是API网关让我们的客户端不用再需要知道和关心模块的地址(address)了。网关负责来搞这些事情,你只需要知道网关就好了。你可以去改变实现而且还可以改变API接口。不过通常来说,你改变接口后,会增加客户端出问题的风险。</p></blockquote>
<p>还有很多有趣的功能,有兴趣的朋友可以参考:</p>
<ol>
<li><p><a href="https://link.segmentfault.com/?enc=K%2FA8azAf8lFyE5mi4VCYeQ%3D%3D.7Sl0va%2BBeQePHm5MmT%2F3VVmmh5j8%2BZV5%2Fz0OGONiqFUfVpkrO52R6Y34bR78E9G2wjbGLil47JIoDjcpe1XtLQ%3D%3D" rel="nofollow">微服务与API 网关(上): 为什么需要API网关?</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=n5txgbBvhZIy19BZvKBGjg%3D%3D.QqlhAOJhVa%2FRKXP0d%2FwLqAuHwsG4Tv%2BDpAlXV3V9EHVs8TzWNw2B8CnfM89MRYhvhDqh5w9Pk2vOy0VI0hWGfw%3D%3D" rel="nofollow">微服务与API 网关(下): Kong能为我们做什么?</a></p></li>
</ol>
<h4>构建</h4>
<p>由于本人使用 ubuntu 虚拟机,所以此处只介绍 ubuntu 下的安装,其他安装方式可参考: <a href="https://link.segmentfault.com/?enc=M0Swg7VU2AMcFPbZ9qkk3A%3D%3D.2tk4VMeXLmMCx73yFpX1gsvioOx4dMBTHZvxRwjV0f8%3D" rel="nofollow">kong installation</a></p>
<h5>安装 postgresql</h5>
<pre><code>sudo add-apt-repository "deb http://apt.postgresql.org/pub/repos/apt/ xenial-pgdg main"
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-9.6</code></pre>
<p>安装 postgresql 完成后,初始化数据库:</p>
<pre><code>su postgres
psql
CREATE USER kong; CREATE DATABASE kong OWNER kong;</code></pre>
<p><code>ctrl + D</code> 退出</p>
<h5>安装 kong</h5>
<pre><code>$ sudo apt-get update
$ sudo apt-get install openssl libpcre3 procps perl
$ sudo dpkg -i kong-0.10.3.*.deb
</code></pre>
<pre><code>$ kong start
# Kong is running
$ curl 127.0.0.1:8001</code></pre>
<p>这个过程可能会出现: <code>[postgres error] FATAL: password authentication failed for user "kong"</code></p>
<p>查看 pgsql 配置文件所在位置,因为我是 <code>apt-get install</code> 的,因此,存储位置在 <code>/etc/postgresql/9.6/</code></p>
<p>修改 <code>/etc/postgresql/9.6/main/pg_hba.conf</code> 文件,找到IPv4 配置项,约92行,修改</p>
<pre><code>host all all 127.0.0.1/32 md5
=>
host all all 127.0.0.1/32 trust</code></pre>
<p>重新启动即可。</p>
<pre><code>$ sudo kong start</code></pre>
<h4>整合 FastD API</h4>
<h5>1. 添加 API 到网关</h5>
<pre><code class="curl">curl -i -X POST \
http://127.0.0.1:8001/apis/ \
--data 'name=example-fastd' \
--data 'hosts=fastd.com' \
--data 'upstream_url=http://127.0.0.1:9527'</code></pre>
<p>添加 API Server 到网关,成果会返回 201 状态吗表示创建成功 (Created)</p>
<h5>2. 检查 API 状态</h5>
<p>获取列表: <code>http://127.0.0.1:8001/apis/</code></p>
<p>发起请求.</p>
<pre><code class="curl">curl -i -X GET \
--url http://127.0.0.1:8000/ \
--header 'Host: fastd.com'</code></pre>
<p>此时此刻你会发现已经成功使用 kong 对其进行代理,至此之外,已经完成了基本的网关接入。最终架构图如下:</p>
<p><img src="/img/bVSXzQ?w=852&h=752" alt="图片描述" title="图片描述"></p>
<h4>插件</h4>
<p>kong 提供的插件非常丰富,因为也是基于 lua 的,因此你可以使用 openresty 对其进行扩展,开发更加贴近业务的应用。</p>
<p>插件文档: <a href="https://link.segmentfault.com/?enc=oFyVfrhJmDqm28j1ZPxOtA%3D%3D.%2BwT%2FeZJRAxQbQCe55nfQuN3w5q3gL6CokKCBFvnfHMcWv9s698LjfuH638L3t9429c1iDjwVVEeW4XYY4nYnwg%3D%3D" rel="nofollow">点击</a></p>
<p>更多可看 kong 官方文档。</p>
FastD 最佳实践二: 构建配置中心
https://segmentfault.com/a/1190000010591858
2017-08-10T12:14:31+08:00
2017-08-10T12:14:31+08:00
黄总
https://segmentfault.com/u/janhuang
1
<p>过去专门做了一篇文档来构建配置中心,基于 <a href="https://link.segmentfault.com/?enc=eR4eEI%2Boi8h%2BCY0ggGHPNQ%3D%3D.unYdKrSy8Y0pGa93NinsPji7HZSNGoLUlZgVVr3rO%2BI%3D" rel="nofollow">zookeeper</a> 的配置中心。</p>
<p>环境要求及构建步骤可参考: <a href="https://segmentfault.com/a/1190000008949515">QConf搭建配置中心</a></p>
<p>随着业务增长,部署的机器可能会随着增长,增加配置难度和维护难度。配置会因为机器的增多而变得更加容易出错,为了解决这个问题,于是我们引入了 360 开发的 Qconf 来解决这个问题,目前已经稳定用于线上环境当中。</p>
<h4>安装 qconf 扩展包</h4>
<pre><code>composer require fastd/qconf-service-provider -vvv</code></pre>
<p>扩展包有点特殊,不需要任何的注册操作,当执行完 composer 依赖之后,会自动加载辅助函数,仅需对配置中心进行读取配置即可。</p>
<p>提供两个函数:</p>
<p><code>qconf_get_value</code> 获取对应节点值</p>
<p><code>qconf_get_values</code> 获取对应节点值数组</p>
<h4>修改配置文件</h4>
<p><code>config/config.php</code></p>
<pre><code class="php"><?php
return [
'demo' => qconf_get_value('/demo/test', null, null, 'abc')
];</code></pre>
<p>值得注意的是,如果万一不小心,qconf 出现错误或者异常无法运行的时候,则需要保留一个默认配置项,这个小动作可能会在你系统出现异常的时候救你一命。</p>
<h4>测试配置中心</h4>
<p>完成基础配置后,需要对配置中心进行简单的测试。</p>
<pre><code>php bin/console config:dump config</code></pre>
<p>结果会将配置文件进行输出,来确认是否可用。</p>
<p>最终架构图如下:</p>
<p><img src="/img/bVSBAu?w=832&h=734" alt="图片描述" title="图片描述"></p>
<p>无论扩展多少个业务应用,仅需要一个配置中心即可完成多处配置修改。</p>
FastD 最佳实践一: 构建 API
https://segmentfault.com/a/1190000010551981
2017-08-08T11:58:59+08:00
2017-08-08T11:58:59+08:00
黄总
https://segmentfault.com/u/janhuang
2
<h2>FastD 最佳实践一: 构建 API</h2>
<p>FastD 是一个专门针对 API 应用层而生的一个 PHP 应用框架,提供良好的中间件,路由以及支持 swoole 扩展运行,从而具体良好的性能条件。</p>
<h5>创建项目</h5>
<pre><code>composer create-project fastd/dobee api -vvv</code></pre>
<p>创建一个为 API 的项目。</p>
<h5>运行第一个程序</h5>
<p>进入命令行模式</p>
<pre><code>php -S localhost:9876 -t web</code></pre>
<p>访问 <code>localhost:9876</code></p>
<pre><code class="json">{
"msg":"hello dobee"
}</code></pre>
<h5>执行流程</h5>
<p>点击: <a href="https://link.segmentfault.com/?enc=LR6ac%2F4aU7D4yTvR3%2F%2BxjQ%3D%3D.l1w0RbFbudqA7GnQ%2Bcnfr8zA1nbah88nSqCQFXUoU%2BY%3D" rel="nofollow">FastD设计详解</a></p>
<h5>实现第一个路由</h5>
<h6>1. 创建"控制器"</h6>
<p>通过命令行</p>
<pre><code>php bin/console controller:create {name}</code></pre>
<p>命令行会自动创建 CURD 多个操作方法,由开发者手动添加操作逻辑。</p>
<p>手动创建</p>
<p>MeController.php</p>
<pre><code class="php"><?php
namespace Controller;
use FastD\Http\ServerRequest;
class MeController
{
public function me(ServerRequest $request)
{
return json([
'name' => 'janhuang'
]);
}
}</code></pre>
<h6>2. 添加路由地址</h6>
<pre><code class="php"><?php
route()->get('/', 'WelcomeController@welcome');
route()->get('/hello/{name}', 'WelcomeController@sayHello');
route()->get('/who', 'MeController@me');</code></pre>
<h6>3. 调用</h6>
<pre><code>curl -i http://127.0.0.1:9876/who</code></pre>
<blockquote><p>response</p></blockquote>
<pre><code class="json">{
"name": "janhuang"
}</code></pre>
<p>完成最第一个路由。</p>
<h5>给应用添加单元测试</h5>
<p>我仍热认为测试是一个非常重要的环节,也是一个优秀开发者一个重要品质之一。</p>
<p>如果其中涉及数据库测试,可以参考: <a href="https://link.segmentfault.com/?enc=upAmqsbIPz8MJXH9FhAhbw%3D%3D.l%2Fb8SZlXu4FzxxYpEbohXIR4gcL8w84K1qnSqsRbITdQ0Xui38ed3k%2BMvep2JdHN2YTroHA7rKMy8E7k5RshL6Jdge6x93BrR6GyvApvqWg%3D" rel="nofollow">数据库测试</a></p>
<pre><code class="php"><?php
namespace Testing;
use FastD\TestCase;
class WelcomeControllerTest extends TestCase
{
public function testSayHello()
{
$request = $this->request('GET', '/');
$response = $this->app->handleRequest($request);
$this->isSuccessful($response);
}
}</code></pre>
<p>可以给你的应用API添加格式各样的测试,来验证程序的有效性。</p>
<h5>给 API 添加公共缓存</h5>
<p><strong>何为公共缓存?</strong></p>
<p>公共缓存的灵感来自于 <a href="https://link.segmentfault.com/?enc=9c73ZqTr3XkfCTRGcMOq%2FA%3D%3D.Ur0qA8qgg2RiuvzEkR6ztNlpbrAqodMXixl6r8j3ZCs%3D" rel="nofollow">varnish</a>,当用户发起非 <code>GET</code> 请求的时候,进行缓存处理并返回(如果有的话),其他请求一律穿透,交给底层处理。</p>
<p>缓存也是一个前置中间件,使用方式与日常操作保持一致。</p>
<p>文档: <a href="https://link.segmentfault.com/?enc=ki3V8P7llh3tqJWT9kNvXA%3D%3D.cTfySXr33C8TWyItCcorqSQ4Q6f4%2Fs5nPr36GVA32i%2BGya%2BSKUAvQtvT%2F%2FsdDkJZeTnZ8hWKrgCZBf6ZolJUenqa%2B8QCuP1lr2dQ82WkCi0%3D" rel="nofollow">中间件</a></p>
<p><code>config/app.php</code></p>
<pre><code class="php"><?php
return [
// code...
/**
* Http middleware
*/
'middleware' => [
// code
'common.cache' => [\FastD\Middleware\CacheMiddleware::class]
],
];</code></pre>
<p><code>config/routes.php</code></p>
<pre><code class="php"><?php
route()->get('/', 'WelcomeController@welcome');
route()->get('/hello/{name}', 'WelcomeController@sayHello');
route()->get('/who', 'MeController@me')->withMiddleware('common.cache');</code></pre>
<p>完成配置后,请求路由地址</p>
<pre><code>curl -i http://localhost:9876/who
HTTP/1.1 200
Host: localhost:9876
Connection: close
X-Powered-By: PHP/7.0.0
Content-Type: application/json; charset=UTF-8
X-Cache: ee4d94f352cb03116b61ce9158720ebf
Expires: Tue, 08 Aug 2017 10:58:21 GMT</code></pre>
<p>会产生 <code>X-Cache</code> 新的响应头,用于代表缓存生效。</p>
<h5>Basic Auth</h5>
<p>大部分 API 中,都需要对请求来源进行一定的鉴权处理,由于框架已经集成了简单的 Basic auth,使用方法与上述保持一致。</p>
<pre><code class="php"><?php
route()->get('/', 'WelcomeController@welcome');
route()->get('/hello/{name}', 'WelcomeController@sayHello');
route()->get('/who', 'MeController@me')->withMiddleware(['basic.auth', 'common.cache']);</code></pre>
<h5>为 API Server 加速</h5>
<p>众所周知,<a href="https://link.segmentfault.com/?enc=yxSJq3Uj4IHjBKHdl4%2BVvg%3D%3D.ryXFDnpd%2F%2FlVAD%2FquZYv77XQjD7VL72w0p%2FcqS%2FcpmM%3D" rel="nofollow">swoole</a> 为 PHP 提供了良好的性能体验和网络通信体验,而框架中也无缝整合 swoole,为框架、服务提供良好体验做了一个铺垫,现在,你只需要配置IP、端口、swoole常用配置,即可无痕启动以 swoole 服务器,为你的 API Server 进行加速处理。</p>
<p><code>config/server.php</code></p>
<pre><code class="php"><?php
/**
* @author jan huang <bboyjanhuang@gmail.com>
* @copyright 2016
*
* @link https://www.github.com/janhuang
* @link http://www.fast-d.cn/
*/
return [
'host' => 'http://'.get_local_ip().':9527',
'class' => \FastD\Servitization\Server\HTTPServer::class,
'options' => [
'user' => 'nobody',
'group' => 'nogroup',
'pid_file' => __DIR__ . '/../runtime/pid/' . app()->getName() . '.pid',
'log_file' => __DIR__ . '/../runtime/logs/' . app()->getName() . '.pid',
'log_level' => 5,
'worker_num' => 10,
'task_worker_num' => 20,
],
'processes' => [
],
'listeners' => [
],
];</code></pre>
<p><code>get_local_ip</code> 函数为了默认或者本地内网ip,如果你的API是对外开放的,请修改为您的外网ip或者 <code>0.0.0.0</code>。</p>
<p><code>options</code> 参数为 swoole 扩展配置参数。请参考: <a href="https://link.segmentfault.com/?enc=8%2BtUfoQQQYsc7MunAb4KeA%3D%3D.4dcvxGAd0qlGAefKMyCsPBGvkWchSkGaH6XTqPDyLXUKU9v7bWwdUBt6M5uu0yZK" rel="nofollow">swoole配置</a></p>
<h6>启动</h6>
<pre><code>php bin/server start</code></pre>
<h6>守护进程</h6>
<pre><code>php bin/server start -d</code></pre>
<h6>停止</h6>
<pre><code class="php">php bin/server stop</code></pre>
PHP 单元测试与数据库测试
https://segmentfault.com/a/1190000008953673
2017-04-05T17:57:45+08:00
2017-04-05T17:57:45+08:00
黄总
https://segmentfault.com/u/janhuang
7
<p>我总感觉 PHP 的开发者们并没有对 PHP 的质量有所追求,可能是因为 PHP 的机制问题吧,让大部分的开发者总以为浏览器访问就没有问题,所以很多时候,做 PHP 开发的,就没有单元测试的这些概念了。能不能有点追求?</p>
<p>我个人也是 PHP,但同时我也比较讨厌那些完事就算了的开发者,作为一个开发者,或者说是一个产品的经手人,就应该用心地去做好每个细节,一次比一次要更好。</p>
<p>但是做单元测试,质量检查,是需要一定的时间和人力投入的,但我敢保证地说,你花时间投入的,绝对不会是没用的,一定对你,对项目来说,是一个质的提升,只要你肯投入时间用心去做。</p>
<p>屁话说太多了,那接下来简单讲讲 phpunit 吧,<a href="https://link.segmentfault.com/?enc=U%2FTA4doJzGFVqp%2B9Imtwqw%3D%3D.LV6NoQ4IV5k7jcNmeVyt0VekrwZ%2BiS%2B%2Fhqkm%2FsDa%2FSoDBpjp%2BHIpBpG81o4XCEN16sdaT34ji3v9k%2FCBkaVUIA%3D%3D" rel="nofollow">官网</a>。</p>
<p>因为我们习惯用 <code>composer</code>,所以我们也使用 <code>composer</code> 安装吧。</p>
<h3>安装与配置</h3>
<pre><code class="php">$ composer require phpunit/phpunit -vvv </code></pre>
<p>安装完 phpunit,bin 执行脚本会创建在 <code>vendor/bin</code> 目录下,命名为 <code>phpunit</code>, 执行 <code>php vendor/bin/phpunit</code> 执行测试脚本</p>
<p>配置 bin 目录:</p>
<pre><code class="json">{
"config": {
"bin": "bin"
}
}</code></pre>
<p>配置 bin 目录产生的目录,执行 <code>php bin/phpunit</code> 脚本开始测试。</p>
<p><code>phpunit</code> 可以配置在当前执行路径添加一个配置文件 <code>phpunit.xml.dist</code> 或者 <code>phpunit.xml</code>,内容如下:</p>
<pre><code class="xml"><phpunit
colors="true"
bootstrap="./vendor/autoload.php"
>
<testsuites>
<testsuite>
<directory>dir1</directory>
</testsuite>
<testsuite>
<directory>dir2</directory>
</testsuite>
</testsuites>
</phpunit></code></pre>
<p>可以通过配置目录和初始化信息,让脚本自动执行对应的测试用例。</p>
<h3>基础使用</h3>
<p>使用 PHPUnit 创建我们的测试用例:</p>
<pre><code class="php"><?php
class DemoTest extends PHPUnit_Framework_TestCase
{
public function testPushAndPop()
{
$stack = [];
$this->assertEquals(0, count($stack));
array_push($stack, 'foo');
$this->assertEquals('foo', $stack[count($stack)-1]);
$this->assertEquals(1, count($stack));
$this->assertEquals('foo', array_pop($stack));
$this->assertEquals(0, count($stack));
}
}</code></pre>
<p>类名需要以 *Test 结尾,继承 <code>PHPUnit_Framework_TestCase</code>。需要测试的方法需要一 test 开头,表明是一个测试方法。</p>
<p>一般常用测试无非就是 "断言",说白了,就是看看产生的结果是不是符合预期,如果是,那就证明,已经测试通过,否则,失败,说明逻辑处理,存在一定的差异,导致不符合预期。</p>
<p>更多的测试使用方法请看官网用例: <a href="https://link.segmentfault.com/?enc=9VlQnxA%2BWo3NjdwDkkgB4A%3D%3D.f2w8oEkCkenmwGxNX9t7EWuZgzonyMIyJsTZSbQ%2FwXb6WKEQJIFwF7S%2B9DVOJABQ" rel="nofollow">PHPUnit</a></p>
<h4>初始化</h4>
<p>当我们的测试对象继承了 PHPUnit 后,初始化方法就需要使用它本身提供的 setUp 方法,代表类初始化,可以在初始化方法中初始化一些资源,或者加载。</p>
<h3>数据库测试</h3>
<p>除了以上基础的测试之外,关键一点应该在动态的数据,需要去测试吗,如果需要,那应该怎么去测试? 生产环境,也需要这样测试? 这个曾经困惑这我的问题,已经解开。</p>
<p>解答:</p>
<ol>
<li><p>composer 中,有 --no-dev 选项,用来部署生产环境,避免测试环境的数据或者代码跑在了生产环境下。并且生产环境上数据库操作是没有很高权限的操作,要是有的话,你得回去面壁思考一下了。</p></li>
<li><p>dbunit 每次测试都重置数据,其实在生产环境下,就重置不了了,第一个是composer --no-dev 已经没有执行权利了,要是有,数据库已经不允许清空操作了。</p></li>
<li><p>要是生产环境不需要这些东西,那么应该怎么测试。其实需要有一个模拟生产环境的测试环境,去模拟生产环境测试,当所有测试都OK没有问题,那么就可以发布到生产环境上,要是严格一些,生产环境也是需要一轮测试。</p></li>
</ol>
<pre><code>$ composer require phpunit/dbunit -vvv </code></pre>
<p>更多测试可看: <a href="https://link.segmentfault.com/?enc=zd0%2F7aubN6ZUYaKG7KTXOg%3D%3D.vPq99IxuGO2ihDFPfTtOwJuDUpghl%2FLNYck1VVC9ycKArMM2DhScbCfs1oadR%2Fz1ls%2BRMBUQS2uqY7XsxU6BrQ%3D%3D" rel="nofollow">数据库测试</a></p>
<pre><code class="php"><?php
class DBTest extends PHPUnit_Extensions_Database_TestCase
{
/**
* @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
$pdo = new PDO('mysql::dbname=test;host=127.0.0.1', 'user', 'pass');
return $this->createDefaultDBConnection($pdo, ':memory:');
}
/**
* @return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
public function getDataSet()
{
return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/guestbook-seed.xml');
}
}</code></pre>
<p><code>getConnection</code> 方法是获取数据库连接,继承数据库测试后,必须实现的一个方法,并且需要返回 <code>PHPUnit_Extensions_Database_DB_IDatabaseConnection</code> 对象,可以仿照上述写法即可。</p>
<p><code>getDataSet</code> 方法是数据集,在创建数据库测试的时候,自动填充,测试,和删除。他执行的流程是,每个测试用例,都会填充一次,以保证不会被其他测试用例影响。当当前测试用例测试完成后,会 <code>truncate</code> 掉填充的数据。</p>
<p>数据集支持挺多种方法,可以自定义数组,yml,xml,可以根据自己的使用习惯,自定义填充数据。数据集可看: <a href="https://link.segmentfault.com/?enc=HLap%2F5%2FkMbKqCb6MATGCNQ%3D%3D.v99fNzZvkkGtkzBdGLTHdJWMiDPRTT%2BlK42zDI9K0X7QJV5xmHDQJmymYAgwVA%2By2RwzMfHk5fITEx1xiV%2Fjk3tWr1sN8T9W96469e7KT8N7y8Vp0sISpTKzblf%2B1aWvl4uoFpt%2BdIXgK%2BSFApCD6A%3D%3D" rel="nofollow">点我</a></p>
<p>执行脚本 <code>php vendor/bin/phpunit</code></p>
<p>然后去对应查看自己的数据表,是否多了一些填充的数据呢?</p>
<h4>抽象自己的数据库测试类</h4>
<p>在很多情况下,我们的业务可谓是各种各样吧,倘若 phpunit 提供的数据库测试还不能满足或者不够方便的时候,就需要扩展自己的数据库测试,来达到自己想要的效果。</p>
<p>幸好,phpunit 提供了灵活的扩展操作(肯定啦,别人肯定不会像你这么傻,写死吧。哈哈),我们可以很容易地去实现自己的数据库测试类。</p>
<pre><code class="php"><?php
abstract class MyApp_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
// 只实例化 pdo 一次,供测试的清理和装载基境使用
static private $pdo = null;
// 对于每个测试,只实例化 PHPUnit_Extensions_Database_DB_IDatabaseConnection 一次
private $conn = null;
final public function getConnection()
{
if ($this->conn === null) {
if (self::$pdo == null) {
self::$pdo = new PDO('mysql::dbname=test;host=127.0.0.1', 'user', 'pass');
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, ':memory:');
}
return $this->conn;
}
}</code></pre>
<p>至今为止,完成了最基础和入门的单元测试和数据库测试,最终数据库无非就是查看数据增删改查是否和预期一样。所以,配置完数据库测试后,就可以走回第一步,编写你的测试用例,断言测试了。</p>
<p>恭喜你,你已经构建完自己的单元测试环境了。接下来需要做的是,提高易用性,测试覆盖率。我只能帮你到这里了,接下来的路,自己走吧。</p>
QConf搭建配置中心
https://segmentfault.com/a/1190000008949515
2017-04-05T14:13:24+08:00
2017-04-05T14:13:24+08:00
黄总
https://segmentfault.com/u/janhuang
12
<p>今天来跟大家分享的是奇虎360开源的 <a href="https://link.segmentfault.com/?enc=mzBLWo7d8MLcL7nc8l7wQg%3D%3D.6BDJJP8ffv7wlxv1vwMFLCzTQ0bNKvB0JDh6Kot2TtC0Cd87RUeQw8FIua%2FKaBjm" rel="nofollow">QConf</a> 配置中心。</p>
<p>为什么我们需要做这么一件事情?</p>
<p>因为遇到了,当业务分布较广,配置分布较广的时候,就会很容易地出现一些问题,比如做了负载均衡,需要调整一下应用配置。刚好改漏了一台机,就偶尔出现一些问题,排查起来也是很吃力的。</p>
<p>QConf 的架构: <a href="https://link.segmentfault.com/?enc=mDkVGXP1k2%2BIVdFOWnjEww%3D%3D.NEsVTKzUROf18uNYFesN6gGpqlbMPPUnZuOsOsR48QeYWY9hDXDKfzZHkanZqIlTZBeWW7NvsuOMjWqhnNYggw%3D%3D" rel="nofollow">QConf架构</a></p>
<p>除了 QConf 本身提供的工具之外,我们用到了一个有掌阅科技开发的 <a href="https://link.segmentfault.com/?enc=NONKkuDm%2BRc5Wu0KfmTx7A%3D%3D.Kv%2Fu%2FCOX4k44T%2BkiX7cBI7n7sF%2BYTSUe98h8KAbRRkOg5S4RIYGNtg1qUPP4Q53%2B" rel="nofollow">zkdash</a> zookeeper 管理工具。</p>
<p>个人理解: </p>
<p>QConf 可以说是一个 zookeeper 的客户端,通过 agent 去 watch zookeeper 的变化,数据存在本机,当 zookeeper 发生变化是,自动改变本地数据,来达到同步更新的效果。</p>
<p><img src="/img/remote/1460000008949518?w=404&h=639" alt="结构" title="结构"></p>
<p>这也就是为什么 QConf 需要搭配 zookeeper 去使用的原因之一吧。</p>
<h3>QConf 服务端</h3>
<p>QConf使用ZooKeeper集群作为服务端提供服务。众所周知,ZooKeeper是一套分布式应用程序协调服务,根据上面提到的对配置内容的定位,我们认为可以将单条配置内容直接存储在ZooKeeper的一个ZNode上,并利用ZooKeeper的Watch监听功能实现配置变化时对客户端的及时通知。 按照ZooKeeper的设计目标,其只提供最基础的功能,包括顺序一致,原子性,单一系统镜像,可靠性和及时性。</p>
<p>我们选择Zookeeper还因为它有如下特点:</p>
<p>1、类文件系统的节点组织</p>
<p>2、稳定,无单点问题</p>
<p>3、订阅通知机制</p>
<p>关于Zookeeper,更多见:<a href="https://link.segmentfault.com/?enc=FjDESXFI8FV55%2BmOGFAqAA%3D%3D.pPCGcyO80EX3JVPlcxd5pPMYbQJfBmlO4Ic1Ds1PgPs%3D" rel="nofollow">https://zookeeper.apache.org/</a></p>
<h3>QConf 客户端</h3>
<p>因为ZooKeeper在接口方面只提供了非常基本的操作,并且其客户端接口原始,所以我们需要在QConf的客户端部分解决如下问题:</p>
<p>1、降低与ZooKeeper的链接数 原生的ZooKeeper客户端中,所有需要获取配置的进程都需要与ZooKeeper保持长连接,在生产环境中每个客户端机器可能都会有上百个进程需要访问数据,这对ZooKeeper的压力非常大而且也是不必要的。</p>
<p>2、本地缓存 当然我们不希望客户端进程每次需要数据都走网络获取,所以需要维护一份客户端缓存,仅在配置变化时更新。</p>
<p>3、容错 当进程死掉,网络终端,机器重启等异常情况发生时,我们希望能尽可能的提供可靠的配置获取服务。</p>
<p>4、多语言版本接口 目前提供的语言版本包括:c,php,java,python,go,lua,shell。</p>
<p>5、配置更新及时 可以秒级同步到所有客户端机器。</p>
<p>6、高效的配置读取 内存级的访问速度。</p>
<h3>安装</h3>
<p>了解完基础的知识和原理,就开始动手实践想法和操作吧,理论要和实践结合,不然就瞎扯淡了。</p>
<h4>环境</h4>
<p>Linux: Ubuntu 16.04</p>
<h4>安装 zookeeper</h4>
<p>安装包地址: <a href="https://link.segmentfault.com/?enc=o%2F8t3P%2FVw0NI30imF17QeA%3D%3D.mNhUU0ETjDOjdZrWDgNIOnvN30lrZWah4GRuki0pez7BkAi6Rmwe8WYL5sFE%2ByK8" rel="nofollow">zookeeper</a></p>
<pre><code class="shell">$ sudo wget http://apache.stu.edu.tw/zookeeper/zookeeper-3.4.9/zookeeper-3.4.9.tar.gz
$ sudo tar -zxvf zookeeper-3.4.9.tar.gz</code></pre>
<p>解压完成后,是可以直接运行 zkServer 的,但是需要配置自己的基础信息。</p>
<pre><code class="shell">$ cd zookeeper-3.4.9
$ sudo cp conf/zoo_sample.cfg conf/zoo.cfg</code></pre>
<p>启动 zkServer</p>
<pre><code class="shell">$ cd ../
$ ./bin/zkServer.sh start</code></pre>
<p>默认监听: <code>0.0.0.0:2181</code></p>
<p>测试服务是否启动: <code>telnet 127.0.0.1 2181</code> 输入 <code>stat</code></p>
<pre><code>Zookeeper version: 3.4.9-1757313, built on 08/23/2016 06:50 GMT
Clients:
xxx
Latency min/avg/max: 0/0/51
Received: 252717
Sent: 252716
Connections: 5
Outstanding: 0
Zxid: 0x13
Mode: standalone
Node count: 10</code></pre>
<p>会输出上述信息,服务器启动成功.</p>
<p>然后创建一下 zookeeper 测试数据:</p>
<pre><code class="shell">sh zkCli.sh
create /demo demo
create /demo/confs confs
create /demo/confs/conf1 111111111111111111111
create /demo/confs/conf2 222222222222222222222
create /demo/confs/conf3 333333333333333333333</code></pre>
<h4>QConf</h4>
<p>安装方式可以查看官方 <a href="https://link.segmentfault.com/?enc=qHZO9nDy%2BRecMQIYLxJRJQ%3D%3D.s3zoGCvEenBrCHIDqfD8RhKf2iWBTvQVyjUuNmmox9eBk%2B8zumd0oyQoilvID%2BM3Y69aOWU4QG7luK2uKM0ARaZ8HyblMjXJ6%2BAVYIUdRCPI4fmMcHL92idW2YuTAVwiy8HB%2FUbvaM%2BskojCjhQlqg%3D%3D" rel="nofollow">wiki</a></p>
<p>安装 <a href="https://link.segmentfault.com/?enc=M%2BeAz4Uceu5gdGP7C8XrZw%3D%3D.fE7gql9YdsR0M%2Fewjp9nn9VPpnVB%2BK0Rp2pC1XRyykXEUYChkUAHZ9g6dHCGtmuR" rel="nofollow">FAQ</a></p>
<p>安装完 qconf 后,进入 qconf 安装目录,调整 idc.conf</p>
<pre><code class="shell">$ cd conf/
$ sudo vim idc.conf</code></pre>
<p>输入自己的 zookeeper 服务器地址,多个用 <code>,</code> 隔开:</p>
<pre><code>#[zookeeper]
############################################################################
# QCONF config #
############################################################################
# all the zookeeper host configuration.
#[zookeeper]
zookeeper.test=zookeeper host</code></pre>
<p>保存即可。</p>
<p>启动 QConf agent。</p>
<blockquote><p>在 ubuntu 下使用 bash 去启动</p></blockquote>
<pre><code class="shell">$ sudo bash ./bin/agent-cmd.sh start</code></pre>
<p>启动成功,查看 QConf 命令。</p>
<pre><code class="shell">qconf get_conf /demo/confs/conf1
qconf get_batch_keys /demo/confs</code></pre>
<p>如果返回对应的信息,那几说明可以正常获取数据了。如果失败,就去 logs/ 目录查询错误信息,对应修改。</p>
<h5>安装 PHP 扩展</h5>
<pre><code class="shell">/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config --with-libqconf-dir=/usr/local/qconf/include --enable-static LDFLAGS=/usr/local/qconf/lib/libqconf.a
make
make install</code></pre>
<p>qconf 配置在 qconf 安装的目录中,根据路径找到对应的依赖包即可。</p>
<p>安装完后,添加到 php.ini,使用 <code>php --ini</code> 查看配置文件位置。</p>
<p>查看扩展安装:</p>
<pre><code class="shell">php -m | grep qconf</code></pre>
<p><a href="https://link.segmentfault.com/?enc=qZNbnRzEOSSiAtjI0A3C8g%3D%3D.9jVCUe6SsglTK3jZKg7Q8tVcBN3Wmp2NATnwDnHDiP%2FRfcHeqKHEp8hWOzcxOwAhp8mElF%2FW75XajHFMTFcgwg%3D%3D" rel="nofollow">QConf 操作 API</a></p>
<p>安装完 QConf 和 zookeeper,安装我们的管理后台。</p>
<h4>zkdash</h4>
<blockquote><p>需要 Python >= 2.7,如果是 centos65 的用户,需要升级自己的 Python 版本,升级方法请看: <a href="https://link.segmentfault.com/?enc=Z0GOU3x8geq0Zjewzt2lfg%3D%3D.zX1FCCjYy5XF3E%2BW82kq8nsryreWkC5gNxajGPLvI2hcMEXJZ1dwP3%2FTB0%2FTwfPbwEmCBZO31zw9rxFho0uBBg%3D%3D" rel="nofollow">Python升级</a></p></blockquote>
<p>地址: <a href="https://link.segmentfault.com/?enc=nLg2MY4wVHhV%2BYY98JoPXQ%3D%3D.cQH%2FAOAChbXgM1L7XGBcktVVvDpz0F8dfORztMc3HabtwY1Yu4XqNg7StRXrcsIM" rel="nofollow">zkdash</a></p>
<p>安装完 zkdash,配置对应节点,进行管理即可。</p>
<blockquote><p>zkdash Web管理服务默认是全网公开访问的,需要修改监听host和端口,修改方式</p></blockquote>
<pre><code class="shell">$ sudo vim init.py</code></pre>
<p>修改监听的 host 地址:</p>
<pre><code> 74 def main():
75 """主程序入口
76 """
77 logger.init_logger(LOG_ITEMS, suffix=OPTIONS.port)
78 application = Application()
79 http_server = tornado.httpserver.HTTPServer(application,
80 xheaders=True)
81 http_server.listen(OPTIONS.port, address="输入你需要监听的地址")
82 tornado.ioloop.IOLoop.instance().start()
83</code></pre>
<p>address 是新增的 host 参数。本身是没有的</p>
<p>恭喜你,你已经构建完自己的配置中心了。接下来需要做的是,提高性能,和稳定性,扩展 zookeeper。我只能帮你到这里了,接下来的路,自己走吧。</p>
<p>更多开源项目欢迎关注: <a href="https://link.segmentfault.com/?enc=WMovBQAdFrpNNvv8TRdCvg%3D%3D.TmZzAz02dDgMQqzBWAnpWXnhRp7VtTb6e0hrhymnxrE%3D" rel="nofollow">https://github.com/janhuang</a></p>
fastd 3.0 开发的那些事
https://segmentfault.com/a/1190000008275779
2017-02-07T16:52:05+08:00
2017-02-07T16:52:05+08:00
黄总
https://segmentfault.com/u/janhuang
1
<p>从上个版本到目前,真个开发已经持续快一年了。这个项目我已经一个人维护了将近2年,陆陆续续陆陆续续地,发布了好几个版本,从0.1.0 的定稿版本 Dobee,到 fastd1.0, fastd2.0,到今天的3.0,经历了太多太多的迷茫与坚持。打心里地说,当时和现在,心态差别太多,每天都花时间去投入,感觉又没有看到回报,每当我去尝试去坚持做这个事情的时候,就会想,到底该不该继续花时间去做?已经工作3个你念头,是否继续下去,还会一无所处?当我不去做这个事情的时候,又是另外一种心态,该不该放弃,还是继续去做,有始有终?始终地,都会陷入这个无线的循环当中,甚至有时候会非常讨厌这种状态。</p>
<p>坚持了这么长的时间,心里总有些不甘心,总想给他来个不那么失败的结局。这就是我为何还不放弃的原因。</p>
<p>比较庆幸的是,任何事情,我还有自己的想法,不会随波逐流,也不随意批判。不管你做的东西如何,有人支持,就肯定有人批判,但总是好事,因为你影响到了别人。</p>
<p>每个框架都有他存在的道理,人也亦如此,有人说,PHP 性能差,有人说,PHP 太烂,亦有人说,PHP 不适合做后端底层开发。那其实他们也没错,但是最大的问题是在于他个人自己本身,这些问题我个人是保持一种视而不见的态度去看待。</p>
<p>有太多太多的感触,也不知道如何描述,只能放在心里。</p>
<p>关于技术上的问题,欢迎朋友们来找我互相学习交流。</p>
<p>项目地址 <a href="https://link.segmentfault.com/?enc=GS2Mn0d2yh0iojmpbH7u7A%3D%3D.VoBeNEj%2FKlPWG55RMvbhHrEDJnLxlW95P%2Fvt%2BTLhOa1%2BwHu2uYOAv6GsoQsrdpG0" rel="nofollow">FastD</a></p>
寻找我梦,再见2016
https://segmentfault.com/a/1190000007978655
2016-12-31T18:47:12+08:00
2016-12-31T18:47:12+08:00
黄总
https://segmentfault.com/u/janhuang
1
<h5>加过最晚的班,经历过最多人的团队,吃过最贵的饭,做过最多的功能,做过最复杂的架构,做过最艰难的抉择,法国最多的脾气,鬼知道我经历了什么。</h5>
<p><img src="/img/bVHDKs?w=1540&h=430" alt="图片描述" title="图片描述"></p>
<p><img src="/img/bVHDKt?w=1404&h=724" alt="图片描述" title="图片描述"></p>
<p>要是绩效按照代码行数来算的话,估计可以达标。</p>
<p>2016年还有一个很突出的事,就是给了最多的份子钱,也算是一个突破吧。?</p>
<p>工作就平平无奇,还是老样子,虽然有各种的框架,各种的架构去调整,但仍然是一个"黑人问号.jpg",迷茫过,但因为也没有太多的方向,也只好,再搏一把,赌注大,也好让自己不那么容易"缴械"。其实我也不知道可以怎么办,只好向前冲,相信我的团队,就像他们相信我一样。</p>
<p>2016听了最多周杰伦的歌,但是没有兑现对我女朋友的承诺,带去看他的演唱会,毕竟暂时还耗不起这个费用,因为人生即将步入另外一个阶段: 婚姻。很向往也很害怕,你懂得。</p>
<p>最大的成长是管理,接触了不同的人,经历不同的事,回想3年,年纪轻轻基本就经历了各种事情,大过年我就不说了,就希望世界和平,大家可以身体健康,代码少bug,还有,工程师们多注意身体,多陪陪家人,没什么事情是比自己身体和家人更重要的!!</p>
<p>我经常调侃道: 加什么班啊,整天加班有什么前途,你看看,写代码的哪个有房有车的,整个公司一百多号人,技术几十个,就一个人有这条件,还是你们CTO。啊,做个开发者真不容易啊,但也是真实,不假,所以啊,还是想想,除了写代码,我觉得,应该还可以做点什么让自己时间,生活更加有意义。苦了你们了。虽然我也经常加班(加你妹的班啊!!)</p>
<p>其实我是一名搞PHP的,但是这几年好像都是在被人搞,好心酸,我想新的一年,可以离自己目标更加近,可以更加多生活的时间。哎哎哎,我不是仅仅说PHP哦,我是说,在座各位,新的一年,都可以达到自己想要的,特别是远离他乡来到这里的朋友们,衷心祝福你们!</p>
<p>明天就是2017,在此,愿世界和平,社会和谐,积极向上,远离雾霾,不想再看到这些新闻: <img src="/img/bVHDLW?w=1040&h=96" alt="图片描述" title="图片描述"></p>
<p>即使我可能这是不可能,但是也不能失去希望。再见2016。我要回家打响新年的第一炮了。</p>
<p>别加班啦!!!</p>
<p>来总结: <a href="https://segmentfault.com/a/1190000007917925">2016 总结</a></p>
想做就马上做,喜欢就坚持做 —— 我的编程之路
https://segmentfault.com/a/1190000004575405
2016-03-10T10:10:43+08:00
2016-03-10T10:10:43+08:00
黄总
https://segmentfault.com/u/janhuang
4
<h2>爆照</h2>
<p><img src="/img/bVtmco" alt="图片描述" title="图片描述"></p>
<p>有女朋友,但是不能爆照。哈哈哈哈......</p>
<h2>目前工作</h2>
<p>打杂,扫地。负责部门大大小小的事务,大到方案制定与落实,小到吃饭喝酒大保健。但工作只是生活中的一小部分,当然我可不是一个普通的开发者,我还有好多自己喜欢做的事情,开发,只是其中一项。</p>
<h2>编程之路</h2>
<p>再这个行业已经摸爬滚打2年多,还记得第一次接触编程的时候,实在高二,那时盛行魔兽争霸3,对,没错,我是 90 后,那是因为贪玩,存一周的钱只为与几个朋友周末去打游戏(澄海3c),话说,也好久没玩游戏了,起码都有5,6个年头了,甚是怀念呀。</p>
<p>第一次编程,就是魔兽地图编辑,那是个图形化界面编辑,第一次肯定是个编辑器了,随便的添加各种物体,调整参数,在后面,是有去地图当中去添加事件,触发器,从此迷上了 "程序" 这个玩意,也曾经为班级出了一个防守图,boss是一级一级的老师,最后是校长,哈哈。</p>
<p>上了大学,真正接触编程语言,实话实说,在学校,基本上都得自学,同学们就不要依靠老师了,在你真正毕业后,那才叫 "学习",但其实,我们的人生也是无时无刻都需要学习。工作 2 年多,没有换过工作,同一家公司</p>
<p>第一年,可能是因为入世未深,对社会上的东西比较好奇,每天加班加点的工作,希望可以做出点成绩,嗯,是做了比较多的东西,问答社区,网站改版,支付模块,后台维护,等等的一些 "大项目",</p>
<p>第二年,负责的东西越来越多,可能因为是老油条的原因吧。可能因为责任和负责的东西越来越多,我也开始越来越迷茫了,其实我还是想多出去外面见识见识。没过多久,整个部门都换人了,只剩下我,然后又陆陆续续的招了很多人。</p>
<p>工作中往往有太多太多的无奈,我们需要妥协,也需要反驳,更重要的是,要找到自己,看到未来的自己。</p>
<p>我在努力,你呢?加油,朋友们。</p>
<h2>比较喜欢的软件和硬件</h2>
<p>开发上面的软件有:</p>
<ul>
<li><p>sublime text</p></li>
<li><p>phpstorm</p></li>
<li><p>webstorm</p></li>
<li><p>clion</p></li>
<li><p>xcode</p></li>
<li><p>sourcetree</p></li>
<li><p>gitbook</p></li>
<li><p>MWeb</p></li>
<li><p>Monodraw</p></li>
<li><p>MySQL Workbrench</p></li>
<li><p>postman</p></li>
<li><p>iterm2</p></li>
</ul>
<p>浏览器:</p>
<ul>
<li><p>chrome</p></li>
<li><p>firefox</p></li>
</ul>
<p>工具类:</p>
<ul>
<li><p>Mweb</p></li>
<li><p>gitbook</p></li>
<li><p>Pocket</p></li>
<li><p>todoist</p></li>
</ul>
<p>以上是我常用的工作中的软件.</p>
<p>硬件:</p>
<ul><li><p>macbook</p></li></ul>
<h2>神器?</h2>
<ul>
<li><p>好像没有哦</p></li>
<li><p>哈哈,还真的没有哦。</p></li>
</ul>
<h2>黑科技?</h2>
<p>想我这么挫的开发者估计还真拿不出手,硬要我说的话,那就去我 github 上去看吧: <a href="https://link.segmentfault.com/?enc=ZZLIe7G%2Fo4qPVB1dcCJ1EQ%3D%3D.WbZD2wYPBZPNiNlKgK6wVXRqGSRe2ny1yfbqxLBcQEM%3D" rel="nofollow">JanHuang</a></p>
<h2>推荐书籍?</h2>
<p>像我这些很少看书的人来说的话,还真的没有什么书籍,哈哈,不过对于开发者还有几本吧。</p>
<ul>
<li><p>TCP/UDP 网络编程</p></li>
<li><p>Unix 环境高级编程</p></li>
<li><p>设计模式最佳实践 (忘记名字了?)</p></li>
</ul>
<h2>附言</h2>
<p>欢乐时光过得真系快,又系时候讲拜拜。</p>
<p>可以做自己喜欢做的事情,是一个非常美妙而又难以言喻的感觉,请不要随便放弃自己的追求。</p>
<p>其实我在踏入这个行业之前,我是一名 bboy,跳舞 5 年载,出来工作后,实在是怀念学校跳舞的生活,现在想起真是有一些莫名的伤感,虽然现在没有怎么跳了,还是偶尔还是会约在一起,出去跳舞。</p>
<p>我还是一个比较爱玩的人,因为失去,懂得珍惜,我现在珍惜当下的每个时间,我要用我想要的方式去记录我的生活,对,我也是一个业余的视频剪辑菜鸟。</p>
<p>这都是我喜欢的表现: 行动。</p>
<p>现在这个行业的人,真的好累,动不动就说创业,动不动就大数据,动不动就说产品,唉,我无心听你们扯淡,你们太浮躁了,请让我看到你对生活的热情,我对当下的互联网已经失去了原有的热情,不是因为我不喜欢,而是我不喜欢跟着你们走,我始终还会坚持我喜欢做的事情。</p>
<p>工作仅仅是生活的一部分,生活的大部分时间都是自己把握的,如果把人生的 1/3 时间都投到 "别人" 的工作当中,那是对自己的不负责,如果觉得有个事情比起当前的事情还重要,那就去做吧,哪怕付出一定的代价。</p>
<p><strong>想做就马上做,喜欢就坚持做</strong></p>
<p>希望 2 年后的你们不会后悔你现在的决定!</p>
<p>有点胡说八道了,大家多多体谅.</p>
<blockquote><p>本文参与了 <a href="https://segmentfault.com/a/1190000004509593">SegmentFault「我的编程之路」计划</a>,欢迎正在阅读的你也加入,一起分享。</p></blockquote>
PHP、Android、iOS 的恩恩怨怨
https://segmentfault.com/a/1190000004570180
2016-03-09T15:14:39+08:00
2016-03-09T15:14:39+08:00
黄总
https://segmentfault.com/u/janhuang
3
<p>其实应该更多的是互相的磨合与学习,希望身边的人可以有自己的经验分享,与理解,互相进步才是大家需要的,作为一个 "年老" (我也是90后) 的开发者,我觉得一代胜一代,未来才有胜算。额,好了,我不充当老师的角色了,去正题。</p>
<h2>1. 安卓中 a 标签的坑</h2>
<p>在和客户端交互的过程中,往往都有跳转的,而在咱们 Web 开发中,默认的 <code>href</code> 属性通常是 <code>#</code>,然后通过获取标签绑定动作触发事件,这里有个坑,在与安卓交互过程中会发现。</p>
<pre><code class="html"><a href="#">click me</a>
// include zepto or jquery or other.
$(function () {
$('a').on('tap, click', function () {
alert('test');
return false;
});
});</code></pre>
<p>上述代码中,会引起安卓交互中的一个坑,就是没点击一次,安卓都会在计数器上+1, 也就是,点击第一次,弹一次窗,点击第二次,会弹两次窗,如此类推。</p>
<p>解决方法:</p>
<pre><code class="html"><a href="javascript:void(0);">click me</a></code></pre>
<p>Fxied!!</p>
<h2>2. 安卓中交互的坑</h2>
<p>我丢,安卓这个坑爹,在 <code>js</code> 交互中,无法传递对象,匿名函数等等的类型。也就是说,如果你的参数是接受一个对象的,你必须将她转成 <code>String</code>,而且安卓中的返回值也是只能返回 <code>String</code>,在咱们接受之后需要将字符串转换成对象。</p>
<p><strong> 简单 </strong>判断客户端类型的:</p>
<pre><code class="javascript">isiOS: function () {
if (this.ua.match(/android/i) == "android") {
return false;
}
return true;
},
isAndroid: function () {
if (this.ua.match(/android/i) == "android") {
return true;
}
return false;
}</code></pre>
<p>解决方法:</p>
<pre><code class="javascript">encode: function (data) {
if ("" == data) {
return {};
}
return JSON.stringify(data).replace(/"/g, '\'');
},
decode: function (data) {
if ("" == data) {
return "{}";
}
return JSON.parse(data.replace(/\'/ig,'\"'));
}</code></pre>
<p>在调用 iOS 或者 android 的时候,先将参数传递到 encode 处进行过滤。</p>
<h2>3. 判断页面是否在应用内打开</h2>
<p>其实这个很简单,经常做 web 开发的都应该知道,咱们每天都与 Http 协议打交道,而在 Http 中,有一个很重的的标识,就是 User-Agent,简称 UA,其实这个东西就像咱们的门票一样,有着各种的信息在上面,其实就包括了很多的浏览器内核信息,版本信息,厂商等等......咱们就是从这个地方入手,在客户端中定义自己的版本信息,与微信一样。从此告别什么 url 上加什么破参数啊什么的。</p>
<p>以我的经验来设计的话,一般按照这个格式: </p>
<p><code>Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5 {company} {appname} [{type}]/{version}</code></p>
<p>如: 腾讯,微信 [海外版本] 6.0.0</p>
<p><code>Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5 Tencent MicroMessager oversase/6.0.0</code></p>
<p>因为此处我还没太多接触客户端开发,暂时没有客户端代码演示.</p>
<pre><code class="javascript">us: window.navigator.userAgent.toLowerCase(),
is: function (name) {
return tools.ua.match(eval("/" + name + "/i")) == name ? true : false;
}</code></pre>
<p>Examples:</p>
<pre><code class="javascript">if (/* Is app inside */) {
/* Operation */
} else {
/* Is app outside */
/* Operation */
}</code></pre>
<p>Combine: In wechat</p>
<pre><code class="javascript">if (/* In wechat app */) {
/* open share guide */
} else {
if (/* In app */) {
if (/* Is iOS */) {
/* share iOS sdk */
} else {
/* share android sdk */
}
} else {
/* share operation */
}
}</code></pre>
<h2>4. 请善用你的 Http Status Code</h2>
<p>曾几何时,我也是一个懵懂无知的骚年,连接口是干嘛用的都不知道,大概也在 2014/8 月份左右,我对接口都是一种很敬畏的眼光去看待,直到现在,2016年,我更加视接口是一切数据库的来源,其价值是最至高无上的。但是由我接触接口(Application Programing Interface)开发之后,我发现目前的接口有好多问题,一个很明显的就是,为啥每个接口都是 200,不应该啊,那非 200 响应的应该怎么办?有做处理吗?<br>直到后来,我推翻了这一现象,也说服了各方的人。我举一个 <code>Ajax</code> 的例子:</p>
<p>首先我先问一个问题,为何 jquery 和 zepto 的 ajax 中,都有一个 <code>error</code> 回调?</p>
<pre><code class="javascript">$.ajax({
// some code
success: function () {},
error: function () {}
});</code></pre>
<p>请不要怀疑设计者的智商与知识,我猜他应该会比你聪明以及牛叉。这么做,肯定有原因的。其实这里的 <code>error</code> 就是为了让非 2xx 状态的时候进入的操作的。</p>
<p>如示例:</p>
<pre><code class="json">{
status: 1
content: {}
}</code></pre>
<p><strong> 以上例子本身没有错误,仅以自己看法对上述进行评述 </strong></p>
<p>其实咱们应该要善用本身已经有的东西,不需要再重新造多一个,吃力不讨好,那你们就开始好好补一补 Http Status code 相关的知识吧。</p>
<p>示例:</p>
<pre><code>Request Method:GET
Status Code:200 OK</code></pre>
<p><code>200</code> 一般都是成功响应,<br><code>3xx</code> <br><code>4xx</code><br><code>5xx</code></p>
<p>根据不同的状态码进行不同的处理机制,无需重复再做这一步骤了,其实设计者也很清晰设计出状态码的意义,应该善用。</p>
<h2>5. 判断应用是否安装,如果安装,则打开,否则则跳去下载</h2>
<p>其实这个有点无理取闹,但是又不得不去做,首先这个其实原理也是挺绕的</p>
<ol>
<li><p>利用iframe尝试打开自定义scheme</p></li>
<li><p>跳转到下载地址</p></li>
</ol>
<p>只是那个判断......</p>
<p>其实我是建议这些操作统一由一个地方去做,比如定义一个域名叫: down.xxx.com/应用id,后台读取应用id 相信信息,统一由一处转发。</p>
<pre><code class="javascript">function open () {
var ifr = document.createElement('iframe');
ifr.src = url;
ifr.style.display = 'none';
document.body.appendChild(ifr);
window.setTimeout(function(){
document.body.removeChild(ifr);
}, 3000);
}</code></pre>
<p><strong> 注意 iOS9 对这个有点不兼容,所以,谨慎,iOS7,iOS8,安卓均可以实现 </strong></p>
<p>获取版本信息:</p>
<pre><code class="javascript">var getVersion = function () {
if (tools.client.isAndroid()) {
return parseFloat(tools.ua.match(/Android\s(.*?);/i)[1]);
}
return parseFloat(tools.ua.match(/OS\s(.*?)\slike/i)[1].split('_').join('.'));
};</code></pre>
<p>这里需要判断系统类型及版本:</p>
<pre><code class="javascript">if (/* iOS */) {
if (/* iOS version less than 9.0 */) {
/* try open scheme */
/* redirect download link */
} else {
/* other operate */
}
} else {
/* try open url */
/* redirect download link */
}</code></pre>
<p>所以,由此可见其实这里的判断会很多,很多,很多。所以我墙裂建议都统一到一个地方做分发。</p>
<p>这里其实还需要判断是否微信,是否应用内打开,是否是分享出去的。</p>
<p>结合第 3 点的例子想想,这里的 <code>if</code> ...... 其实还是很恐怖的,我已经无路可退了......</p>
PHP学习计划
https://segmentfault.com/a/1190000004501914
2016-02-27T17:42:09+08:00
2016-02-27T17:42:09+08:00
黄总
https://segmentfault.com/u/janhuang
21
<h2>PHP 相关</h2>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=hl2ly6wLQrA5AIrBEqJbJQ%3D%3D.pUs3PGq2CdHHUKIE5Q9xpjShBUSMj4QS%2BT7jpPO4UWmJ5smY8avnYldjLkJUEVj7" rel="nofollow">FastD PHP Web框架</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=%2FYPIRBEBZC538GQhNH9WqQ%3D%3D.%2FTqXL9hka8Zyke2sCRN3qxgCz5WgagjUCPgRX8dBFCQ0OhswzBtvjychoneP%2B3RJ" rel="nofollow">PHP路由</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=PniO%2Fpbgr1cSvXaSv4qL4w%3D%3D.mE7vIfywBPBH4tJw5HJeOBQzSCgJgj463elBx4HBsVaNFK4jcu4DUKtEnYo0jze2" rel="nofollow">Swoole网络组件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=lJ9NSiObAGzTJmHius0EdA%3D%3D.lZpCEE3sVKEIigsub%2BZfBVoTjdgH7qlOXX9ispU024YNsVLigVwichoMl7UZd%2Fqz" rel="nofollow">PHPHttp解析组件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=6AlMEE93Ag6NS9NHvZmVKw%3D%3D.dOPFEbJzrOMBDrM2NgZOkQlIg14l124%2F1u8YljTNoAtk%2B4GaFWS6p0WVChtzuQW%2B" rel="nofollow">PHP数据库</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=uDekjZM4vjhInsQdaI53TA%3D%3D.LvkAySk2gxrViwZqgPccBS5rVLpqFxzhsYQNL2cKB6HikHqtaCb2%2Bl8FzylQouEB" rel="nofollow">PHP对象生成器</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=ihfuKFLiPoFViu%2ByLwvv%2FQ%3D%3D.uXe%2Ft%2FsolUF8vHFZg70%2FMTY3DYOlq4ekX54Mq21mY84DtlzAiIWyuu1%2FlK7DuriY" rel="nofollow">PHP错误处理组件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=FVG87wtxk%2Fez9pMyv7ubag%3D%3D.a7V37KV3W940l9ld69%2FR1HtTEGx7B5maXDMxMJaT6yFUZwDKxTzmItDDOZL7FxNj" rel="nofollow">PHP配置</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=f1wBeemhb%2F7ZxWvFMspNJQ%3D%3D.Dq4KqSHm%2FCenFi9MTlgDaUuK%2FIlZUOrlKfLSODvyZS49%2Bo%2BEuGNPOX6YyL2rqPuS" rel="nofollow">PHP命令行工具</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=YGyGARj4Edgwz576hqF7Jg%3D%3D.C4nCVv%2FjhpKIvtgNWW85R0zkWbW5WCRk0rO3fLJT%2BQajR8tvVEf8J%2FqYSDggz2d5" rel="nofollow">PHP简单容器</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=Jd0isolfOu0AURCPrsnmrA%3D%3D.LIP0MhZGE4mXsASTvHBTq8VxlRCI%2FOVDePlm%2FGOqIPL7RI7apvA3fIw4ODs3LEJM" rel="nofollow">PHP注释解析组件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=bXeY%2FqDnyhwXLR38wdcEiw%3D%3D.aI%2FSA9aEQZsau7XhCNG%2FTp1ZPSfVPTH2LqjVmO%2FE1MQG9pM3vbuZGfG9qkdVITlD" rel="nofollow">PHP文件处理(基于symfony/finder)</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=iyRjCkvvnpoqDUjNQ0JBVw%3D%3D.TwgxvTCc3fUqkg3E1R3xob1XRm1I8AUZMoKv8Nse%2FbhhpSLKMUWtHv2jyppmM5LJ" rel="nofollow">PHP数据存储</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=hx15xzRjp4gBeBqyBZgnXg%3D%3D.kN4smxyzLbPbQuRY5C3EkZHrZ6VNytkD0ccHJL4Ea2%2B%2BmskeAYt4YBlvWYaTPPWF" rel="nofollow">PHP日子处理(基于monolog)</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=%2F9nOQP714UsaazZVk4hGUQ%3D%3D.RyIwYGDk8PBBzLjDG8u1iBMvUDzv6xgSKoG%2BGeJQ59ygiR58IPlzgMAc%2F6%2Fe8Y4%2F" rel="nofollow">PHP模板引擎(基于twig)</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=p50ihJ218UkbMzIxH1yX4A%3D%3D.dg8nUxGO0eefNlXnVpk%2Bh6Q%2BSYMk3tD4rXQP0D4PpRiSmO2QyMyi9UV0%2Fwhgb3vP" rel="nofollow">PHP中间件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=SN63B3R9AnMw1BzVA0nQ2A%3D%3D.PBCfu8VyM3fCL1OWwb3xG6Zi36jantj%2BEfIvimC0vwo07LkeLZKqJ2eB9HmaMA5n" rel="nofollow">PHP单元测试</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=L9tRJUkdgBWAVIcJHQiAFQ%3D%3D.ia2F1ACHnzYX%2BwmcTxLQm2qN7O3ds84rG5RErJ7L1hxoBBxgQavi3t%2FXZO7tSGQS" rel="nofollow">HTTP 中间件</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=10y877y%2FumbBhjkgWWgwQQ%3D%3D.5ndHNK0oLssnluvFRFttfA9tu43S78K6r73jpXzSA6XW9O8fvAAkJvZYsdZ9SbQD" rel="nofollow">PHP 数据类型操作</a></p></li>
</ul>
<h2>网络编程</h2>
<ul><li><p><a href="https://link.segmentfault.com/?enc=KbT6%2BNa9LhZGJeseiQKh1Q%3D%3D.D%2Fk3Eva4nlLS%2Fq2JQ6lLuNs4PC7m11ji4ptsBeoHbcOQlUMbXlRhXwBt6g2arWU9" rel="nofollow">swoole</a></p></li></ul>
<h4>PHP 知识</h4>
<ul>
<li><p>基础HTML、JavaScript、CSS知识</p></li>
<li><p>环境配置,内置 Web 服务器</p></li>
<li><p>PSR 知识</p></li>
<li><p>Git 版本管理</p></li>
<li><p>Composer 与 Packagist</p></li>
<li><p>代码注释</p></li>
<li><p>PEAR</p></li>
<li><p>命名空间</p></li>
<li><p>日期相关</p></li>
<li><p>设计模式</p></li>
<li><p>依赖注入、容器</p></li>
<li><p>数据库操作、注入</p></li>
<li><p>PHP 模板??? (能够深入PHP,而不仅仅在套模板上)</p></li>
<li><p>错误与异常</p></li>
<li><p>web 安全,密码哈希</p></li>
<li><p>CLI 命令行</p></li>
<li><p>配置文件</p></li>
<li><p>多环境配置,测试环境,开发环境,生产环境</p></li>
<li><p>应用测试,单元测试,测试驱动</p></li>
<li><p>持续集成</p></li>
<li><p>服务与部署、PaaP 服务</p></li>
<li><p>Vagrant</p></li>
<li><p>缓存,OPcode,文件缓存,redis 缓存</p></li>
<li>
<p>开源框架、开源组件</p>
<ul>
<li><p>Symfony</p></li>
<li><p>Laravel</p></li>
<li><p>FastD</p></li>
<li><p>Slim</p></li>
<li><p>cakePHP</p></li>
</ul>
</li>
<li><p>热门社区,Github</p></li>
<li><p>网络编程 (Swoole)</p></li>
<li><p>其他语言学习,go、python、lua、C/C++</p></li>
<li><p>PHP扩展开发</p></li>
<li><p>架构基础,理论</p></li>
<li><p>分布式概念</p></li>
<li>
<p>服务化概念</p>
<ul><li><p>实践 RPC</p></li></ul>
</li>
</ul>
<h4>运维知识</h4>
<ul>
<li><p>linux 环境搭建,centos,ubuntu,arch</p></li>
<li><p>基本文件目录管理命令</p></li>
<li><p>压缩打包命令</p></li>
<li><p>挂载命令</p></li>
<li><p>vim编辑器使用</p></li>
<li><p>磁盘管理命令</p></li>
<li><p>用户管理</p></li>
<li><p>权限管理</p></li>
<li><p>进程管理</p></li>
<li><p>计划任务</p></li>
<li><p>服务管理</p></li>
<li><p>系统管理</p></li>
<li><p>FTP, SFTP</p></li>
<li><p>shell编程</p></li>
<li>
<p>lnmp 环境部署</p>
<ul>
<li><p>php</p></li>
<li><p>nginx</p></li>
<li><p>mysql</p></li>
</ul>
</li>
<li><p>nginx web配置</p></li>
<li><p>网络管理,抓板</p></li>
<li><p>DNS,LVS</p></li>
<li><p>TCP/IP</p></li>
<li><p>虚拟化,docker</p></li>
<li><p>自动化工具 Puppet,ansible</p></li>
<li><p>系统工具 SaltStack</p></li>
<li><p>监控工具 zabbix,Cacti</p></li>
<li><p>安全 iptables</p></li>
<li><p>配置管理 CMDB</p></li>
<li><p>内核配置及优化</p></li>
<li><p>Linux C</p></li>
<li><p>硬件</p></li>
</ul>
<h4>架构思维</h4>
<ul>
<li><p>LNMP 搭建</p></li>
<li><p>Nginx 负载均衡</p></li>
<li><p>网络通信,Socket编程,Swoole扩展</p></li>
<li><p>缓存 NoSQL</p></li>
<li><p>消息队列</p></li>
<li><p>异步通知</p></li>
<li><p>池</p></li>
<li><p>负载策略</p></li>
<li><p>网关入口</p></li>
<li><p>监控体系</p></li>
<li><p>监控指标与可用性</p></li>
<li><p>性能预判</p></li>
<li><p>网络通信,Socket编程,Swoole扩展</p></li>
</ul>
<h4>分布式概念</h4>
<ul>
<li><p>调度器</p></li>
<li><p>网络通信</p></li>
<li><p>一致性</p></li>
<li><p>消息队列</p></li>
</ul>
<h4>监控体系</h4>
<ul>
<li><p>监控指标</p></li>
<li><p>可用性</p></li>
<li><p>CPU,memory,磁盘,网络</p></li>
<li><p>执行时长,调用链</p></li>
</ul>
<h4>日志分析</h4>
<ul>
<li><p>flume</p></li>
<li><p>ELK</p></li>
<li><p>Hive</p></li>
<li><p>Hadoop</p></li>
</ul>
<h4>大数据</h4>
<ul>
<li><p>hive</p></li>
<li><p>Hadoop</p></li>
</ul>
FastD 框架与前端整合方案
https://segmentfault.com/a/1190000004368922
2016-01-26T18:59:22+08:00
2016-01-26T18:59:22+08:00
黄总
https://segmentfault.com/u/janhuang
0
<p>FastD 默认使用的是 twig,因此从前端来说,twigjs 和 fastd是可以整合的,而且并且试验过,twigjs和 fastd 整合也是前后端开发一个不错的方案。</p>
<h2>gulp</h2>
<p>安装 gulp: <code>npm install gulp --save</code></p>
<h2>guip-twig</h2>
<p>安装 gulp-twig: <code>npm install gulp-twig --save</code></p>
<p>文档地址: <a href="https://link.segmentfault.com/?enc=U5xG5sJTMEU17qdJclNNEw%3D%3D.%2F6Mi4548WgLBtvs%2FThYmJ7Bu4%2FVvGlCY8umMdIWgJKNw3dMAnpxiN2otP9%2FCqXKZ" rel="nofollow">gulp-twig</a></p>
<h2>其他: less, compass(由前端决定)</h2>
<p>可选安装: <code>npm install [options] --save</code></p>
<h2>环境配置</h2>
<p>FastD: 2.0.x-dev</p>
<p>新建 <code>gulpfile.js</code>,编写内容:</p>
<pre><code class="javascript">'use strict';
var gulp = require('gulp'),
bundles = require('./bundles.json'),
twig = require('gulp-twig'),
tasks = (function (bundles) {
var tasks = [];
for (var i in bundles) {
tasks.push(bundles[i].name);
}
return tasks;
})(bundles)
;
var resources = './resources';
// todo. waiting...
/**
* Task list.
* */
gulp.task('default', tasks, function () {
console.log("Tasks: " + tasks.join(','));
console.log('Register bundles length: ' + bundles.length);
});
</code></pre>
<p>引入gulp,基础的处理,如果没有接触过 npm(Node Package Manager) 和 gulp 的同学,可能要去学习下咯: <a href="https://link.segmentfault.com/?enc=p9OeeQkxMUVravz8A6Fakw%3D%3D.IXaGQIry%2FiBb9VPbG0c5vkbhh4ar43ibOEpaQGLjPJM%3D" rel="nofollow">npm</a> <a href="https://link.segmentfault.com/?enc=vcQrrPt6muQcEXS9is%2B9iQ%3D%3D.lmubeHJRe5GnANlik0cHhNReeAaIqoamZZ6%2BbKOxT8Y%3D" rel="nofollow">gulp</a></p>
<p>这里的 <code>require</code> 当中有一个 <code>bundles.json</code> 配置文件,是因为 FastD 和 Symfony 一样,模块需要注册,因此在开发环境中也需要配置相对应的配置文件,现在新建文件: <code>bundles.json</code>。文件中的所有模块按照 <code>app/application</code> 的 <code>registerBundles</code> 里面注册的模块。</p>
<p>bundles.json:</p>
<pre><code class="json">[
{
"name": "welcome",
"path": "./src/Index/Resources",
"data": {
"title": "test title"
}
}
]</code></pre>
<p>每个 bundle 均已一个对象存在,json 你懂的。其中 <code>name</code> 表示模块名,<code>path</code> 表示模块资源所在地址,<code>data</code> 是根据 <code>twig</code> 模板中所需的 "变量",也就是 <code>{{ 变量名 }}</code> 中所需要的变量。</p>
<p>再看看 gulpfile 文件,因为 FastD 中模板引擎实现两个预定义函数,所以在配置环境的时候也需要统一处理,函数分别为: <code>url(name, args, format)</code>, <code>asset(name, version)</code>,分别表示生成 url 地址,生成资源链接地址(link, script等资源地址)。</p>
<p>所以 gulpfile 也需要定义此函数,实现的内容根据自己喜好调整:</p>
<pre><code class="javascript">var functions = [
{
"name": "asset",
func: function (args) {
return args; // todo
}
},
{
"name": "url",
func: function (args) {
return args; // todo
}
}
];</code></pre>
<p>回头看看上面的配置,因为 <code>bundles</code> 是以一个数组的形式组成,所以我们的配置需要根据动态注册的 <code>bundle</code> 进行处理,不应该手动一个一个去操作,因为开发效率问题以及一些灵活性的问题。</p>
<p>循环每个 <code>bundle</code> 进行动态读取配置:</p>
<pre><code class="javascript">/**
* Register bundles.
* */
bundles.forEach(function (bundle) {
module.exports = function (gulp, twig, bundle) {
var view = bundle.path + '/views';
var asset = bundle.path + '/assets';
var watch = bundle.name + '.watch';
gulp.task(watch , function() {
console.log(bundle.name.replace(/(\w)/,function(v){return v.toUpperCase()}) + 'Bundle: task running......');
var tpl = gulp.src(view + '/**/*.twig')
.pipe(twig({
base: view,
data: bundle.data,
functions: functions
}))
// output html
.pipe(gulp.dest(resources + '/' + bundle.name + '/views'))
;
var js = gulp.src(asset + '/**/*.js')
.pipe(gulp.dest(resources + '/' + bundle.name + '/js'))
;
var css = gulp.src(asset + '/**/*.css')
.pipe(gulp.dest(resources + '/' + bundle.name + '/css'))
;
});
gulp.task(bundle.name, [watch], function () {
gulp.watch(view + '/**/*.twig', [watch]);
gulp.watch(asset + '/**/*.css', [watch]);
gulp.watch(asset + '/**/*.js', [watch]);
});
}(gulp, twig, bundle);
});</code></pre>
<p>以上是完整配置,包括监听文件,此处监听文件变化动态生成文件。</p>
<p>生成规则,存放在 <code>./resources</code> 下,按照模块名分开, <code>js</code>, <code>css</code>, <code>views</code> 目录分开,如果存在图片,也是按照 <code>images</code> 区分,自行配制即可,如此类推。</p>
<p>配置已经完成,新增: </p>
<p><code>bundles.json</code><br><code>gulpfile.js</code></p>
<h2>正式编码</h2>
<p>正是编码之前哆嗦几句,我们总说前后端分离,前后端分离,前后端分离,我也不知道前后端应该怎么分离,前后端为什么要分离。实际上很多人都想分离分离分离,但是想想,分离之后真的有你想象的那么好吗?我看其不然,技术本身是相辅相成的东西,其实分离了之后,虽然说负责的东西和处理的东西单一了,但是在很多时候,这并不利于一个团队以及一个项目的发展,因为这从一定基础上隔阂了大家。</p>
<p>我更推荐是精通一种并且懂得与其他的技术交互,这并不是分离,而是更好地将自己熟悉的东西融合到其他技术当中,并且更好地与其他技术一起工作。</p>
<p>前后端分离一直都是个梗,还是以团队利益最大化考虑。</p>
<h3>前端</h3>
<p>通过以上配置,前端现在只需要关注自己的代码,结构与后端一致即可,有变动需要同步到 git 并且与后端沟通</p>
<p>前端编写的 twig 代码存放在 <code>{Bundle}/Resources/views</code> & <code>{Bundle}/Resources/assets</code>,输出通过 gulpfile 控制,环境,数据由自己控制,摆脱后端的依赖。</p>
<p>当前端调整模板的时候,完全可以按照后端的变量分配方式进行处理。并且后端可以很好地兼容和操作 twig 模板。后端也可以很好地处理 twig 模板。配合 git 可谓是一个非常高效的方式。</p>
<h3>后端</h3>
<p>通过以上配置,后端现在只需要关注自己的代码,结构与前端一致即可,有变动需要同步到 git 并且与前端沟通</p>
<p>后端现在就可以完全不用理会前端干啥了,完全可以做自己做的工作了,当前端有变动的时候,你可以第一时间看到变化的情况,而且发现有操作不当或者需要调整的地方,可以直接调整修改,前端进行代码同步。</p>
<p>逻辑处理,动态变量分配都可以自由控制。</p>
<h2>总结</h2>
<p>其实为啥这样做?原理很简单,双方的工作应该自己都要清楚,但是双方之间的工作对接也更应该统一并且易于处理,也就是说,中间的那一层应该是大家都能操作,并且双方都作为监督者,一方面可以提高效率,另一方面可以提高项目、团队之间的沟通,还可以提高默契。</p>
<p>所以我觉得这是一个值得尝试的方案。</p>
<p>如果你的工作中有使用用 twig 作为模板引擎,可以根据自己的业务定制自己对应的开发环境,提高效率是咱们要去认真对待的事情。</p>
我的 PHP 学习路线
https://segmentfault.com/a/1190000004356309
2016-01-23T23:15:02+08:00
2016-01-23T23:15:02+08:00
黄总
https://segmentfault.com/u/janhuang
29
<h2>我的 PHP 学习路线</h2>
<h3>1. PHP 入门</h3>
<p>PHP 入门没啥好说的,会基本编码,基本的开发结构(通常指MVC,呵呵哒),类与对象,就差不多了,可以说是入门了。</p>
<h4>1.1 OOP</h4>
<p>写 PHP 最多目前无非就是 OOP,面向对象,说时迟那时快。不开玩笑地说,与我共事的某些前辈是连 OOP 都搞不清的,这里我就不多说了。</p>
<p>其实不是说一定要会,要去理解 OOP,只是如果你理解这个玩意,对于你后面的工作已经学习会非常的有好处。</p>
<p>其实面向对象也没有那么复杂,只要理解一点,<strong>单一职责</strong>,其实就是一个对象越简单越好。</p>
<p>也就是说,是自己该做就由自己做,不该由自己做的就不要自己做。</p>
<h4>1.2 MVC</h4>
<p>老梗了,所谓 MVC 很多人会说 Model, View, Controller, 其实这样说没错的,只是这样说不会让你更好地理解结构以及扩展性。</p>
<p>MVC 我当前的理解是:调用(度),也很简单,其他框架也差不多这个原理,当然每个框架的实现方式都不一样。</p>
<p>基本流程:</p>
<pre><code>+--------------+ +-----------+ +--------------+ +--------------+
| | ----+ request +----> | | ------------> | |
| | +-----------+ | | | |
| Request | | Route Match | | Handle |
| | +-----------+ | | +----------+ | |
| | <----+ Response +----- | | <-+ Response +-- | |
+--------------+ +-----------+ +--------------+ +----------+ +--------------+</code></pre>
<p>大致列了下最基础的框架底层原理,实际上咱们开发中 90% 接触的只是在 <code>Handle</code> 处的工作,至于 <code>Request</code>、 <code>Match</code>、 <code>Route</code> 等基础的对象或者数据,框架早已经封装好了,也无需过度纠结和头疼这方面的事情,先要把业务功能处理好,并且容错处理好即可。</p>
<p>简单总结一句话:<strong>知其然而不知其所以然</strong>。</p>
<hr>
<h3>2. PHP 进阶</h3>
<p>进阶这个真是个头疼的问题,啥叫进阶,我的理解是,做一些之前没有做过的(指本领域:PHP),例如:设计模式,SPL,缓存设计(Cache),研究框架,开发规范,单元测试,行为驱动开发,组件(包括自己着手开发的),Composer 等等的东西,都可以理解为进阶。</p>
<p>框架推荐:</p>
<ol>
<li><a href="https://link.segmentfault.com/?enc=qd5Co9PnS%2FyxI1cgdDzv8w%3D%3D.Xg8P%2BgTiszXpRZe%2Bqwn7knwHWIqByyP%2BKo8DxL1ZTJU%3D" rel="nofollow">Symfony</a></li>
<li><a href="https://link.segmentfault.com/?enc=mpnUUirCZvmLQqD390GW1g%3D%3D.UTPxI%2BNTv%2FhW9QrCpLtWl78R93RomQ2oq57fh50A0v0%3D" rel="nofollow">Laravel</a></li>
<li><a href="https://link.segmentfault.com/?enc=rX%2BRmmqP3YgtgzeWsd53tg%3D%3D.3nxEzHc4Xgyl2suq6cu%2FVenoC0KC6cvtEa6aG9Bpv90%3D" rel="nofollow">Yaf</a></li>
</ol>
<p>PHP 开发规范(PSR):</p>
<ol><li><a href="https://link.segmentfault.com/?enc=otVrUBuJWZbBVWGEmGgh6w%3D%3D.511vBVuwTJ%2B2qH0eo0a6vEJZW5HVbBfTDzk8hX3zjFg%3D" rel="nofollow">PHP FIG</a></li></ol>
<p>PHPUnit & TDD/BDD:</p>
<ol>
<li><a href="https://link.segmentfault.com/?enc=G3O%2FZ8x%2BVG%2FQ12G%2BycVhKA%3D%3D.q1xviTiTQlj7NmQPrDwnyrWYL2x0yuAN6c2YU3snpXk%3D" rel="nofollow">PHPUnit</a></li>
<li><a href="https://link.segmentfault.com/?enc=%2Fp7F8VYMJmvSEEQK5HhF3Q%3D%3D.f94XmXp6LaPm4r9l6spv1qLMJ4QMRBQmiaKsvOKXb2PR0EpCZ%2BVlJnXnXzD%2B6BEa" rel="nofollow">PHP spec</a></li>
</ol>
<p>设计模式:</p>
<ol><li><a href="https://link.segmentfault.com/?enc=4r1Dxiz%2Fv29eh%2BUCSTnTEA%3D%3D.R0PUUYDpUH4p5kmbKMf9QGRTylDz%2B5fT90gDehQQHQGmNWWeguq0WfEjLBekENH9IleKkKWQtIwrSyDD%2FMY4nA%3D%3D" rel="nofollow">设计模式</a></li></ol>
<p>SPL:</p>
<ol><li><a href="https://link.segmentfault.com/?enc=RDNvFfiHBbim7IegICxN%2FA%3D%3D.M%2FREZjOxIfmtRIkIgJ42XBtw8IT%2F27Go9dMr42SfJmjaFXdH7jf7KdLfZ1IUrHDc" rel="nofollow">PHP SPL</a></li></ol>
<p>Composer:</p>
<ol><li><a href="https://link.segmentfault.com/?enc=ZxKqDpCMGSCBImWyLomalQ%3D%3D.sEhczvI57cOBFMGC3ObiHJpLGkqCxjWncBuddDCuyKQ%3D" rel="nofollow">Composer</a></li></ol>
<p>欢迎补充</p>
<h4>2.1 设计模式</h4>
<p>设计模式,一个在软件设计中占有重要角色的课程,而我们又不得不去了解。而对于设计模式来说,很多人是抗拒的,因为他和普通的开发者有着不一样的思维方式,一个很明显的提升就是: <strong>设计</strong>,而设计应该是根据发展的变化而变化,业务的变化而变化,模式也一样,不会是永恒不变的。唯一不变的是,基础,这些技巧与思维少不了的是需要良好的基础。</p>
<p>所以说,基础是多么重要的,任何一切的一切都是建立与基础之上,这叫积累。如同盖楼。</p>
<p>在这些已有基础,我们需要学习并且还需要学会各种推陈出新,这样才可以让自己让他人走得更远。</p>
<p>设计模式学习推荐: <a href="https://link.segmentfault.com/?enc=7UgWLuqolaTn2jN%2B%2FOaihA%3D%3D.EztlCWORFb%2BvPCRJXxyGdbMNEbeKB5rIpAm3ndXJwaui7Kg6iLC6zparKYgyN1L2S5uw9%2F50gwiqGIoTQIoSiQ%3D%3D" rel="nofollow">设计模式</a></p>
<h4>2.2 资源节省</h4>
<p>当今互联网,每秒钟都是钱,能节省的,绝对不能浪费,而且节省还可以一定地提高服务的质量。</p>
<p>一般我们说的资源是:</p>
<ol>
<li>内存</li>
<li>带宽</li>
<li>磁盘</li>
<li>CPU</li>
</ol>
<p>而这些之间其实是有一定的关联的,和代码质量和容错机制也有一定关系。</p>
<p>上述每一个都很重要,决定着你的服务质量。</p>
<p>能少占用内存就少占用内存,图片、媒体资源能压缩就压缩,减少无用的存储,降低代码的复杂度。</p>
<h4>2.3 容错</h4>
<p>比较常见的是: <code>file_get_contents</code>,<code>connection</code>,有时候我们会很自然地遗忘这里其实会有一定几率出现超时,最严重的就是造成服务无响应,如下代码:</p>
<pre><code class="php">file_get_contents('https://www.google.com/');</code></pre>
<p>这代码分分钟被打,活生生将程序毁了。</p>
<p>如果发生超时,会占用大量 CPU,严重可能会导致服务无响应,危害极大。</p>
<p>容错是每个开发者在开发过程中必须要考虑的地方,没有代码没有 “八阿哥”,总会有意外的地方,做好容错可以最大程度地减少对用户的伤害以及可以有效提高在出错时的用户体验。百利而无一害。</p>
<h4>2.4 日志</h4>
<p>日志,一个很容易被无视而又非常非常重要的环节,可以说,这一环节比你做的任何一个地方的业务代码都要重要。</p>
<p>日志的好处:</p>
<ol>
<li>记录,跟踪</li>
<li>调试</li>
<li>恢复</li>
<li>分析</li>
<li>调优</li>
<li>等等...</li>
</ol>
<p>好处有好多,但在我们开发者当中,其日志最重要的功能之一就是,记录问题,调试代码,优化架构。</p>
<p>不得不说日志的重要性,请你好好重视这一个容易被忽略的环节。</p>
<p>而日志的设计需要考虑性能,不能因为日志而影响服务质量哦。所以在日的环节在完整性和效率上要做好权衡。</p>
<h4>2.5 缓存设计</h4>
<p>缓存必须是针对业务情况而设计,不能生搬硬套。大公司的不一定适合你,开源方案也不一定不适合你,一定要找到自己合适的方案。</p>
<p>而在缓存设计方面,我个人推荐这一系列的文章: <a href="https://link.segmentfault.com/?enc=1xTksbzSH%2FcjRjuUDn11Zg%3D%3D.eo78SrqdU7MGsYJVKVFbu%2BozcWAtegzzOB8lEN3eqkOIGuwdlp9KXLQWYBSrCqVq" rel="nofollow">缓存使用与设计</a></p>
<p>通俗简单地说,缓存的目的是为了提高服务的响应速度以及质量,不能因为缓存的添加而导致服务异常。</p>
<h4>2.6 原理理解 (这里说到框架的研究,说得不好的地方,请多多批评和教导)</h4>
<p>我们平时开发接触不同的开发工具,操作,甚至是监控,那么他们其中的原理,你又理解么?</p>
<p>其实我们每天接触的东西都有它的实现原理,算法等等的东西,但我们一直都在使用,并没有很好地去理解他们当中的奥秘。时候有空去接触一下了。</p>
<p>首先,开发,编码我觉得就是将现实生活搬到互联网中,我举个例子: </p>
<p>为什么人访问量上来了,咱们就要扩容,去扩展呢?原理很简单,看看咱们超市里面的收银台就知道了。咱们超市里面所有收银台不是每天每个位置都有人的,根据人流(流量)动态扩容,而且有些收一台的一个位置上设置有两个人(多线程/多进程,看个人理解),这个设计挺妙的。当人一多,就会开多各个位置收银台,和增加人手。那他是怎么知道人多的呢?也很简单,就是经理(Manager/Master进程或者是运维,看理解),他知道,内部调度安排人手。</p>
<p>大致举了这么一个例子来说明扩展的原理。</p>
<p>回到正题: 框架</p>
<p>相信大家都有接触过很多不同的框架,CI, TP, Symfony, Yii, Laravel, Slim等等,最深刻,最相似的地方在哪里呢?可能有些没有,但是,有一点可以肯定的,就是,都是有一系列 Http 封装,Route 处理。因为这两是灵魂,剩下的是不是业务和结构。</p>
<p>剩下的就是考验框架代码质量的时候了,这里有一系列的框架,有的轻量,有的重量,有的灵活,有的高效。其实说到这里很多朋友都大概了解框架的运行原理。</p>
<p>我看过Symfony、Laravel、CI、Silex和ThinkPHP的代码,当然很粗略地看看。里面实现的大部分代码都是将各个处理模块 “粘合” 一起,也就是 "Bridge"。</p>
<p>大致流程图:</p>
<pre><code> +----------+ +-------------+ +------------+
| | | | | |
| Client |---------->| Application |--------------------->| Start |
| | | | | |
+----------+ +-------------+ +------------+
^ |
| |
| v
| +-------------+ +-------------+
| | | | |
| | Container |------------>| Bootstrap |-----+
| | | | | |
| +-------------+ +-------------+ |
| ^ | +------------+
| +-------+--------+ | | |
| | | +---->| Request |
| +-----------+ +-----------+ | |
| | | | | +------------+
| | Route | | Service | |
| | | | | |
+----------------+ +-----------+ +-----------+ +-------------+ |
| | | | |
| Response | | Dispatcher | |
| |<----------------------------------------------| |<---------------+
| | | |
+----------------+ +-------------+
^
|
+--------------+
| |
| v
| +----------+
| | |
+--------|Controller|
| |
+----------+ </code></pre>
<p>当你了解了这些流程以及所需的组建,用先用的组件的话都是可以很方便地组合出自己想要的框架。</p>
<p>其实我这里希望更多的初学者多看看 ThinkPHP 的代码,然后也看看其他开源框架的代码,你会发现,除了 ThinkPHP 之外,你还可以发现更多新奇好玩的东西。</p>
<p>最终其实还是离不开自己动手开发一个。</p>
<p>具体框架开发系列我会找个时间与大家分享。框架的理解可谓到一段落,下次我会分享我对其他服务器的理解。</p>
<p>来到这个阶段的,应该始终有一个意识:<strong>服务质量高于一切</strong>,就是不能因为添加服务而影响原有的服务</p>
<p>简单总结一下。大量动手实践自己的猜想,分析自己做过的项目并且优化自己觉得不足的地方。</p>
<p>总结: <strong>知其然并且知其所以然</strong>。</p>
<h3>3. PHP 高手模式</h3>
<p>我相信经过九九八十一难,能来到这个模式的人已经不多了。我自己还是菜鸟,不敢在这里胡说,这里会记录我所有的 PHP 底层学习的经过,这里的故事,我想我等不到下一个故事了。</p>
<ol>
<li>Unix 环境高级编程</li>
<li>tcp/ip 网络编程</li>
<li>PHP 扩展开发</li>
<li>颈椎病康复指南</li>
<li>活着</li>
</ol>
<h4>3.1 Unix 环境高级编程</h4>
<p>为什么要理解并学习这一方面的知识?若果你是一名有追求的 PHP 开发者,最终还是应该将精力落入到 “系统” 处,因为真正处理 PHP 程序的,正正是 “系统”,如何可以让自己的 PHP 做得更好,如何可以让自己的服务做的更好,如何可以让后端支持更加强大,灵活,高效,你就应该关注到底层系统的实现当中,但平时也不要忘了 PHP 的开发规范。</p>
<p>此处的文章和笔记,我将会在以后的 Unix 学习一系列当中给大家呈现。</p>
<h4>3.2 tcp/ip 网络编程</h4>
<p>在日常生活中,<code>Http</code> 已经离不开大家了,从平时打开应用,打开浏览器,搜索,都是需要使用 <code>Http</code>,应该没有比这个接触更多了吧。但是里面的奥秘你又知道多少?没关系,和我一起探索 <code>TCP/IP</code> 的奥秘吧。</p>
<p>文章和笔记,以后会在 TCP/IP 章节中给大家呈现.</p>
<h4>3.3 PHP 扩展开发</h4>
<p>学习扩展开发和学习 PHP 是一样一样的,根据 PHP 手册上的流程...</p>
<ol>
<li>语言参考</li>
<li>基本语法</li>
<li>类型</li>
<li>变量</li>
<li>常量</li>
<li>表达式</li>
<li>运算符</li>
<li>流程控制</li>
<li>函数</li>
<li>类与对象</li>
<li>命名空间</li>
<li>Errors</li>
<li>异常处理</li>
<li>生成器</li>
<li>引用的解释</li>
<li>预定义变量</li>
<li>预定义异常</li>
<li>预定义接口</li>
<li>上下文(Context)选项和参数</li>
<li>支持的协议和封装协议</li>
</ol>
<p>看到这里应该要懂得举一反三的了,后面的看你们的造化了......</p>
<p>此处的文章和笔记,我将会在以后的 PHP 扩展开发 学习一系列当中给大家呈现。</p>
<h3>预告</h3>
<p>我目前已经开始做了: <a href="https://link.segmentfault.com/?enc=inVLiA7yydXvEhDjYvlr6Q%3D%3D.CYYXJqQHmWDPXWhq1W9SgxMIDA9ltQcS40V1rZGLOTYdS589o6ViXLhrzzCT4HCE" rel="nofollow">http://blog.fastdlabs.com/tag...</a></p>
URL 的理解
https://segmentfault.com/a/1190000004354626
2016-01-23T14:36:23+08:00
2016-01-23T14:36:23+08:00
黄总
https://segmentfault.com/u/janhuang
0
<h2>URL 的理解</h2>
<p>维基百科给出答案</p>
<p><strong> URL(全名Uniform Resource Locator),又譯劃一資源定位器,俗稱網頁地址(網址),係互聯網上標準嘅資源嘅地址。要去互聯網上任何一個地方,都需要佢嘅URL。 </strong></p>
<p>首先咱们要理解这个叫资源定位的意思,咱们平时在配置部署 web 服务器的时候,应该都有见过 document root 这个配置项,以 nginx 为例,nginx 中,就是需要配置一个 <code>root</code> 选项,而每个 server 都对应一个 server_name,或是域名,或是ip,一个标示,那这个 server_name 其实可以简单地把它看成是 <code>root</code> 的别名,当访问到这个 server_name 的时候,就会自动在 <code>root</code> 目录下进行查询。如:</p>
<pre><code>server {
server_name test.com
root /htdocs
# some config
}</code></pre>
<p>当访问到 <code>http://test.com</code> 的时候,就会去到 <code>/htdocs</code> 目录下寻找目标。</p>
<p><code>http://test.com/a.html</code> => <code>/htdocs/a.html</code><br><code>http://test.com/public/a.html</code> => <code>/htdocs/public/a.html</code></p>
<p>这个是最基本的方法,当然你可以在 server 中配置其他,这里不再阐述。</p>
<p>其实说白了,他只是一个 “资源定位” 的作用。</p>
<p>很明显地,其实这里寻找的是 html 文件,也可以寻找 json,xml 文件,如:</p>
<p><code>http://test.com/a.json</code> => <code>/htdocs/a.json</code><br><code>http://test.com/public/a.xml</code> => <code>/htdocs/public/a.xml</code></p>
<p>其实都是一样,只要理解了 “资源定位”,那么其实,URL 就是访问一个文件内容,至于这个 “文件” 内部怎么执行,就需要自己定义了。</p>
<h4>Http Api 设计</h4>
<p>那么既然理解了上述的 URL 的意义,那么设计 API 起来就很好做了,所谓的 RESTful API 设计,其实我的理解无非就是让你清楚 URL 的作用,然后对应地给不同的 “文件” 进行操作。</p>
<h5>1. 文章的获取:</h5>
<p>Host: <a href="https://link.segmentfault.com/?enc=em0wQXgpIYUlsTkBVOwRfA%3D%3D.f6OLrtkRbKtl%2BiMUQ2hwCQ%3D%3D" rel="nofollow">http://test.com</a></p>
<h6>1.1 文章列表</h6>
<pre><code>http://test.com/articles/{page}
http://test.com/articles/1
http://test.com/articles/2</code></pre>
<h6>1.2 文章详情</h6>
<pre><code>http://test.com/articles/{page}/{id}.{format}
http://test.com/articles/1/3.html
http://test.com/articles/2/12.json</code></pre>
<p>以上理解为:</p>
<p><a href="https://link.segmentfault.com/?enc=uuR7F2Q67Dh3m9TjTrcYzg%3D%3D.PNE6oeL74u2VO8dPJVe2j72CUx2X%2FRu1J7AXMb2yJasg%2BvamFjzP0pUX2z53VYnC" rel="nofollow">http://test.com/articles/1/3.html</a> 这个地址定位到: /htdocs/articles/1/3.html<br><a href="https://link.segmentfault.com/?enc=4RU5gBP%2FjKRL29xCHMee3w%3D%3D.80mxilBuq4ZYokiMjmOVWwYthDMyYOGGo0SIPrFgYp5l1A5hdSfs1EYo8KQWsZU9" rel="nofollow">http://test.com/articles/2/12.json</a> 这个地址定位到: /htdocs/articles/2/12.json </p>
<p>此处有一个 <code>format</code> 用于决定响应的资源类型。需要考虑下吧,都定义为 <code>json</code> 就好了。</p>
<h6>分页设计(优化版)</h6>
<p>设计一个文章列表,可分页查询,可以查询文章详情。</p>
<pre><code>http://test.com/articles/{id}?page={page}&limit={limit}</code></pre>
<p>如果存在文章 <code>id</code> 详情信息,则返回指定的文章详情。若非查询制定的文章内容,则显示分页的文章信息。</p>
<p>分页请求:</p>
<pre><code>http://test.com/articles/?page={page}&limit={limit}
// or
http://test.com/articles?page={page}&limit={limit}</code></pre>
<p>第一种更为合理,因为该资源是一个 "目录",请理解 "根" 的概念。</p>
<p>内容请求:</p>
<pre><code>http://test.com/articles/1</code></pre>
<p>可以可以,这样就完成了请求的样例。</p>
<p>很好理解吧,这样设计出来的 API 其实可以收是一目了然。在资源后缀上,是可以自己定义的,如 <a href="https://link.segmentfault.com/?enc=PMgogInKaTUbfwQU6o%2Bo4Q%3D%3D.%2BTa%2Bk0THBxs%2FL9BsseOiIUbUSZSPyLzyD5hUyLHE9EdUqkXWBpH3EZpsBm4e7gr%2F" rel="nofollow">FastD</a> 中就有此项功能,可以根据不同后缀返回不同的内容格式。</p>
<p>以上仅是个人理解及想法,欢迎指点与批评。</p>
说说自己写PHP框架的一些感受
https://segmentfault.com/a/1190000004282650
2016-01-10T23:12:47+08:00
2016-01-10T23:12:47+08:00
黄总
https://segmentfault.com/u/janhuang
2
<h2>说说自己写PHP框架的一些感受</h2>
<h3>个人感受</h3>
<p>我以前觉得,造一个框架,不是很难,其实我现在也是这么认为的,不是说我到底有多么厉害,其实我仅仅是觉得,造出来不难,恰恰是造出来之后,你要去 “养育” 它,这对我来说,则是最难的,我现在正是体会到了。</p>
<p>不知不觉,我已经写过3个框架,其实都均已模仿他人框架为主的,然后肯定没有对方的好,当然有自己的一些想法融入里面了。然而,目前还存活的还剩下最后一个,他就是 <a href="https://link.segmentfault.com/?enc=A74e0Az6LarrEFkak91lBg%3D%3D.PKm0amOMP6vY3RMQjE6EdBwrfZIjWNmHQn5W%2Ffm5I4mh%2FJ4lw5Cfr4m4dZ6Fg2dC" rel="nofollow">FastD</a>,不去总结不知道,这个东西喔已经 “养”了快一年了,感觉还不错,算是3个框架下来,比较 “好” 的了。</p>
<p>FD第一次拿出来的时候如果我没有记错的话是 2015年的新年后的第一个工作周,当时拿了出来演示了给部门的同事看,大家的反应都不错,这让我激情四射啊。?</p>
<p>其实说老实话,我觉得自己也是一枚菜鸟及新手。造这个框架的初衷仅仅是想尝试下,然后进而发展到,让其他人都用上,目前算是实现了目标吧,因受 <code>Symfony</code> 框架的影响哈。</p>
<hr>
<h3>个人看法</h3>
<p>然后我说说自己对当前PHP框架的一些看法吧。</p>
<p>我觉得现在市面上充斥着很多的 php 框架,其实每个框架上,都有自己好的一面,也就是可以参考的地方。其实我偶尔会在一些群上看到 "xxx是世界上最好的框架,没有之一",额,这到底对 xxx 框架有多狂热呀,和 “xxx是世界上最好的语言,没有之一” 一样,虽然说是老梗了,但其实是毫无意义的,包括有一些工作久的也会犯这些毛病。额,不好说吧,唉,自己顺手就好。</p>
<p>我接触的框架也不算多,也就几个,寥寥可数啊。但其实细心可以发现一个很基础的点就是:<strong>路由是一个框架灵魂</strong>,这么说不是没有道理的,目前咱们看到的框架,大部分都是只处理 “一个” 事情,<code>Http</code> 请求。而这个请求,看来则是一个由 “控制器” 及 “方法” 组成的指定地址而已,也就是咱们开发中所说的 <strong>路由</strong>,每个请求都是一个 <strong>路由</strong>,第一时间经过的,一定是路由,由路由负责解析到指定的方法。那剩下的就是处理了,至于是MVC还是其他,自定定制吧。</p>
<p>嗯嗯,还有,顺便提醒一下咱们这些搞PHP的,<strong>一定要学好面向对象</strong> <strong>一定要学好面向对象</strong> <strong>一定要学好面向对象</strong></p>
<p>好了骚年,继续努力吧。</p>
<p>我个人推荐学习的有几款框架:</p>
<ol>
<li><p><a href="https://link.segmentfault.com/?enc=plnnnLSR9Dj%2Bs695kcfWKw%3D%3D.vZ5U2EHbunQOkjYiADNnLvroKErIcffe5T6mhqJN1Uw%3D" rel="nofollow">Symfony</a> / <a href="https://link.segmentfault.com/?enc=eFfF59ao9IhKr1HQmbUcaA%3D%3D.EGuD0VrpdnbLewrH8VqbDGxks4bV41ChHJONaZcet7s%3D" rel="nofollow">社区</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=glbMFiL4wqltBU5aRlgBiQ%3D%3D.NHLqOzFwrKOnIUBe9s5Gb35krC1EVcKvnQ0XD1Gw2tI%3D" rel="nofollow">Laravel</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=eGesf1MIhHaFdzGY0%2ByOcA%3D%3D.9UMupi2W5Me3Ao6BcTxHb9BmJsYt8CD678OAMRMPCaI%3D" rel="nofollow">Yaf</a></p></li>
</ol>
<p>顺便推广一下 <a href="https://link.segmentfault.com/?enc=%2BcezQpb7MsgTUwZjVTG7ow%3D%3D.GZg4GPBKHr2iAgo3MjxldkLLP1tdQa540VdQOqzcd%2Bs%3D" rel="nofollow">Swoole</a></p>
<p>PHP比你想象中的要牛逼。</p>
Nginx 动态添加模块
https://segmentfault.com/a/1190000004282498
2016-01-10T22:40:11+08:00
2016-01-10T22:40:11+08:00
黄总
https://segmentfault.com/u/janhuang
1
<h2>Nginx 动态添加模块</h2>
<blockquote><p>这个在工作中遇到的其中一个活生生的例子,因为 <code>nginx</code> 在编译安装的时候并没有考虑太多,而且我一向的做法都是按照项目需要安装相应的功能,尽量让服务和应用简洁。</p></blockquote>
<p>我一向的作风:</p>
<pre><code>./configure --prefix=/path/to/nginx</code></pre>
<p>? 是不是够简洁,仅仅一个安装路径。如果兄弟们觉有欠妥的话,请直接批评我,谢谢。</p>
<p>广告回来继续。</p>
<p>......</p>
<p>成功将 <code>nginx</code> 安装完, 然后可以将二进制的 <code>nginx</code> 管理脚本配置到系统 <code>PATH</code> 或者做个软链接到出来。</p>
<p><code>/path/to/nginx/sbin/nginx</code> 启动 <code>ngin</code>。</p>
<p>因为当时系统服务需要支持 <code>https</code>, 所以以上的配置炸了,根本不支持,所以就得动态给 <code>nginx</code> 添加ssl证书的支持。</p>
<p>首先要确保系统的 <code>openssl</code> 有正常安装。没有安装的朋友也不要灰心,只需要安装一下就好了。</p>
<pre><code>yum install -y openssl-devel</code></pre>
<p>确认安装完成以后,查看一下 <code>nginx</code> 当初的配置 <code>/path/to/nginx -V</code></p>
<p>然后回到以前的 <code>nginx</code>, 复制一下刚才的配置信息 <code>./configure --prefix=/path/to/nginx --with-http_ssl_module</code> </p>
<p>保留自己一贯的作风。 ?</p>
<p>然后 <code>make</code>, 记得不要 <code>make install</code> 不要 <code>make install</code> 不要 <code>make install</code>!!! 重要的事情说三遍</p>
<p>然后这里是需要停掉 <code>nginx</code> 服务,才能覆盖二进制的管理脚本。</p>
<p>复制 <code>cp objs/nginx /path/to/nginx/sbin/</code></p>
<p>覆盖即可。</p>
<p>查看: <code>nginx -v</code></p>
那是我夕阳下的奔跑
https://segmentfault.com/a/1190000004282416
2016-01-10T22:13:15+08:00
2016-01-10T22:13:15+08:00
黄总
https://segmentfault.com/u/janhuang
0
<p>额...<br>我也不知道为何自己那么喜欢折腾。</p>
<p>自己想写一个网站,额,但是......现在有这么多的网站提供这样的功能,而且做得都比我好,我还有必要去自己造一个?</p>
<p>项目练习?额,我觉得不是,因为我自己的能力和实力都可以做到,有必要花这个时间在上面,还是自己去学习其他?</p>
<p>额,纠结,请叫我纠结帝</p>
FastD
https://segmentfault.com/a/1190000002975432
2015-07-09T18:16:05+08:00
2015-07-09T18:16:05+08:00
黄总
https://segmentfault.com/u/janhuang
2
<p>最近终于放下手头的一件小事了。就是本人的框架官网终于弄完了,哈哈。地址: <a href="https://link.segmentfault.com/?enc=FJF%2FWZNzj%2FZdGThlvovnfQ%3D%3D.m3ao9ZRFJoxBkW0IpmAOOiTgOS5zld6X1xxXAgVGsCGx8N6QRoVlUF%2FxovqiQ3QL" rel="nofollow">fast-d.cn</a>。大家多多指教。</p>
<p><strong>fast-d</strong> 是以往 <code>dobee + fast-d api</code> 合并而来的。组件也全部更名为 <code>fastd-components</code>了</p>
<p>可以继续开展更多的学习了,酸爽。</p>
Dobee Routing 组件
https://segmentfault.com/a/1190000002797266
2015-05-26T18:29:58+08:00
2015-05-26T18:29:58+08:00
黄总
https://segmentfault.com/u/janhuang
0
<blockquote>
<p>Dobee php routing component. 框架路由组件</p>
</blockquote>
<p>改玩意也是依赖composer的哟。</p>
<p>项目地址:</p>
<ol>
<li><strong><a rel="nofollow" href="https://github.com/JanHuang/routing">github</a></strong></li>
<li><strong><a rel="nofollow" href="https://coding.net/u/janhuang/p/routing/git">coding</a></strong></li>
</ol>
<h2>由来</h2>
<p>其实一开始不打算做这个东西的,因为现在到处都是,之前也讲过,本人也是一个爱作死爱折腾的人,所以还是参考了一些别人的,做了一下玩玩</p>
<h2>安装</h2>
<p>此玩意已经推托管到<strong><a rel="nofollow" href="https://packagist.org/packages/dobee/routing">packagist</a></strong>了,用composer装逼的同学可以试试。但是鉴于国内的那道墙和网络,实在不敢恭维呀,苦了墙内的小伙伴们,还是直接到<strong><a rel="nofollow" href="https://github.com/JanHuang/routing">github</a></strong>或者<strong><a rel="nofollow" href="https://coding.net/u/janhuang/p/routing/git">coding</a></strong> clone下来吧。</p>
<h2>故事/发展</h2>
<p>一开始的时候,搞这个东西,是用注释去配置陆游的,但是后来发现在项目中如果这样配置,程序每次初始化,都需要加载分析一次全部路由,每个请求一次(我擦,这玩笑开大了),然而对系统的开销是很大的。所以这样的一个不足有几点:<br>
1. io<br>
2. 程序对注释段分析<br>
3. 不好排查</p>
<p>那么应对的也有几个方案:<br>
1. 在生产环境部署的时候缓存所有路由配置,形成路由表文件<br>
2. 整合命令行工具,通过命令行排查<br>
3. 代码常驻到内存里面 (<strong><a rel="nofollow" href="http://swoole.com/">swoole</a></strong>)</p>
<p>后面经过一段的调整,把他拆直接通过全局 <code>Routes</code> 对象创建,感兴趣的同学可以安装试试,玩玩,对象里面的方法我就不讲了,你们都懂得,看不懂的去面壁吧。</p>
<p>目前路由配置的方法是类似 <code>laravel</code> 的路由配置的,但请不要喷我,我只是参考了而已,折腾了下。</p>
<p>目前支持 <code>get</code>, <code>post</code>, <code>group</code> 其他还没具体测试过用例,小伙伴们可以帮下忙哦,有问题直接反馈或者在项目上 <code>issue</code></p>
<h2>感受</h2>
<p>做这个玩意,当时也是抱着试一试的想法去做,没想到就做成现在这样,现在也用上了正途,虽然偶尔会发现点bug,但是我特么都解决了。多烂的东西只要持续去做持续去完善,总有一天会变好的,这叫打磨。首先你要去尝试。是吧,包哥。@<strong><a rel="nofollow" href="http://www.runnerlee.com/">runnerlee</a></strong>,很荣幸有这大神陪伴,感觉自己就是个渣。</p>
<h2>用例</h2>
<h4>GET</h4>
<pre><code>Routes::get('/', function () {
return 'hello world';
});
</code></pre>
<p>每个配置之后都 <code>return \Dobee\Routing\RouteInterface</code> 对象,也就是说,以上用例配置完之后,可以执行一系列的链式操作。比如:</p>
<pre><code>Routes::get('/', function () {})->setFormat(['html', 'php'])->setXX()->setXX;
</code></pre>
<p>最用用<strong><a rel="nofollow" href="http://www.jetbrains.com/phpstorm/">PHPStorm</a></strong>吧,有提示的。</p>
<p>不说了,这么简单的东西,你看看,刚入门都懂了,你们,应该都透了,我就不浪费口水了,去撸吧,程序猿。</p>
<h2>关于作者</h2>
<p>不得不说我是一个喜欢折腾比较作死的人。常言道:生命不止,折腾不息。干我们这行的,不是被人干就是干别人(通常都是被人干)。趁年轻,干点自己喜欢的事情呗,反正自己开心,喜欢就得了,哪怕是重复造轮子。喜欢的,总有收获。感恩各位小伙伴指点。</p>
<h2>关于大神Runnerlee</h2>
<p>他威胁我不能说。</p>
关于Dobee
https://segmentfault.com/a/1190000002794616
2015-05-25T22:54:41+08:00
2015-05-25T22:54:41+08:00
黄总
https://segmentfault.com/u/janhuang
1
<h2>What is “Dobee”</h2>
<p>其实Dobee,是一个很简单的轻量级PHP框架,composer管理安装。</p>
<p>项目地址:<br>
1. <a rel="nofollow" href="https://coding.net/u/janhuang/p/dobee-php-simple-framework/git">coding</a><br>
2. <a rel="nofollow" href="https://github.com/JanHuang/dobee-php-simple-framework">Github</a></p>
<p>欢迎大家去吐槽一下。</p>
<h3>由来</h3>
<p>整个项目的来龙去脉,其实是来源于生活中的那个东西:<strong>乐高积木</strong>,小时候经常玩的一个玩具,乐高积木有啥特性哇?就是很多个部件,然后提供不同形状,凸点,凹点,自己自由组合,那么这些凸点和凹点其实可以相比作为平时开发当中的标准化"<strong>接口</strong>",只要你的另外一个部件能够符合这个接口,就可以安装接入了。框架本身也参考了这个和一些开源框架的特性,如symfony, laravel等,当然后没有那么精湛。其实就一逗比嘛。</p>
<hr>
<h3>造轮子</h3>
<p>首先做这个东西,其实也算是一个重复造轮子的过程吧。当然有时候,重复造轮子,还是有必要的,过程中遇到各种不一样的问题,现已不也统统解决了嘛,所以说嘛,重复造轮子,有时候还是需要试试的,因为做过可以做得更好。</p>
<hr>
<h3>设计</h3>
<p>框架中采用组件依赖整合的特性,其核心本身就没有什么东西,就是一个粘合剂,整合了各个组件,让其可配置,通用化而已,然而并没有什么卵用。所以其实核心在于每个组件上面,上面实现的东西才是至关重要的。也因为正因如此,每个组件都是独立更新,互不干扰的。提倡灵活,轻便,这句话说出来跟屁话一样哈哈。</p>
<h3>未来畅想</h3>
<p>至于未来,我觉得互联网这世界正在发生翻天覆地的变化,PHP也是。以前的PHP大家都在停留在web开发阶段,我想,现在的PHP应该不只是在这个层面了。为毛,因为咱们有了<strong><a rel="nofollow" href="http://www.swoole.com/">swoole</a></strong>。所以,在框架上面,以后会着重发展服务端开发。也暂时不太确定是否适合用这玩意进行服务端开发,先看看吧,反正会出力的,凡事都要先试试再说。钢铁侠他爹跟我说的。</p>
对面向对象的理解
https://segmentfault.com/a/1190000002503927
2015-01-21T23:26:23+08:00
2015-01-21T23:26:23+08:00
黄总
https://segmentfault.com/u/janhuang
0
<p>好,今天来给大家总结一下我觉得的面向对象。</p>
<p>首先,我们带着问题去想。</p>
<p>问题就是:什么是面向对象?</p>
<p>过去一些学校老师每当讲到面向对象的章节,都会猫不犹豫的定义一些class(类)。那么,这就是面向对象了么?过去我也是这么认为的,但现在看来,其不然。</p>
<h3>那么到底什么才是面向对象呢?</h3>
<p>相信到这里大家都有一些疑惑。那平时咱们写那么多的类,难道都不是面向对象?我很遗憾的告诉你,并不全是。包括我一年前刚入职写了很多代码,也维护过很多'前辈'的项目,当时又傻又天真的以为:'哇,这代码真特么牛逼,一个类就几百几千行'。</p>
<p>现在回头一想,哎呀我去,我都没眼看了,那些代码,跑着跑着,是会出bug的,他们的质量不过关就,那上去跑了,跑条毛啊,最终还不是返回来给我们改bug。</p>
<p>到目前为止,我已经慢慢开始转变了这些想法。其实那都不是叫面向对象,因为他们把不同类本身该有的事情都综合起来一个类里面,所以它本身只是个类,并不具备面向对象的基本特质。</p>
<p>回来正题,继续探究面向对象。想想我们人类。举个简单的例子:</p>
<pre><code>/**
* 抽象人类,人们说的人类,反正就是随你想的
*/
Interface Human
{
public function setLastName($last_name);
public function getLastName();
public function setFirstName($first_name);
public function getFirstName();
}
/**
* 我老爸
*/
class Father implement Human
{
/**
* 姓,在国内,姓是不能随便改的,当然也是可以改。所以这里修饰成保护
*/
protected $last_name;
public $first_name;
public function setLastName($last_name)
{
$this->last_name = $last_name;
return $this;
}
public function getLastName()
{
return $this->last_name;
}
public function setFirstName($first_name)
{
$this->first_name = $first_name;
return $this;
}
public function getFirstName()
{
return $this->first_name;
}
}
/**
* 儿子 - 我
*/
class Son extends Father
{
}
$father = new Father();
$father->setLastName('黄');
$son = new Son();
$son->setFirstName("总");
// 打印我的名字
echo $son->getLastName() , $son->getFirstName();
</code></pre>
<p>既然我继承我老爸,当然是跟我老爸姓的啦。要改姓也可以,得问问老爸是否同意先。</p>
<p>不过一般我们的做法是</p>
<p>直接定义一个'Son'类,然后里面各种方法,甚至一些人类不可以做到的方法,比如会飞(不借助工具),反正就是一个类里头夹杂各种不可思议的方法,真的是万万没想到。那么如果是这样,那其实我就不认为这是面向对象了,而是简单的定义类而已。</p>
<p>其实面向对象是有一系列的继承关系,实现关系,依赖关系组成。写代码,做开发,其实和生活是息息相关的,多留意自己的生活,多拿生活例子在开发工作中举例,其实,原理就一目了然了。</p>
<p>继续回来上面的话题,继续面向对象。</p>
<p>比如说,我老爸会使用工具,但是我也会,而且过程,结果大不相同,而且使用的工具也不一样,而且这个工具是需要买,或者找别人借的,不是自己家的。结合上面的小例子稍微修改一下。</p>
<pre><code>/**
* 抽象人类,人们说的人类,反正就是随你想的
*/
Interface Human
{
...... // 代码省略
public function useTools(ToolInterface $toolInterface = null);
}
/**
* 我老爸
*/
class Father implement Human
{
/**
* 姓,在国内,姓是不能随便改的,当然也是可以改。所以这里修饰成保护
*/
...... // 代码省略
public function useTools(ToolInterface $toolInterface = null)
{
$result = $toolInterface->useTool();
......//代码省略,自由发挥
// 用工具得到结果,经过老爸一系列加工,变成了不一样的东东
return $result
}
}
/**
* 儿子 - 我
*/
class Son extends Father
{
...... // 代码省略
public function useTools(ToolInterface $toolInterface = null)
{
// 使用工具,我想调整一下这个工具再使用
......// 调整中
$result = $toolInterface->useTool();
......//代码省略,自由发挥,加工结果
return $result
}
}
Interface ToolInterface
{
public function useTool();
}
class Nokia implements ToolInterface
{
// 代码自己想想
public function useTool();
}
class IPhone20Plus implements ToolInterface
{
// 代码自己想想
public function useTool();
}
$father = new Father();
$father->setLastName('黄');
$son = new Son();
$son->setFirstName("总");
// 打印我的名字
echo $son->getLastName() , $son->getFirstName();
// 使用工具, 我不管他们怎么使用,反正我就是给了他了。而且,只要是个工具(实现ToolInterface)就行了
$father->useTool(new Nokia());
$son->useTook(new IPhone20Plus());
</code></pre>
<p>那么其实到这里,你可以很清晰的看到,其实这,才算是"我理解"的面向对象。每个类都有自己单独,独有的方法,不同的操作。</p>
<p>而且在后面工具的哪里,我并没有强制的在内部是使用工具,而是在外部通过注入的方式去实现想要的。</p>
<p>再想想现实中,我们用的iPhone,安卓,到底是哪里来的呢?就是买来的嘛,总没有说自己出一款,就算是自己出的,零件也是需要依赖各个地方的吧。所以其实我们整个生活圈就是一个很大的生态系统,你在编程世界里面有的,基本上在生活当中都能找到影子。</p>
<p>而且这样有一个很好的好处,就是你会不知不觉的,降低了类与类之间的耦合性了,我们面向对象,要的不就是这个效果吗?再结合学过的知识,面向对象的特性,好好思考这些问题,不断考量,改善自己的这些想法。</p>
<p>希望这篇文章可以帮助到一些对这方面还有困惑,不解的小白。有什么地方说的不足的,还请各位大大指点,感谢各位。</p>
<h2>By Jan(我也是一个一年多的小白)</h2>
关于对聚合和组合的理解
https://segmentfault.com/a/1190000000666319
2014-09-12T17:19:00+08:00
2014-09-12T17:19:00+08:00
黄总
https://segmentfault.com/u/janhuang
0
<p>今天看了看</p>
<blockquote>
<p>深入PHP++面向对象、模式与实践_第三版</p>
</blockquote>
<p>其中看到“聚合”跟“组合”的概念,对此理解也比较模糊<br>
文中是这样说的:</p>
<p><img src="http://segmentfault.com/img/bVcXuQ" alt="请输入图片描述"><br><img src="http://segmentfault.com/img/bVcXuR" alt="请输入图片描述"></p>
<h2>理解上面文本中的话,我个人理解是成这样:</h2>
<p>首先是聚合</p>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: AggregationA
* User: JAN
* Date: 14-9-12
* Time: 下午12:18
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Aggregation;
class AggregationA
{
protected $aggregation = array();
public function __construct()
{
array_push($this->aggregation, new AggregationB());
array_push($this->aggregation, new AggregationC());
}
public function addAggregation(AggregationInterface $aggregation)
{
array_push($this->aggregation, $aggregation);
return $this;
}
public function getAggregationList()
{
return $this->aggregation;
}
}
</code></pre>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: AggregationB
* User: JAN
* Date: 14-9-12
* Time: 下午12:18
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Aggregation;
class AggregationB implements AggregationInterface
{
public function getName()
{
return $this;
}
}
</code></pre>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: AggregationC
* User: JAN
* Date: 14-9-12
* Time: 下午2:34
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Aggregation;
class AggregationC implements AggregationInterface
{
public function getName()
{
return $this;
}
}
</code></pre>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: AggregationInterface
* User: JAN
* Date: 14-9-12
* Time: 下午2:38
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Aggregation;
interface AggregationInterface
{
public function getName();
}
</code></pre>
<p>类内聚了两个类:分别是agregationB, agregationC 分别都实现了AgregationInterface, 而且在实力AgregationA的时候构造时实例化B和C。</p>
<p>再次是组合:</p>
<p>A类</p>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: CompositionA
* User: JAN
* Date: 14-9-12
* Time: 下午12:25
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Composition;
class CompositionA
{
private $compositionB = null;
private $compositionC = null;
public function setCompositionB(CompositionB $compositionB)
{
$this->compositionB = $compositionB;
return $this;
}
public function getCompositionB()
{
return $this->compositionB;
}
public function setCompositionC(CompositionC $compositionC)
{
$this->compositionC = $compositionC;
return $this;
}
public function getCompositionC()
{
return $this->compositionC;
}
}
</code></pre>
<p>B类</p>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: CompositionB
* User: JAN
* Date: 14-9-12
* Time: 下午12:25
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Composition;
class CompositionB implements CompositionObjectInterface
{
public function getName()
{
return $this;
}
}
</code></pre>
<p>C类</p>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: CompositionC
* User: JAN
* Date: 14-9-12
* Time: 下午2:42
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Composition;
class CompositionC implements CompositionObjectInterface
{
public function getName()
{
return $this;
}
}
</code></pre>
<p>接口</p>
<pre><code> <?php
/**
* Created by PhpStorm.
* ClassName: CompositionObjectInterface
* User: JAN
* Date: 14-9-12
* Time: 下午12:29
* Link: http://blog.segmentfault.com/janhuang
*/
namespace DesignPattern\SimpleExample\Composition;
interface CompositionObjectInterface
{
public function getName();
}
</code></pre>
<p>在实例化CompositionA 的时候并不会直接实例化其他类,这里只是清楚限定了CompositionB类作为参数类型传入setCompotionB方法,这里理解的组合就是:有提供组合的方法提供外界指定的类进行自由组合,并且使用组合当中的方法。</p>
<p>以上仅是个人的理解,不知道这对类的关系有没有帮助,最近在学习设计模式,概念有点模糊,还请各路英雄指点。感谢</p>
资源收集(有资源就会更新)
https://segmentfault.com/a/1190000000445244
2014-03-25T18:48:32+08:00
2014-03-25T18:48:32+08:00
黄总
https://segmentfault.com/u/janhuang
7
<h2>个人</h2>
<p><a href="https://link.segmentfault.com/?enc=7Aw7nyO38bPolbn3JOKGmA%3D%3D.7x%2F0gi1v9OUHM%2BltO%2FlAKsT1pW61isiZOaBvGNMTQofpUyQJkvFYvwYmcRbbSmeV" rel="nofollow">Fast D</a></p>
<h2>文档参考</h2>
<p><a href="https://link.segmentfault.com/?enc=XlpPPWiHQ3CT32A8akbvxg%3D%3D.x9TmcWpODoNOlvetww73GmbelMz7szhNsg%2FtNj4Jbpix8uBkLwVY%2FesgYucjdaBs" rel="nofollow">Doctrine 2</a><br><a href="https://link.segmentfault.com/?enc=UesGQQmhFaHI7OFndnKuCg%3D%3D.a62KO1S6nGD7n8sWwhM2wtX%2FUGpjuIn8CGwBnLE84Gg%3D" rel="nofollow">php spec</a><br><a href="https://link.segmentfault.com/?enc=O02kLKblXblNtSPluVWpow%3D%3D.3i34HtuUW1anD5ld0MoucspvRVOb%2F1E%2By9TCgSsYkepNNoD9Lch5MJSygjwOa247" rel="nofollow">Doctrine Document</a><br><a href="https://link.segmentfault.com/?enc=voVihUYywvlouCt0bdJINg%3D%3D.fEAzI4X03OZXBilH1TM%2B06A9o7HQdWkeSUWtauepuISc0ZC8v2HSO8HBfCTieTBt" rel="nofollow">Symfony cheat sheet</a><br><a href="https://link.segmentfault.com/?enc=cYl4l%2Bg2IoAeaF%2FHH6%2BX9w%3D%3D.fQNrnI9lMC4WgMc10K%2Bzi89I5nFq0SE9xIXYQ5C%2BAphLPGSLI4AnucB8k9kKYpcA" rel="nofollow">HTTP API Design Guide</a><br><a href="https://link.segmentfault.com/?enc=5fITQSoP4TCiiag3Wxhtog%3D%3D.Tn5ijgy8myBPP0LRxxFZew%2FI2bjtl3nMBIPH3LPw4MQ%3D" rel="nofollow">pro git中文</a><br><a href="https://link.segmentfault.com/?enc=Z%2B%2F8TqgDI8dD78fcx%2BZFww%3D%3D.3EmLuV4yZ9GqO1eq4rtFWnvoonX7J8Y76F7E9HRgxBhGT441eeafhWgPo9wmWW%2Ff" rel="nofollow">Swift 入门</a><br><a href="https://link.segmentfault.com/?enc=IqK5WGjLQ4AhmbWvLICexQ%3D%3D.ofyGV1WgtkleOln6C5mepx0Z9%2Fe3e7wuDU1ZU5s7i56v3SAZ4fwvG2RaI0oeAKLm" rel="nofollow">C Lang</a></p>
<h2>工具资源</h2>
<p><a href="https://link.segmentfault.com/?enc=kjH6ywGWc5Zn9QokUrBPlQ%3D%3D.fei480IrGOyz%2Bs8kXtGdb38qRVaFX2SpjfSZSQ2wJ97363dcsxpgX42QS%2FKxYzbB" rel="nofollow">在线正则匹配</a><br><a href="https://link.segmentfault.com/?enc=4EI4RjrI9aK9D7DIuwQtBw%3D%3D.PTEVXLiZ0i65wQJZ%2Bou37O%2BftUOGKV00UrXotgHKBzFPfsmmuB%2ByunYA%2FPze30lP" rel="nofollow">PHPUnit手册</a><br><a href="https://link.segmentfault.com/?enc=US9AzxBhcvWqJJceoPIb9g%3D%3D.gV4dZ7flxEoYfdghUN310N%2Fk%2F2pRSPad9qmdKXhs8zUr3g05RUZmdU76QYzg4zX%2B" rel="nofollow">Pjax 可以试试优化体验</a><br><a href="https://link.segmentfault.com/?enc=Tzgoy8YESquaVoY%2BM9JOnQ%3D%3D.Nx3lHPI7RAjLkrS1MSbL6EK4AUTfAfEvQ1bLX5v%2BCYdAkNgCgyBpO0JRXopGPgVi" rel="nofollow">Nprogress 进度条</a><br><a href="https://link.segmentfault.com/?enc=EMJslxOcZjv0sLYBoSA%2FQg%3D%3D.KijXtOwHIfH0AEh%2BwCKnxd6AhrBLu9Cl0zofvCR%2BCWg%3D" rel="nofollow">packagist.org</a><br><a href="https://link.segmentfault.com/?enc=6Vk8iihDUnbrWOcSGagAUg%3D%3D.el2vtwjLiq5og%2FY0NmgQII8pkqmPG%2FV54vhNeBvn3Od50HXOlLvo%2FGynKa%2FgKrgH" rel="nofollow">php-cassandra</a><br><a href="https://link.segmentfault.com/?enc=2oygCT6DLrKa21wpWRg%2FdQ%3D%3D.VpQZQ1SjWJdsUm0xtd1pdbpGBOf%2FmqpfVXikyGTIDrk%3D" rel="nofollow">PHP-GO</a><br><a href="https://link.segmentfault.com/?enc=rOrGLggMFI%2FtERqtsz6Opw%3D%3D.3t6fW3xW4qypxBK0eScWgUD%2FpGZomO5Cn1kqAh26uv8%3D" rel="nofollow">SSDB</a><br><a href="https://link.segmentfault.com/?enc=4uzY5a7cBBNB1kkvPMPMAA%3D%3D.hTcwBYHAvywZ%2F0uygF7Z4VKQR8B2aXIgzYMlRV%2Bst5U%3D" rel="nofollow">PHP Swoole</a><br><a href="https://link.segmentfault.com/?enc=GrADy8thHMRZoLp2wEoLFw%3D%3D.fpEWAQNHF7gwCREJZE8UnWo3Ggc6q3uOWSBZvZZuwfA%3D" rel="nofollow">Easyicon 专门搜索icon的网站</a><br><a href="https://link.segmentfault.com/?enc=Mhv6ApbYihCGcd%2BTsak5Dw%3D%3D.NXnzpkeDczRudJtJYcOeaAbnvppSmQmx13ZwqMPj9HY%3D" rel="nofollow">web开发工具(不知道大家合适不,如果多屏估计效果更好)</a><br><a href="https://link.segmentfault.com/?enc=FgTm%2Bd%2F%2FcAotI9I9CCiATQ%3D%3D.8pd8eI0gES%2B7Ti2kRw2Cm4kNL10hQ%2FzaZIkyxSOrdvw%3D" rel="nofollow">font</a><br><a href="https://link.segmentfault.com/?enc=3zHQFMKfY7gdpjdNPe3K5A%3D%3D.QA18EmU4ZwQ9%2BXmkJykdSJ5uUKk9OhBXxwhWTki9WuE%3D" rel="nofollow">在线排版网站</a><br><a href="https://link.segmentfault.com/?enc=maz5VqctOkd96c8coQOHAQ%3D%3D.G4o7HIInJuKbeY%2BJJkRB8cnXNhDkWFMcz7M%2FkdWzYSY%3D" rel="nofollow">PHP packagist makedown 标签</a></p>
<h2>开发教程</h2>
<p><a href="https://link.segmentfault.com/?enc=poWeRBK1W6OHW931t9nWww%3D%3D.WoDqFJWztOGt7L8xUZ6lPBUNlslKT%2FmSSLbztfOe7Ig%3D" rel="nofollow">symfony smyblog博客教程:</a></p>
<h2>其他</h2>
<p><a href="https://link.segmentfault.com/?enc=qQIpTaxDm9yKpNaYnA2Xyw%3D%3D.KxEu87gm5RIH%2FOvjtCX%2BVcWyJSDsa6iu4pAWgRWhRa0%3D" rel="nofollow">游戏中学习</a><br><a href="https://link.segmentfault.com/?enc=MsgRjza%2Bb1X6N6%2FYTN5Rag%3D%3D.3uqun%2FLMFaax%2BYwP2Cq2XLWd6r2CZxRX1whtM0PFrsY%3D" rel="nofollow">在线文档</a><br><a href="https://link.segmentfault.com/?enc=2jMbXvPCYRgRNKlPWXQL%2Fg%3D%3D.7yeQiN4BSAgS%2BPOFPuZbQoUPpgnkwRjUgGP%2BA1sCdXU%3D" rel="nofollow">Shell编程</a><br><a href="http://blog.segmentfault.com/cloudmario/1190000000635914">RESTful实践总结(站内)</a><br><a href="https://link.segmentfault.com/?enc=w%2Fs%2FCq5UgAx%2FFdxSAeNrqA%3D%3D.C0N%2F6%2B3COu3mzK8YIcsMVg8P6NcyOH0vcTp20f3pCJ0J111MHSbehXNqKkPW7Htn" rel="nofollow">如何创建一个自己的 Composer/Packagist 包 (PHP)</a><br><a href="http://segmentfault.com/q/1010000000575030">靠谱的API设计(来自segmentfault)</a></p>