php urlencode
公司里使用dubbo做rpc,启动时dubbo的service会向zookeeper注册其自身,包含一些参数,如版本号,服务地址等等信息,但是zookeeper里的信息是urldecode的,非常不友好,又不想总去找个网站做urldecode,这时候脚本语言的优势就显现出来了
|
|
非常简单的命令,执行时直接把zookeeper里的内容复制下来
php xxx.php $url 就可以在cli里看到实际内容了
公司里使用dubbo做rpc,启动时dubbo的service会向zookeeper注册其自身,包含一些参数,如版本号,服务地址等等信息,但是zookeeper里的信息是urldecode的,非常不友好,又不想总去找个网站做urldecode,这时候脚本语言的优势就显现出来了
|
|
非常简单的命令,执行时直接把zookeeper里的内容复制下来
php xxx.php $url 就可以在cli里看到实际内容了
docker是一个开源项目,其目的是实现轻量级的操作系统虚拟化解决方案.
Docker 的基础是linux的容器(LXC)等技术
stackoverflow上的一个问题,二楼是vagrant的作者,三楼是docker的作者
Docker镜像就是一个只读的模板。例如一个镜像可以包含一个完整的Ubuntu镜像,里面仅安装了某些你需要的应用程序,比如Apache+Mysql+php等等
镜像可以用来创建Docker容器
Docker提供了一个简单的方式来创建镜像或是更新现有镜像DockerHub(有点像github),你可以直接从官方或是其他人那里获得镜像直接使用
Docker 利用容器来运行应用。
容器是镜像创建的运行实例,每个容器可以是隔离的,保证安全的平台。
镜像是只读的,容器在启动的时候创建一层可写层作为最上层。
仓库是集中存放镜像文件的场所,有公有仓库和私有仓库
公有仓库就是我们前面说的DockerHub,用户可以在本地网络中创建一个私有仓库
用户可以创建自己的镜像并push到共有或私有仓库,方便下次或另外机器上使用
和github类似,不多说了
Ubuntu系统安装(我的是13.10系统 这种安装方式不是最新的Docker)
首先要注册一个Docker账户这里,邮箱确认后就可以登录了
登录后,可以输入$ docker run
来查看可用指令
接下来 试一试$ sudo docker run ubuntu:14.04 /bin/echo 'Hello world'
sudo docker run ubuntu:14.04
告诉 Docker加载 ubuntu 14.04。
Docker在运行时会判断当前运行的镜像是否加载到Docker主机上,如果没有,会到Docker下载指定的镜像
以上 就安装并运行了一个简单的Docker实例
我承认,我自己也忍不住用过这项技术。它简直太方便了,可以省去一次不必要的类型转化。这就是:
|
|
现在你便可以安全地将包装类转换成任意类型了:
|
|
这也正是你在使用JOOR时所用到的API,jOOR是我们开发并开源的一个反射工具库,它可以提升我们编写集成测试的效率。有了jOOR,你可以这么编写代码:
|
|
这个API非常简单。on()方法会对某个对象或类进行封装。call()方法会通过反射去调用这个对象上的方法(不需要签名正确,也不需要方法声明成public,同时也不会抛出任何的受检查异常)。同时你也不需要强制类型转换,便能通过get()方法将结果转换成任意的类型。
对于一款像jOOR这样的反射库而言,这么做是没问题的,因为这个库本身就不是类型安全的。当然不会安全了,因为本来就是反射。
不过这还是会让人感到有些不爽。感觉就是在调用点这里承诺会返回某个类型,但实际上却无法兑现,这很可能会导致ClassCastException异常——在有了Java 5以及泛型之后才开始写Java的开发人员是很难体会到这种感觉的
没错,JDK是这么干了。不过这样的情况并不多,并且只是在泛型参数的类型并不那么重要的情况下才会这么做。比如说,当你通过Collection.emptyList()获取一个空列表的时候,这个方法的实现是这样的
|
|
没错,EMPTY_LIST从List强转成List是挺不安全的。但从语义层面来说,这样的强转是安全的。你不能修改这个列表,因为这是个空列表。List中也不会有任何一个方法会返回给你一个非目标类型的T或者T[]的实例。因此,这些做法都是合法的:
|
|
JDK的开发人员从来都会非常小心地不去对你所可能获取到的泛型类型做出任何无法兑现的承诺。也就是说,即便存在一个更适合的类型的时候,通常返回给你的也都是Object类型。
哪个类型更适合只有你知道,编译器可不了解。类型擦除是有代价的,代价就是当包装类或者集合为空的时候。像这样的一个表达式编译器是无法得知它的类型的,它可不能不懂装懂。也就是说:
不要使用这种只为了省一次类型转化的泛型方法的反模式。
这是segmentfault上的一个系列,做业务做累了就该找点东西玩玩,不是么?
但是说实话,这仅仅是了解,熟悉真谈不上,业务中没有真正使用到,都是纸上谈兵
原文地址:Day 1: Bower —— 管理你的客户端依赖关系
英文原文在这里
由于环境的千差万别,遇到的问题肯定会不一样,所以我在跟着原文使用时也会遇到一些问题,我会将我遇到的问题整理出来
Bower是一个客户端技术的软件包管理器,它可用于搜索、安装和卸载如JavaScript、HTML、CSS之类的网络资源。其他一些建立在Bower基础之上的开发工具,如YeoMan和Grunt,这个会跟着写 :)
为了安装bower,你首先需要安装如下文件:
安装好准备的东西,就可以安装bower了sudo npm install -g bower
-g 表示全局安装
|
|
运行bower help 时遇到了如下错误
/usr/bin/env: node: 没有那个文件或目录
修改/usr/local/bin/bower中的第一行,#!/usr/bin/env node
改成#!/usr/bin/env nodejs
再启动就好了
Bower是一个软件包管理器,所以你可以在应用程序中用它来安装新的软件包。举例来看一下来如何使用Bower安装JQuery,在你想要安装该包的地方创建一个新的文件夹,键入如下命令:bower install jquery
由于没有权限 安装出现错误
然后使用sudo = =#
提示就不翻译了,使用如下命令 安装成功$ bower --allow-root install jquery
引起这个的原因是因为bower是在当前文件夹下创建bower_components文件夹,而bower是没有root权限的(我是因为改动/usr/local/bin/bower进入到/usr/local/bin/下的,所以运行命令时就会遇到权限问题),所以在root权限文件夹下使用该命令就会导致这种问题,如果你一定要在该文件夹下创建,可以使用–allow-root参数,或者修改文件夹的权限
现在就可以在应用程序中使用jQuery包了,在jQuery里创建一个简单的html5文件:
正如你所看到的,你刚刚引用jquery.min.js文件,现阶段完成。
|
|
bower.json文件的使用可以让包的安装更容易,你可以在应用程序的根目录下创建一个名为“bower.json”的文件,并定义它的依赖关系。使用bower init 命令来创建bower.json文件:
可以查看该文件view bower.json
已经加入了jQuery依赖关系
现在假设也想用twitter bootstrap,我们可以用下面的命令安装twitter bootstrap并更新bower.json文件:$ bower install bootstrap --save
它会自动安装最新版本的bootstrap并更新bower.json文件
原谅我这一生不羁放纵爱自由,上班时间写博客,罪过罪过。
用过java的maven管理,理解起来这个就相对简单了。
搭建依赖库时,最讨厌的就是到处找依赖包不是么,而有了这些工具之后,都省掉了。
这大概是很多人都想知道的吧,开发的时候,能看到程序真正执行的是什么可以减少很多时间去调试。
尤其是在当今IOC,MVC框架中,很多只懂得复制粘贴的程序员们遇到问题就只能问,根本不去深究为什么可以这样
废话真多,不说了,先来看这段代码
如果直接var_dump($sql);返回的只是绑定的sql,即一个不完整的sql,还要去var_dump($id),才能完整地显示出来sql语句,而有时候,这种调试显得异常笨拙。
我们急需一种方法来快速定位并解决sql问题
|
|
使用man tcpdump可以查看tcpdump工具的具体使用
其他的就不多解释了,也不会,跟着干货师傅学到的~
这样就能在本地看到有一部分包中包含如下内容
|
|
实际上能看到发送的包是因为mysql的预处理机制
这里有一些关于预处理的翻译
这里的note也不错
With PDO_MYSQL you need to remember about the PDO::ATTR_EMULATE_PREPARES option.
The default value is TRUE, like
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,true);
This means that no prepared statement is created with $dbh->prepare() call. With exec() call PDO replaces the placeholders with values itself and sends MySQL a generic query string.
The first consequence is that the call $dbh->prepare(‘garbage’);
reports no error. You will get an SQL error during the $dbh->exec() call.
The second one is the SQL injection risk in special cases, like using a placeholder for the table name.
The reason for emulation is a poor performance of MySQL with prepared statements. Emulation works significantly faster.
如果添加如下改动,这种方式放弃了预编译
两个包的接收片段
|
|
尽管exec方法和查询在PHP中仍然被大量使用和支持,但是PHP官网上还是要求大家用预处理语句的方式来替代。为什么呢?主要是因为:它更安全。预处理语句不会直接在实际查询中插入参数,这就避免了许多潜在的SQL注入。
然而出于某种原因,PDO实际上并没有真正的使用预处理,它是在模拟预处理方式,在将语句传给SQL服务器之前会把参数数据插入到语句中,这使得某些系统容易受到SQL注入。
实际上$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,true); 使得pdo将语句进行了本地处理,并直接向mysql提交了sql语句。
而$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);则是先向sql服务器发送prepare中的语句,接受到服务器返回的一些数据(meta data),再发送参数
这样做安全,但是速度变慢了
今天在处理dbrt时,类似的建表语句如下:
对于这种字段,需要赋予默认值,然后有人说,我设置的不为null,就是不希望有空值插入进去,所以这里不需要设置default。
首先,我们要搞清楚“空值” 和 “NULL” 的概念:
|
|
所以空值还是会被插入到db中,只有NULL列才不会被插入。
NULL列需要更多的存储空间,还需要mysql内部进行特殊处理。NULL列被索引后,每条记录都需要一个额外的字节,还能导致MYisam 中固定大小的索引变成可变大小的索引。这在《高性能mysql》中有介绍的,
所以为了尽量避免NULL,我们指定列为NOT NULL。
可以看出,在MySQL中,含有NULL的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
##问题
同事问了一个问题:
框架底层对executeSql进行了封装(对sql进行了处理,做主从分离),在执行insert xxx values xxx on duplicate key update语句后,使用lastInsertId时返回结果是0。
由于要判断是否插入失败,所以分析了一下框架executeSql,除了select,desc 其他语句使用master进行处理(主要是为了实现框架级别的主从分离),在PDOStatement::execute之后,
对操作语句的类型进行了判断,对于insert的返回,执行成功的结果都是返回PDO::lastInsertId,如果执行失败,则返回false.
查看了PDO::lastInsertId 的说明手册
但是我的数据库底层支持自增字段或序列啊,不应该返回0,结果仔细看表发现,由于表是从其他表中获取有效数据同步过来的,所以没有自增字段(auto-increment),
这样,判断executeSql是否执行失败,需要使用result === false来判断了
经过良好设计的系统一般通过方法调用传递对象实例。通常单例使用与如下背景环境:
1.对象会被系统中的任何对象使用
2.对象不应该储存在会被修改的全局变量中
3.系统中不应该超过一个该对象,也就是说Y设置了X对象的属性,Z可以不通过其他对象,直接获取到该属性的值
因此,单例对象的构造方法应该是私有的
|
|
但是这样是不能用的,因为构造方法是私有的。所以需要静态方法和静态属性来间接完成实例化。
|
|
单例模式和全局变量依然会被误用,因为它可以在任何地方被使用,所以很难调试它的依赖关系(比如公司的框架,对pdo进行了封装,而在对某些语句进行跟踪调试时,就变的困难了,因为不只是一处去调用该模块)
关于单例模式,java中有很多种实现方式,每一种都有自己的优缺点,另外java中有时候单例模式有时候会失效,这些都是语言层级了,就不多写了
略。注意安装依赖,有些命令需要安装依赖
一个启动的例子
|
|
说明
|
|
结束
`sudo pkill varnishd`
|
|
|
|
1.三个重要的数据结构
- req
请求头信息 当varnish接收到请求的时候 req会被创建- beresp
后端返回对象 包含从backend返回的头信息- obj
缓存了的对象。大多数是驻留在内存中的只读对象。obj.ttl是可以写的,剩下的都是只读的
更多变量点这里
2.子程序
VCL 文件被分为多个子程序 常用子程序
- vcl_recv 当有访问时,varnish会执行该子程序,该程序通过对req的一些参数进行处理,决定接下来的动作 这里只能访问到req数据结构
一般以以下3种动作结束
- pass:执行pass动作,把请求交给vcl_pass模块处理。
- pipe:执行pipe动作,把请求交给vcl_pipe模块处理。
- error code [reason]:表示把错误标识返回给客户端,并放弃处理该请求。错误标识包括200、405等。”reason”是对错误的提示信息。
- vcl_fetch 当varnish成功从后端返回数据时,执行该子程序 这里既能访问req 也能访问beresp
- vcl_pipe 执行pipe动作时调用,用于将请求直接传递至后端主机,在请求和返回的内容没有改变的情况下,
也就是在当前连接未关闭时,服务器将不变的内容返回给客户端,直到该连接被关闭- lookup 一个请求在vcl_recv中被lookup后,Varnish将在缓存中提取数据。
如果缓存中有相应的数据,就把控制权交给vcl_hit子程序;
如果缓存中没有相应的数据,请求将被设置为pass并将其交给vcl_miss子程序
varnish 将在不同阶段执行它的子程序代码,因为它的代码是一行一行执行的,不存在优先级问题。随时可以调用这个子程序中的功能并且当他执行完成后就退出。
3.动作(action):
- pass: 当一个请求被 pass 后,这个请求将通过 varnish 转发到后端服务器,但是它不会被缓存。pass 可以放在 vcl_recv 和 vcl_fetch 中。
- lookup: 当一个请求在 vcl_recv 中被 lookup 后,varnish 将从缓存中提取数据,如果缓存中没有数据,将被设置为 pass,不能在 vcl_fetch 中设置 lookup。
- pipe: pipe 和 pass 相似,都要访问后端服务器,不过当进入 pipe 模式后,在此连接未关闭前,后续的所有请求都发到后端服务器,而pass只对当前的传输有效,后续的请求会根据策略决定是否发送到客户端
- deliver: 请求的目标被缓存,然后发送给客户端。
- esi: ESI-process the fetched document (Edge Side Includes ,使用varnish+esi
可以对用户个性化相关的进行缓存,而我们通常的做法是使用ajax+memcache对用户个性化的内容进行缓存 官方文档)
HTTP协议定义了四个可以用来控制浏览器缓存的HTTP头,它们是:
- Last-Modified
- Expires
- Pragma: no-cache
- Cache-Control
在HTTP/1.0协议中:
- Last-Modified是控制缓存的一个非常重要的HTTP头。
如果需要控制浏览器的缓存,服务器首先必须发送一个 以UTC时间为值的Last-Modifeid头,
当第二次访问这个页面时,浏览器会发送一个If-Modified-Since头给服务器,让服务器判断是否有必要更新内容,
这个If-Modified-Since头的值就是上次访问页面时,浏览器发送的Last-Modifeid头的值。- Expires是HTTP/1.0中另外一个很重要的HTTP头,它表示缓存的存在时间,
告诉客户端浏览器在这个时间之前不对服务器发送请求,而直接使用浏览器的缓存。
在HTTP/1.0中,可以使用Pragma: no-cache头来告诉浏览器不要缓存内容,
它相当于HTTP/1.1中的Cache-Control:no-cache
HTTP/1.0协议的这种实现方式的缺点是,服务器和客户端的时间有可能是不同步的,
这样会造成缓存的实现达不到预期效果。HTTP/1.1协议用Cache-Control头解决了这个问题
在http 1.1中
Cache-Control响应头的语法为:Cache-Control = “Cache-Control” “:”{缓存响应指令}
缓存响应指令为一下几个中的任意一个:
public 指示响应数据可以被任何客户端缓存
private 指示响应数据可以被非共享缓存所缓存。这表明响应的数据可以被发送请求的浏览器缓存,而不能被中介所缓存
no-cache 指示响应数据不能被任何接受响应的客户端所缓存
no-store 指示所传送的响应数据除了不能被缓存,也不能存入磁盘。一般用于敏感数据,以免数据被复制。
must-revalidate 指示所有的缓存都必须重新验证,
在这个过程中,浏览器会发送一个If-Modified-Since头。
如果服务器程序验证得出当前的响应数据为最新的数 据,那么服务器应当返回一个304
proxy-revalidate 与must-revalidate相似,不同的是用来指示共享缓存
max-age=时间 数据经过max-age设置的秒数后就会失效,
相当于HTTP/1.0中的Expires头。
如果在一次响应中同时设置了max-age和Expires,那么max-age将具有较高的优先级
s-maxage=时间 与max-age相似,不同的是用来指示共享缓存。
注:通过POST方法发送的请求不能以如上所述的方式缓存
可以在使用浏览器发送如下信息头
当我们在浏览器中输入url网址时,如果有过该网址的访问记录,浏览器会判断 max-age,如果没有会发送If-Modified-Since头信息,服务器去处理是否有更新
有些内容有max-age,浏览器不会发送请求,使用浏览器缓存,Chrome返回的状态码是200 OK (from cache),firefox没有请求。当我们按下F5时,浏览器会发送If-Modified-Since,max-age=0 头信息 这样服务器就会返回其最新内容(如果有varnish缓存的话,只返回varnish缓存)
当我们按下Control + F5,强刷,浏览器会发送 Pragma: no-cache,Cache-Control: no-cache信息头 如果在vcl中对这类信息头做处理,就可以刷新varnish缓存
这在生产环境中很适用,但是在线上环境中所有人都可以通过强制刷新varnish.所以需要使用白名单允许一些ip可以清除缓存,通常Cache-Control:nocache 是被varnish忽略的,根据自身需要处理
当然也可以写代码发送请求,或是使用浏览器插件比如livehttpheader或者使用curl命令
1.这两个规则定义了允许哪些主机通过HTTP来执行PURGE进行缓存删除。如果不是指定的IP,就会出现HTTP 405错误,提示Not allowed错误字样
2.varnishadm ban.url [regexp] 不需要加域名
如果backend返回的头信息中包含setcookie,varnish不会对此缓存,一般会在vcl中unset掉backend header产生的cookie 更多请看这里
php中有两种set cookie方法:
一种是header(string,replace,http_response_code),
一种是setCookie(name,value,expire,path,domain,secure)
setCookie设置的cookie,Varnish会缓存吗?(其实是一样的)
如果varnish看到授权头信息(Authorization)时,它会pass该请求。可以unset这个头信息
当几个客户端正访问相同页面时,varnish会发送一个请求到后端,然后让那个其他几个请求挂起等待返回结果,
返回结果后,复制请求的结果发送给客户端并且让其他请求等待,
但是如果,每秒上千的请求同时呢?(这个队列很庞大),这时候我们期待的应该是,返回过期的cache给用户
为了使用过期的cache给用户提供服务,我们需要增加他们的TTL,保存所有cache中的内容在TTL过期以后30分钟不删除,
使用以下VCL:
|
|
Varnish还不会使用过期的目标给用户提供服务,所以我们需要配置以下代码,在cache过期后的15秒内,使用旧的内容提供服务:
|
|
如果后端出了问题,是不是不应该返回问题的页面,而应该返回就版本的正常页面,可以开启健康检查,如果后端出问题了,可以长时间提供旧版本的内容
|
|
说一下我遇到的一些坑吧,毕竟不是运维人员,很多监控还不是很懂,所以只是说一些代码层面上的。
一般来说varnish是要将后端返回的header头的cookie信息重置掉,这样可以高效地利用缓存。
这种方式带来的后果就是:
由于Varnish 是一个HTTP缓存服务器,也就是说它是直接和浏览器打交道的,更通俗点,Varnish缓存的内容是直接被浏览器解析的!
所以 不要在后端服务器中返回带有状态的代码!Varnish对于所有用户访问的同一链接,返回的都是同一个内容。
如果你在后端返回的信息存在cookie,并且在Varnish中将cookie干掉了,就会导致,一个用户的行为被Varnish缓存住了,其他用户也被动地进行了这个行为。
通常解决这种方式的做法是使用ajax。
varnish 配置文件是允许重复配置同一个动作的,但是只是先加载的有效= =#。所以注意这个坑(已测试)
由于我们的业务需要,测试环境是穿透varnish的,也就是白名单,所以很多时候,内网会泄漏掉varnish的很多东西
这时候需要使用一些外网IP,挂代理取检测网站内容了(不过要小心,外网代理的策略有可能是把整个网页缓存了)
以前分享的文章, 放到博客里吧