0%

我们走得太快,灵魂都跟不上了 ...

代码

之所以把代码放到第一位,是因为这一点最容易引起技术人员的忽视。很多技术人员拿到一个性能优化的需求以后,言必称缓存、异步、JVM等。实际上,第一步就应该是分析相关的代码,找出相应的瓶颈,再来考虑具体的优化策略。有一些性能问题,完全是由于代码写的不合理,通过直接修改一下代码就能解决问题的,比如for循环次数过多、作了很多无谓的条件判断、相同逻辑重复多次等。

数据库

数据库的调优,总的来说分为以下三部分:

  • SQL调优
    这是最常用、每一个技术人员都应该掌握基本的SQL调优手段(包括方法、工具、辅助系统等)。这里以MySQL为例,最常见的方式是,由自带的慢查询日志或者开源的慢查询系统定位到具体的出问题的SQL,然后使用explain、profile等工具来逐步调优,最后经过测试达到效果后上线。这方面的细节,可以参考MySQL索引原理及慢查询优化。

  • 架构层面的调优
    这一类调优包括读写分离、多从库负载均衡、水平和垂直分库分表等方面,一般需要的改动较大,但是频率没有SQL调优高,而且一般需要DBA来配合参与。那么什么时候需要做这些事情?我们可以通过内部监控报警系统(比如Zabbix),定期跟踪一些指标数据是否达到瓶颈,一旦达到瓶颈或者警戒值,就需要考虑这些事情。通常,DBA也会定期监控这些指标值。

  • 连接池调优
    我们的应用为了实现数据库连接的高效获取、对数据库连接的限流等目的,通常会采用连接池类的方案,即每一个应用节点都管理了一个到各个数据库的连接池。随着业务访问量或者数据量的增长,原有的连接池参数可能不能很好地满足需求,这个时候就需要结合当前使用连接池的原理、具体的连接池监控数据和当前的业务量作一个综合的判断,通过反复的几次调试得到最终的调优参数。

缓存

  • 本地缓存(HashMap/ConcurrentHashMap、Ehcache、Guava Cache等),缓存服务(Redis/Tair/Memcache等)。

使用场景
什么情况适合用缓存?考虑以下两种场景:

短时间内相同数据重复查询多次且数据更新不频繁,这个时候可以选择先从缓存查询,查询不到再从数据库加载并回设到缓存的方式。此种场景较适合用单机缓存。
高并发查询热点数据,后端数据库不堪重负,可以用缓存来扛。
选型考虑
如果数据量小,并且不会频繁地增长又清空(这会导致频繁地垃圾回收),那么可以选择本地缓存。具体的话,如果需要一些策略的支持(比如缓存满的逐出策略),可以考虑Ehcache;如不需要,可以考虑HashMap;如需要考虑多线程并发的场景,可以考虑ConcurentHashMap。
其他情况,可以考虑缓存服务。目前从资源的投入度、可运维性、是否能动态扩容以及配套设施来考虑,我们优先考虑Tair。除非目前Tair还不能支持的场合(比如分布式锁、Hash类型的value),我们考虑用Redis。
设计关键点
什么时候更新缓存?如何保障更新的可靠性和实时性?
更新缓存的策略,需要具体问题具体分析。这里以门店POI的缓存数据为例,来说明一下缓存服务型的缓存更新策略是怎样的?目前约10万个POI数据采用了Tair作为缓存服务,具体更新的策略有两个:

接收门店变更的消息,准实时更新。
给每一个POI缓存数据设置5分钟的过期时间,过期后从DB加载再回设到DB。这个策略是对第一个策略的有力补充,解决了手动变更DB不发消息、接消息更新程序临时出错等问题导致的第一个策略失效的问题。通过这种双保险机制,有效地保证了POI缓存数据的可靠性和实时性。
缓存是否会满,缓存满了怎么办?
对于一个缓存服务,理论上来说,随着缓存数据的日益增多,在容量有限的情况下,缓存肯定有一天会满的。如何应对?
① 给缓存服务,选择合适的缓存逐出算法,比如最常见的LRU。
② 针对当前设置的容量,设置适当的警戒值,比如10G的缓存,当缓存数据达到8G的时候,就开始发出报警,提前排查问题或者扩容。
③ 给一些没有必要长期保存的key,尽量设置过期时间。

缓存是否允许丢失?丢失了怎么办?
根据业务场景判断,是否允许丢失。如果不允许,就需要带持久化功能的缓存服务来支持,比如Redis或者Tair。更细节的话,可以根据业务对丢失时间的容忍度,还可以选择更具体的持久化策略,比如Redis的RDB或者AOF。

缓存被“击穿”问题
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑另外一个问题:缓存被“击穿”的问题。

概念:缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
如何解决:业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。类似下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
} else {
return value;
}
}

异步

使用场景
针对某些客户端的请求,在服务端可能需要针对这些请求做一些附属的事情,这些事情其实用户并不关心或者用户不需要立即拿到这些事情的处理结果,这种情况就比较适合用异步的方式处理这些事情。

作用
缩短接口响应时间,使用户的请求快速返回,用户体验更好。
避免线程长时间处于运行状态,这样会引起服务线程池的可用线程长时间不够用,进而引起线程池任务队列长度增大,从而阻塞更多请求任务,使得更多请求得不到技术处理。
线程长时间处于运行状态,可能还会引起系统Load、CPU使用率、机器整体性能下降等一系列问题,甚至引发雪崩。异步的思路可以在不增加机器数和CPU数的情况下,有效解决这个问题。
常见做法
一种做法,是额外开辟线程,这里可以采用额外开辟一个线程或者使用线程池的做法,在IO线程(处理请求响应)之外的线程来处理相应的任务,在IO线程中让response先返回。

如果异步线程处理的任务设计的数据量非常巨大,那么可以引入阻塞队列BlockingQueue作进一步的优化。具体做法是让一批异步线程不断地往阻塞队列里扔数据,然后额外起一个处理线程,循环批量从队列里拿预设大小的一批数据,来进行批处理(比如发一个批量的远程服务请求),这样进一步提高了性能。

另一种做法,是使用消息队列(MQ)中间件服务,MQ天生就是异步的。一些额外的任务,可能不需要我这个系统来处理,但是需要其他系统来处理。这个时候可以先把它封装成一个消息,扔到消息队列里面,通过消息中间件的可靠性保证把消息投递到关心它的系统,然后让这个系统来做相应的处理。

比如C端在完成一个提单动作以后,可能需要其它端做一系列的事情,但是这些事情的结果不会立刻对C端用户产生影响,那么就可以先把C端下单的请求响应先返回给用户,返回之前往MQ中发一个消息即可。而且这些事情理应不是C端的负责范围,所以这个时候用MQ的方式,来解决这个问题最合适。

NoSQL

和缓存的区别
先说明一下,这里介绍的和缓存那一节不一样,虽然可能会使用一样的数据存储方案(比如Redis或者Tair),但是使用的方式不一样,这一节介绍的是把它作为DB来用。如果当作DB来用,需要有效保证数据存储方案的可用性、可靠性。

使用场景
需要结合具体的业务场景,看这块业务涉及的数据是否适合用NoSQL来存储,对数据的操作方式是否适合用NoSQL的方式来操作,或者是否需要用到NoSQL的一些额外特性(比如原子加减等)。

如果业务数据不需要和其他数据作关联,不需要事务或者外键之类的支持,而且有可能写入会异常频繁,这个时候就比较适合用NoSQL(比如HBase)。

比如,美团点评内部有一个对exception做的监控系统,如果在应用系统发生严重故障的时候,可能会短时间产生大量exception数据,这个时候如果选用MySQL,会造成MySQL的瞬间写压力飙升,容易导致MySQL服务器的性能急剧恶化以及主从同步延迟之类的问题,这种场景就比较适合用Hbase类似的NoSQL来存储。

JVM调优

什么时候调?
通过监控系统(如没有现成的系统,自己做一个简单的上报监控的系统也很容易)上对一些机器关键指标(gc time、gc count、各个分代的内存大小变化、机器的Load值与CPU使用率、JVM的线程数等)的监控报警,也可以看gc log和jstat等命令的输出,再结合线上JVM进程服务的一些关键接口的性能数据和请求体验,基本上就能定位出当前的JVM是否有问题,以及是否需要调优。

怎么调?
如果发现高峰期CPU使用率与Load值偏大,这个时候可以观察一些JVM的thread count以及gc count(可能主要是young gc count),如果这两个值都比以往偏大(也可以和一个历史经验值作对比),基本上可以定位是young gc频率过高导致,这个时候可以通过适当增大young区大小或者占比的方式来解决。
如果发现关键接口响应时间很慢,可以结合gc time以及gc log中的stop the world的时间,看一下整个应用的stop the world的时间是不是比较多。如果是,可能需要减少总的gc time,具体可以从减小gc的次数和减小单次gc的时间这两个维度来考虑,一般来说,这两个因素是一对互斥因素,我们需要根据实际的监控数据来调整相应的参数(比如新生代与老生代比值、eden与survivor比值、MTT值、触发cms回收的old区比率阈值等)来达到一个最优值。
如果发生full gc或者old cms gc非常频繁,通常这种情况会诱发STW的时间相应加长,从而也会导致接口响应时间变慢。这种情况,大概率是出现了“内存泄露”,Java里的内存泄露指的是一些应该释放的对象没有被释放掉(还有引用拉着它)。那么这些对象是如何产生的呢?为啥不会释放呢?对应的代码是不是出问题了?问题的关键是搞明白这个,找到相应的代码,然后对症下药。所以问题的关键是转化成寻找这些对象。怎么找?综合使用jmap和MAT,基本就能定位到具体的代码。
多线程与分布式
使用场景
离线任务、异步任务、大数据任务、耗时较长任务的运行**,适当地利用,可达到加速的效果。

注意:线上对响应时间要求较高的场合,尽量少用多线程,尤其是服务线程需要等待任务线程的场合(很多重大事故就是和这个息息相关),如果一定要用,可以对服务线程设置一个最大等待时间。

常见做法
如果单机的处理能力可以满足实际业务的需求,那么尽可能地使用单机多线程的处理方式,减少复杂性;反之,则需要使用多机多线程的方式。

对于单机多线程,可以引入线程池的机制,作用有二:

提高性能,节省线程创建和销毁的开销
限流,给线程池一个固定的容量,达到这个容量值后再有任务进来,就进入队列进行排队,保障机器极限压力下的稳定处理能力在使用JDK自带的线程池时,一定要仔细理解构造方法的各个参数的含义,如core pool size、max pool size、keepAliveTime、worker queue等,在理解的基础上通过不断地测试调整这些参数值达到最优效果。
如果单机的处理能力不能满足需求,这个时候需要使用多机多线程的方式。这个时候就需要一些分布式系统的知识了。首先就必须引入一个单独的节点,作为调度器,其他的机器节点都作为执行器节点。调度器来负责拆分任务,和分发任务到合适的执行器节点;执行器节点按照多线程的方式(也可能是单线程)来执行任务。这个时候,我们整个任务系统就由单击演变成一个集群的系统,而且不同的机器节点有不同的角色,各司其职,各个节点之间还有交互。这个时候除了有多线程、线程池等机制,像RPC、心跳等网络通信调用的机制也不可少。后续我会出一个简单的分布式调度运行的框架。

度量系统(监控、报警、服务依赖管理)
严格来说,度量系统不属于性能优化的范畴,但是这方面和性能优化息息相关,可以说为性能优化提供一个强有力的数据参考和支撑。没有度量系统,基本上就没有办法定位到系统的问题,也没有办法有效衡量优化后的效果。很多人不重视这方面,但我认为它是系统稳定性和性能保障的基石。

关键流程
如果要设计这套系统,总体来说有哪些关键流程需要设计呢?
① 确定指标
② 采集数据
③ 计算数据,存储结果
④ 展现和分析

需要监控和报警哪些指标数据?需要关注哪些?
按照需求出发,主要需要二方面的指标:

接口性能相关,包括单个接口和全部的QPS、响应时间、调用量(统计时间维度越细越好;最好是,既能以节点为维度,也可以以服务集群为维度,来查看相关数据)。其中还涉及到服务依赖关系的管理,这个时候需要用到服务依赖管理系统
单个机器节点相关,包括CPU使用率、Load值、内存占用率、网卡流量等。如果节点是一些特殊类型的服务(比如MySQL、Redis、Tair),还可以监控这些服务特有的一些关键指标。
数据采集方式
通常采用异步上报的方式,具体做法有两种:第一种,发到本地的Flume端口,由Flume进程收集到远程的Hadoop集群或者Storm集群来进行运算;第二种,直接在本地运算好以后,使用异步和本地队列的方式,发送到监控服务器。

数据计算
可以采用离线运算(MapReduce/Hive)或者实时/准实时运算(Storm/Spark)的方式,运算后的结果存入MySQL或者HBase;某些情况,也可以不计算,直接采集发往监控服务器。

展现和分析
提供统一的展现分析平台,需要带报表(列表/图表)监控和报警的功能。

你为什么从事喜剧行业?人们总是嘲笑我,所以我想,去他妈的,干脆收他们点儿钱!
一个简单的下载图片的爬虫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
from bs4 import BeautifulSoup
headers = {'User-Agent':"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"}
all_url = 'http://www.mzitu.com/all'
start_html = requests.get(all_url, headers=headers)
Soup = BeautifulSoup(start_html.text, 'lxml')

all_a = Soup.find('div', class_='all').find_all('a')
for a in all_a:
title = a.get_text()
href = a.get('href')
html = requests.get(href, headers=headers)
html_Soup = BeautifulSoup(html.text, 'lxml')
max_span = html_Soup.findAll('span')[10].get_text()
for page in range(1, int(max_span) + 1):
page_url = href + '/' + str(page)
img_html = requests.get(page_url, headers=headers)
img_Soup = BeautifulSoup(img_html.text, 'lxml')
img_url = img_Soup.find('div', class_='main-image').find('img')['src']
name = img_url[-9:-4]
img = requests.get(img_url, headers=headers)
f = open(name + '.jpg', 'ab')
f.write(img.content)
f.close()

more less

JWT stand for Json Web Token. JWT will helps to create authentication and connect front-end and back-end function. JWT through we can create login and register API. So first we have to install “tymon/jwt-auth” package in laravel 5.2.

JWT Installation

First fire following command on your terminal.

Installation Package

composer require tymon/jwt-auth
After install this package, Now open config/app.php file and add service provider and aliase.
config/app.php

1
2
3
4
5
6
7
8
'providers' => [
....
'Tymon\JWTAuth\Providers\JWTAuthServiceProvider',
],
'aliases' => [
....
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth'
],

Now we need to publish JWT configration file, that way we can change configration like token expire time etc. so, let’s fire bellow command.

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
At last on installation, we have to generate jwt key, fire bellow command on your terminal.

php artisan jwt:generate

Create API Route

Now we require to create create route for API, in bellow route you can see i use two middleware “api” and “cors”. cors is not mandatory, but when you call api and found like:

“Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://test.com/api/register. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).”

Then you have two must be create cors middleware by following link : Ajax - Cross-Origin Request Blocked in Larave 5?.

app/Http/routes.php

1
2
3
4
5
6
7
Route::group(['middleware' => ['api','cors'],'prefix' => 'api'], function () {
Route::post('register', 'APIController@register');
Route::post('login', 'APIController@login');
Route::group(['middleware' => 'jwt-auth'], function () {
Route::post('get_user_details', 'APIController@get_user_details');
});
});

In above i use also added jwt-auth for token is valid or not. so we must need to create jwt-auth middleware and first fire following command.

php artisan make:middleware authJWT
On now you can check on Middleware(app/Http/Middleware) directory, you can find authJWT.php file and put bellow code on that file.

app/Http/Middleware/authJWT.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Exception;
class authJWT
{
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::toUser($request->input('token'));
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
return response()->json(['error'=>'Token is Invalid']);
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
return response()->json(['error'=>'Token is Expired']);
}else{
return response()->json(['error'=>'Something is wrong']);
}
}
return $next($request);
}
}

Ok, now register new created middleware on Kernel.php(app/Http/Kernel.php) file and append following line.

app/Http/Kernel.php

1
2
3
4
5
6
7
8
9
10
11
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
...
...
protected $routeMiddleware = [
...
'jwt-auth' => \App\Http\Middleware\authJWT::class,
];
}

Create Controller

Here we have to create controller that will manage all route request. so first create “APIController” and put bellow code.

app/Http/Controllers/APIController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Hash;
use JWTAuth;
class APIController extends Controller
{

public function register(Request $request)
{
$input = $request->all();
$input['password'] = Hash::make($input['password']);
User::create($input);
return response()->json(['result'=>true]);
}

public function login(Request $request)
{
$input = $request->all();
if (!$token = JWTAuth::attempt($input)) {
return response()->json(['result' => 'wrong email or password.']);
}
return response()->json(['result' => $token]);
}

public function get_user_details(Request $request)
{
$input = $request->all();
$user = JWTAuth::toUser($input['token']);
return response()->json(['result' => $user]);
}

}

Test API from Front-end

Don’t forgot to change http://learnl52.hd = your site url.

Now we are ready to check our API from front-end side. i did make jquery ajax request example but you can also call API in angularjs. first fire register api that will return just success if user created success fully.

Call Register API

1
2
3
4
5
6
7
8
9
$.ajax({
url: "http://learnl52.hd/api/register",
dataType: "json",
type: "POST",
data: {"name":"HD","email":"test@gmail.com","password":"123456"},
success: function (data) {
alert("user created successfully")
}
});

Now you can fire login API, this API will return token in result if, email and password will not wrong. that token you have to pass in all other route that way you can identify this user is current login user so fire following way:

Call Login API

1
2
3
4
5
6
7
8
9
$.ajax({
url: "http://learnl52.hd/api/login",
dataType: "json",
type: "POST",
data: {"email":"test@gmail.com","password":"123456"},
success: function (data) {
alert(data.result)
}
});

At last we can fire get_user_details API, This API will take only one paramete token, token is that you find on login API. this API will return all users details if your token is valid, if token is not valid then return error message. fire ajax like this way:

Call Get User Details API

1
2
3
4
5
6
7
8
9
$.ajax({
url: "http://learnl52.hd/api/get_user_details",
dataType: "json",
type: "POST",
data: {"token":your toke here},
success: function (data) {
console.log(data)
}
});

add

Require the barryvdh/laravel-cors package in your composer.json and update your dependencies.

composer require barryvdh/laravel-cors
Add the Cors\ServiceProvider to yourconfig/app.phpproviders array:

Barryvdh\Cors\ServiceProvider::class,

http://itsolutionstuff.com/post/laravel-52-api-using-jwt-authentication-tutorial-from-scratch-exampleexample.html

 你必须很努力,才能看上去毫不费力
最近学习了微信小程序,才接触到了这一布局方式,大概试了一下,记录之

1. 布局html

1
2
3
4
5
6
7
8
9
10
11
<div class="container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item4">4</div>
<div class="item item5">5</div>
<div class="item item6">6</div>
<div class="item item7">7</div>
<div class="item item8">8</div>
<div class="item item9">9</div>
</div>

2.order 属性

order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

1
2
3
4
5
6
.item1{
order: 2;
}
.item2{
order: 1;
}

3. flex-grow属性

即如果子元素在父容器的横向上还没有撑满,左或者右都存在空间
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

1
2
3
4
.item1{
order: 2;
flex-grow: 2;
}

如果三个元素都设置这样的属性,则由这三个元素一起撑满剩余的部分

1
2
3
4
5
6
7
8
9
10
.item1{
flex-grow: 1;
}
.item2{
order: 1;
flex-grow: 1;
}
.item3{
flex-grow: 1;
}

4. flex-shrink属性

如果所有的子元素的宽度已经超过了父容器的宽度,而且设置了不能换行,这时候如果某个元素设置了这样的属性,
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

1
2
3
4
5
6
.item2{
flex-shrink: 2;
}
.item3{
flex-shrink: 2;
}

没有学到死,就往死里学。
最近学习了微信小程序,才接触到了这一布局方式,大概试了一下,记录:

使用方式:

1. html

1
2
3
4
5
6
7
8
9
10
11
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>

2. 定义基本css样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
body{
background: #c0ffb6;
}
.container{
width: 100%;
height: 100%;
}
.item{
width: 150px;
height: 200px;
background-color: #4cae4c;
margin: 2px;
}

3. 设置父容器为flex布局:

  • div 块儿级元素
    1
    2
    3
    4
    5
    .container{
    width: 100%;
    height: 100%;
    display: flex;
    }
  • 或者 行内元素
    1
    2
    3
    4
    5
    .container{
    width: 100%;
    height: 100%;
    display: inline-flex;
    }
    设为Flex布局以后,子元素的float、clear和vertical-align属性将失效

4. flex-direction 属性:

决定容器里面元素的排列方式
横向排列
横向反转排列
纵向排列
纵向反转排列

1
2
3
4
5
6
.container{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}

5. flex-wrap 属性

横向排列的子元素是否换行
在子元素的大小超出了父容器的大小以后是换行显示
不换行: nowrap 设置了不换行以后,子元素的大小将。。

1
2
3
4
5
6
7
8
9
10
11
12
13
.container{
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.item{
width: 650px;
height: 200px;
background-color: #4cae4c;
margin: 2px;
}

6. justify-content 属性

子元素的对齐方式
左对齐
右对齐
居中显示
相等间隔

1
2
3
4
5
6
7
8
.container{
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-around;
}

7. align-items:

决定子元素在垂直位置上的对齐方式

1
2
3
4
5
6
7
8
9
.container{
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-around;
align-items: flex-end;
}

8. align-self 属性

决定当前元素的对齐方式,用于覆盖父容器的align-items 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.container{
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-items: center;
}
.item{
width: 450px;
height: 200px;
background-color: #4cae4c;
margin: 2px;
}
.item1{
height: 120px;
align-self: flex-end;
}
.item2{
height: 100px;
}
.item3{
height: 150px;
}

是谁来自山川湖海,却囿于昼夜、厨房与爱。

laravel的404

  • 配置404页面
    resources\views\errors\404.blade.php,如果配置了该页面,那么访问任何一个不存在的路由,就会显示到该页面,同时返回的状态码也会是404,同样,也可以通过代码abort(404);来展示出404页面,如果在你的视图文件中没有配置404.blade.php 页面,那么访问一个不存在的路由就会引起程序报错。

nginx的404

  • 配置404页面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    server {
    listen 80;
    server_name www.la51.dev la51.dev;
    root "D:/phpStudy/WWW/la51/public";
    fastcgi_intercept_errors on;
    error_page 404 /404.html ;
    location / {
    index index.html index.htm index.php;
    #autoindex on;
    try_files $uri $uri/ /index.php?$query_string;
    }

404.html在网站的根目录下,关键的两句,`fastcgi_intercept_errors on;error_page 404 /404.html ;这样访问一个不存在的页面,即使没有在laravel中做相关的配置,也能在路由不存在时跳转到404页面;

不管我本人多么平庸,我总觉得对你的爱很美。

manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name": "消息传递",
"version": "1.0",
"manifest_version": 2,
"description": "各个页面JS之间事件的触发和消息的传递",
"browser_action": {
"default_icon": "images/icon38.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["js/background.js","js/jquery.min.js"],
"persistent": false
},
"permissions": ["tabs", "<all_urls>"]
}
1
2
3
4
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/content.js"></script>

<button id="newtab" >new tab</button>

content.js

1
2
3
4
5
$(document).ready(function () {
$("#newtab").click(function () {
chrome.runtime.sendMessage({action:"new_tab"});
});
});

background.js

1
2
3
4
5
6
7
8
9
10
11
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {

if(request.action != 'new_tab'){
var newURL = "http://stackoverflow.com/";
chrome.tabs.create({ url: newURL });
}else {
var newURL = "http://www.baidu.com/";
chrome.tabs.create({ url: newURL });
}

});

当斧头来到树林里的时候,好多树都说,至少它的把手是我们自己人。
ES中的聚合被分为两大类:Metric度量和bucket桶(原谅我英语差,找不到合适的词语.....就用单词来说吧!)。说的通俗点,metric很像SQL中的avg、max、min等方法,而bucket就有点类似group by了。

Sum 求和

1
2
3
"aggs" : {
"intraday_return" : { "sum" : { "field" : "change" } }
}

Min 求最小值

1
2
3
4
5
{
"aggs" : {
"min_price" : { "min" : { "field" : "price" } }
}
}

cardinality 求唯一值,即不重复的字段有多少

1
2
3
4
5
6
7
8
9
{
"aggs" : {
"author_count" : {
"cardinality" : {
"field" : "author"
}
}
}
}

一次性返回所有指标,count,min,max,avg,sum

1
2
3
4
5
{
"aggs" : {
"grades_stats" : { "stats" : { "field" : "total_price" } }
}
}

先过滤后聚合

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"paid_order" : {
"filter" : { "term": { "status_code": "paid" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "total_price" } }
}
}
}
}

去除结果中的产品

1
2
3
4
5
6
7
8
9
10
11
{
"size":0,
"aggs" : {
"paid_order" : {
"filter" : { "term": { "status_code": "paid" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "total_price" } }
}
}
}
}

使用区间统计

1
2
3
4
5
6
7
8
9
10
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "total_price",
"interval" : 1000
}
}
}
}

日期过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"aggs": {
"range": {
"date_range": {
"field": "created_at",
"format": "MM-yyy",
"ranges": [
{ "to": "now-10M/M" },
{ "from": "now-10M/M" }
]
}
}
}
}

受任于败军之际,奉命于危难之间,尔来二十有一年矣。

manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name" : "Imageinfo",
"version" : "1.0.1",
"description" : "Get image info for images, including EXIF data",
"background" : { "scripts": ["js/background.js"] },
"permissions" : [
"contextMenus",
"tabs",
"http://*/*",
"https://*/*"
],
"minimum_chrome_version" : "6.0.0.0",
"icons" : {
"16" : "images/icon16.png",
"48" : "images/icon48.png",
"128" : "images/icon128.png"
},
"manifest_version": 2
}

background.js

1
2
3
4
5
6
7
8
9
10
11
12
13
function getClickHandler() {
return function(info, tab) {
var url = 'html/index.html#' + info.srcUrl;
chrome.windows.create({ url: url, width: 520, height: 660 });
};
};

chrome.contextMenus.create({
"title" : "Get image info",
"type" : "normal",
"contexts" : ["image"],
"onclick" : getClickHandler()
});

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script type="text/javascript" src="../js/jquery.min.js"></script>
<script type="text/javascript" src="../js/content.js"></script>
<title>Document</title>
</head>
<body>
<h1>tihs is a new page</h1>
<div id="con">

</div>
</body>
</html>

content.js

1
2
3
4
5
$(function () {
url = window.location.href;
kk = url.split('#');
$("#con").html("<img src='"+kk[1]+"' />");
});

她是你姑姑,又是你师父,怎么能做你妻子!

简述

他们的区别是,触发的时机不一样,先触发DOMContentLoaded事件,后触发load事件。

DOM文档加载的步骤为:

  • 解析HTML结构。
  • 加载外部脚本和样式表文件。
  • 解析并执行脚本代码。
  • DOM树构建完成。//DOMContentLoaded
  • 加载图片等外部文件。
  • 页面加载完毕。//load

在第4步,会触发DOMContentLoaded事件。在第6步,触发load事件。
用原生js可以这么写:

1
2
3
4
5
6
7
8
// 不兼容老的浏览器,兼容写法见[jQuery中ready与load事件](http://www.imooc.com/code/3253),或用jQuery
document.addEventListener("DOMContentLoaded", function() {
// ...代码...
}, false);

window.addEventListener("load", function() {
// ...代码...
}, false);

用jQuery这么写:

1
2
3
4
5
6
7
8
9
// DOMContentLoaded
$(document).ready(function() {
// ...代码...
});

//load
$(document).load(function() {
// ...代码...
});

http://www.jianshu.com/p/d851db5f2f30