2019年终结

2019还有3天就结束了,理一理今年的经历。对照去年的flag复盘下,惨不忍睹 啪啪打脸。唯一好的是,养成了记笔记在博客的习惯。

阅读

今年一共读完了16本书,有标识的是给我带来较大映象和收获的书籍。

###技术

语言
嗨翻C语言
深入理解C指针
GO 语言实战
GO WEB编程

协议
图解HTTP
图解TCP/IP

架构
大型网络技术架构

创业

创业36条军规
低风险创业
合伙人制度——有效激励而不失控制权是怎样实现的
增长黑客

理财

工作前5年,决定你一生的财富
小狗钱钱

管理

奈飞文化手册
非暴力沟通
见识

技能

  • C语言语法特性堆,栈,指针(能读懂,能写demo
  • Go的语法特性 beego框架(能开发
  • Mysql的底层原理
  • HTTP,TCP/IP协议(稍微知识扎实了一点

经济

  • 买房
  • 装修
  • 结婚的钱勉强搞定了

出去玩

  • 上海水族馆
  • 上海迪士尼
  • 枸杞岛看了海上的日出
  • 东方明珠看了城市的日落

《低风险创业》

樊登读书这个APP我比较喜欢,也经常在通勤的路上使用。后面看见APP上再推这本书,加上深知打工出路两茫茫所以买来细品下

因为经历过创业团队,并且团队已经壮大。前半部分感觉樊老师总结的真的是颇有细节和条理

  • 低风险创业的基本逻辑

    主要阐述了创业前的准备,家庭和事业间作者的一些见解。带着愉快且有使命感的心情创业,比打工者吃饭的心情创业成功率会高很多

  • 创业找到好的问题开始

    茅舍顿开的一点是樊老师对用户痛点的概括即创业的根本是解决一个社会型问题这一章作者分享了一些他寻找痛点的方式方法(从抱怨中寻找解决点,找到足够大的蛋糕要干就干最肥的市场,在客户最痛的点上寻找赚钱的方法

  • 秘密是最好的抗风险武器

    这章有点迷,既要留一手又要告诉你这手后你也学不会。但是自己的感悟是产品要么不做,要么做到数一数二。试错期做个又大又全的产品试错成本太高,尽量做个最小化产品尝试市场。

  • 反脆弱结构设计

    那些杀不死你的总会让你变得更加强大,未雨绸缪做产品或者创业别过于依赖某一点。要多元化的分配资源配置自己的创业杠杆,保留选择权

  • 赋能生物态创业团队

    这章感同身受,能创业成功的团队肯定是目标一致充满干劲的生态管理团队。机械管理团队比较刻板化,能工厂化的交付产出但是确实创新精神(不求有功但求无过,混口饭吃等工作氛围会在机械管理团队尤为突出。

  • 最优客户发展方法

    口碑传播,这章感觉就是樊老师对《上瘾》一书的总结了。怎么病毒化的传播自己的产品

《见识-吴军》

吴军博士是一个把爱好做到学术的人,在没看这本书之前在喜马拉雅上听过他的硅谷来信。看了这本书过后更多的感觉是对硅谷来信的一些总结,如书名一样感觉开阔了见识并且有的地方产生了共鸣,但书中有部分观点不能完全认同可能是境界还没有到那样的高度吧。很喜欢,还会二刷吧

《创业36条军规》

出于对公司的创业文化很感兴趣,并且想了解创业到底会经历哪些过程,需要注意什么,哪些是推动创业成功比较重要的点。阅读了这本书,感觉对自身有帮助的主要是如下内容

1.创业是高风险没有回头路的生活方式
2.创业者除专业技能外还需要有大量的准备和心里承受能力目标感
3.创业内容的讲究
4.团队管理的注意事项

两数之和解题笔记

每日一刷,用PHP和GO分别实现了2种我能想到的和官方给出的最优解。

题目

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解题

PHP

穷举

思路 : 2个循环遍历一一对应,简单暴力

1
2
3
4
5
6
7
8
9
10
11
12
function twoSum($nums, $target) {
foreach ($nums as $k => $v) {
foreach ($nums as $kk => $vv) {
if ($v + $vv == $target) {
if ($k != $kk) {
return [$k,$kk];
}
}
}
}
return false;
}

时间复杂度 o(n^2),平均执行时间 1533.6ms 平均内存消耗15.96MB

使用array_search查找

思路:PHP提供了超多的数组函数,比其他语言方便了很多。

  1. 循环取数
  2. 求差
  3. array_search查找是否存在
1
2
3
4
5
6
7
8
9
10
11
12
function twoSum ($nums, $target) {
foreach ($nums as $k => $v) {
$temp = $target - $v;
unset($nums[$k]);
$kk = array_search($temp, $nums);
if ($kk != false) {
return [$k,$kk];
}

}
return false;
}

时间复杂度 o(n),平均执行时间 116ms 平均内存消耗15.94MB

Hashmap

hash表其实就是键值对的形式,在php中可以用数组来实现

  1. 循环等量的 内容长度
  2. 生成一个 数值为键,下标为值的hashmap
  3. 在循环中查找hashmap是否有差值对应
  4. 有就返回,没有把当前值写入hashmap的key下标写入value

这样做虽然时间复杂度也是o(n),但是节省了array_search的函数开销,hashmap中查找的时间复杂度是o(1)

1
2
3
4
5
6
7
8
9
10
11
12
function twoSum ($nums, $target) {

$diff = array();
for ($i = 0; $i < count($nums); $i++) {
$key = $diff[$target - $nums[$i]];
if (!isset($key)) {
$diff[$nums[$i]] = $i;
} else {
return [$key,$i];
}
}
}

时间复杂度 o(n),平均执行时间 18.4ms 平均内存消耗16.06MB

go

go 没有原生函数支持是否存在切片或者数组中,所以这边只试了2种解法

穷举

1
2
3
4
5
6
7
8
9
10
11
12
13
func twoSum(nums []int, target int) []int {
for k,v := range nums{
temp := target - v
for kk,vv := range nums{
if temp == vv {
if (k != kk){
return []int{k,kk}
}
}
}
}
return []int{}
}

时间复杂度 o(n^2),平均执行时间 36ms 平均内存消耗2.9MB

Hashmap

1
2
3
4
5
6
7
8
9
10
11
12
function twoSum ($nums, $target) {
$diff = array();
for ($i = 0; $i < count($nums); $i++) {
$key = $diff[$target - $nums[$i]];
if (!isset($key)) {
$diff[$nums[$i]] = $i;
} else {
return [$key,$i];
}
}
}

时间复杂度 o(n),平均执行时间 4ms 平均内存消耗3.8MB

ps: 那个0ms应该是抽了就不算进去了

性能对比

算法 语言 平均执行时间 平均内存
穷举 PHP 1533.6ms 15.96MB
穷举 GO 36ms 2.9MB
Hashmap PHP 18.4ms 16.06MB
Hashmap GO 4ms 3.8MB
array_search PHP 116ms 15.94MB

回文数算法的实现 && PHP GO C 的性能对比

一直以来我都有一个疑问,大家说的编译型语言比脚本型语言效率高运行快是否属实。在书本知识上来理解,一种更接近底层,一种需要解释器进行转换感觉来说的确如此,周末闲不下来所以特地试试

目的

  1. 了解回文数,并写出回文数的几种解法
  2. 对比(PHP / GO / C)对单个回文数算法的执行数据

什么是回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

1
2
输入: 121
输出: true

示例 2:

1
2
3
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

1
2
3
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
  • 所有个位数都属于回文数

解题

因为主要从事PHP开发这边也想了3中用PHP进行解题的思路

PHP

一. 翻转字符串

1
2
3
function isPalindrome ($x) {
return strrev($x) == $x ? true : false;
}

翻转字符串

平均执行时间 30.4ms,平均内存消耗 14.88MB

二. 转换为字符串,截断字符串 对比前后

思路

  1. 统计字符串长度,除2得到中间那个数
  2. 除模2判断单双数
  3. 双数 -> 前部分|取(0,字符长度/2) 后部分|取(字符长度/2,字符长度/2)
  4. 单数 前后多取一位
  5. 对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function isPalindrome($x) {
$len = strlen($x);
$centre = floor($len / 2);
if (($len % 2) == 0) {
$before = substr($x, 0, $centre);
$after = substr($x, $centre, $centre);
if ($before == strrev($after)) {
return true;
} else {
return false;
}
} else {
$position = $centre + 1;
$before = substr($x, 0, $position - 1);
$after = substr($x, $position, $len - 1);

if ($before == strrev($after)) {
return true;
} else {
return false;
}
}
}

翻转字符串

平均执行时间 32.8ms,平均内存消耗 14.86MB

取模对比法

  1. 负数都是非回文数
  2. 取10模因此需要过滤掉他本身
  3. 个位数都是回文数需要过滤掉0
  4. x取模10得到个位数
  5. rev乘10加上尾数实现’反转’效果
  6. x除10 rev乘10判断中位数
  7. rev 除10去除单数时的中数

注: PHP 是弱类型语言会隐式转换为浮点数,在乘除取中位数时会有干扰,说以需要强制取整

PHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function isPalindrome ($x) {

if ($x < 0 || ($x % 10 == 0 && $x != 0)) {
return false;
}

$rev = 0;
while (floor($x) > $rev) {
$rev = ($rev * 10) + ($x % 10);
$x = floor($x) / 10;
}


return ( (floor($rev / 10) == floor($x)) || ($rev == floor($x))) ? true : false;

}

平均执行时间 42.4ms,平均内存消耗 14.86MB

GO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func isPalindrome(x int) bool {

if (x < 0) || ((x%10) == 0 && x != 0) {
return false
}

var rev int
rev = 0

for x > rev {
rev = (rev*10) + (x%10)
x = x/10
}

if (rev/10 == x) || (rev == x) {
return true
} else {
return false
}
}

平均执行时间 16ms,平均内存消耗 5.2MB

C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool isPalindrome(int x){

if( x<0 || (x%10 == 0 && x !=0)){
return false;
}

int rev = 0;

while (x > rev) {
rev = rev*10 + x%10;
x = x/10;
}

if ( (rev/10) == x || rev == x){
return true;
} else {
return false;
}
}

平均执行时间 14.4ms,平均内存消耗 7.12MB

性能比较

PHP

名称 平均耗时 平均内存消耗
翻转字符串 30.4ms 14.88MB
截断字符串,对比前后 32.8ms 14.86MB`
取模对比法 42.4ms 14.86MB

语言对比

语言 平均耗时 平均内存消耗
PHP 30.4ms 14.88MB
GO 16ms 5.2MB
C 14.4ms 7.12MB

总结

至此给我带来最大收获的可能会有如下几点

  1. GO的性能消耗无限接近C
  2. 脚本型语言性能弱于编译型
  3. PHP内部封装函数优于我们自己用PHP语言实现的各种花里胡哨的算法(1.开发小组或多或少会选择执行性能最好的解法。 2.底层C直接执行函数,效率远远高过脚本解释成二进制再去执行。)

这里浅浅的翻了下PHP strrev 函数的C实现是通过指针向前解引实现的(C的功力有限描述不出来了,2020年绝逼去深挖一波争取可以玩玩源码)

另外作为程序员我们该做到的不是去比较语言的性能,而是在适应的场景用适应的技术顺势而为。过分的执着语言本身的性能不能通盘考虑往往是单一语言的布道者,但不是一名合格的程序员和技术管理者。

《大型网站技术架构》

从极客时间上了解到了李智慧这个人,并且公司大佬提过一下这本书就买来读读了。

《大型网站技术架构》

理解

  1. 这本书把单体架构和分布式架构交代的明明白白
  2. 对于我个人来说算把我做现在这个项目的经历做了归纳总结,有形或无形中异曲同工
  3. 内审自己学过的分散知识,这算是一次点线成面的过程

同时,在我们内部系统中看见了很多人评价这本书入门级没用什么什么的。其实内心来说真的想鸣个不平,技术没有简单复杂的说法只有适用性和当前最优解(相对当前业务场景,公司形态)

邋遢的整理

粗略的整理了下这本书中讲到的知识点,结合自己浅薄的知识做出了一个整理。同样也是算当前阶段对自己的一个内审和总结吧类似《评估WEB网站架构的一些标准和处理方法》一样

张俊杰的博客-对架构的阶段性理解

资源

xmind下载

Supervisor 的使用和集群下的管理

项目中经常会有些需要常驻后台的服务,它的存活问题也成为了一个重点。在 php laravel 和 go 的 beego 中同时提到了 Supervisor。 项目中也刚好有使用(运维包干了),本着自己也会好沟通的原则进行了下面的尝试

Supervisor 是什么

Supervisor是一个客户端/服务器系统,允许其用户在类UNIX操作系统上控制多个进程。

我的理解就是它是一个可以提供web管理页面的守护进程工具

安装

CentOS

1
2
3
4
//安装
yum install supervisor
//生成配置文件
echo_supervisord_conf > /etc/supervisord.conf

安装成功后会有2个程序 supervisordsupervisorctl

supervisor d 负责监听进程
supervisor ctl 负责管理supervisord监听的进程,并且监听supervisord

测试运行

在 /etc/supervisord.conf 添加

1
2
[program:foo]
command=/bin/cat

如果只杀死主进程,子进程就可能变成孤儿进程。通过这两项配置来确保所有子进程都能正确停止

1
2
stopasgroup=true             ; send stop signal to the UNIX process group
killasgroup=true ; SIGKILL the UNIX process group

运行

1
2
// -n 前台运行
supervisord -c /etc/supervisord.conf -n

测试图

可以看见我们Supervisor成功启动,并且拉起和监听了一个 PID 26540 的 cat进程

supervisorctl 使用

supervisorctl 需要使用 supervisord 一样的配置文件。在配置文件中我们可以添加一些分组信息来更好的区分和使用

1
2
3
[group:test]
programs=foo ; each refers to 'x' in [program:x] definitions
priority=999 ; the relative start priority (default 999)

下面我们运行 supervisorctl

1
supervisorctl -c /etc/supervisord.conf

会看见如下输出

可以看见我们 test 分组下的foo正常工作,并且进入交互模式。我们输入 help可以看见它支持的一些命令

1
2
3
4
default commands (type help <topic>):
=====================================
add clear fg open quit remove restart start stop update
avail exit maintail pid reload reread shutdown status tail version

在退出交互模式的情况下 正常 supervisorctl + commands 可当作shell使用,比如这个场景

我们发布Nginx反向代理下的go代码

  1. git push 到远程仓库
  2. rdc打包同步到服务器
  3. 服务器上使用 supervisorctl restart name 来重启这个进程完成代码的部署发布

安装web管理界面(集群管理)

这里我们使用官方推荐的 PHP monitor

  1. 创建目录下载项目源码
1
2
make monitor
git clone git@github.com:mlazarov/supervisord-monitor.git

2.修改 supervisord.conf http serve配置

1
2
3
4
[inet_http_server]         ; inet (TCP) server disabled by default
port=*:5555 ; (ip_address:port specifier, *:port for all iface)
username=zjj ; (default is no username (open server))
password=zjj ; (default is no password (open server))
  1. 修改PHP配置文件
1
2
3
4
cd ~/monitor/supervisord-monitor/application/config

cp supervisor.php.example supervisor.php

1
2
3
4
5
6
7
8
$config['supervisor_servers'] = array(
'测试' => array(
'url' => 'http://116.196.88.52:5555/RPC2',
'port' => '5555',
'username' => 'zjj',
'password' => 'zjj'
),
);
  1. 配置Nginx vost
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
34
35
36
37
38
39
server {

listen 5566;

index index.html index.htm index.php;
root /opt/www/monitor/supervisord-monitor/public_html;

location / {
try_files $uri $uri/
/index.php$is_args$query_string;
}

location /nginx_status
{
stub_status on;
access_log off;
}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}

location ~ .*\.(js|css)?$
{
expires 12h;
}


location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
  1. 重启 supervisord 和 Nginx 让配置生效

ps:

  1. 集群管理才需要用到 monitor,非集群可以打开自带的 inet_http_server 模块功能够用
  2. monitor 没有权限验证可以再 Nginx 上加上 401保证安全

产考文档

https://www.rddoc.com/doc/Supervisor/3.3.1/zh/
http://supervisord.org/running.html

评估WEB网站架构的一些标准和处理方法

我司现有类比项目组代码是否优秀的指标有2个,一是听云中的响应时间,一是框架中的执行时间。一直感觉有所缺失,而且太过于看重面板数据少了一些说不出来的东西(犹如中国教育制度,只看答卷成绩。

衡量一个B/S应用是否优秀的方法有很多,除了简单的面板数据外应当具备以下7点

  • 性能
  • 伸缩性
  • 简单性
  • 可见性
  • 移植性
  • 可靠性/高可用
  • 可修改性

这7种特性会结合项目现状,公司形态,市场等众多情况有所平衡,如一张雷达图。


按照上面7点结合自己浅薄的工作经验,整理出了一张图。希望一年过后看见这张图的时候有更多新的认识

B/S架构标准

资源

下载xmind

《图解TCP/IP》

从事B/S相关的软件开发逃不掉和协议打交道,也应该较为全面和系统的学习理解,TCP/IP 是易懂难精的网络通讯协议群,也是一项基本内功。

封面

这边书讲的范围很大,但是有的细节不够细。

主要偏入门讲个大概笼统的知识。

网络编程这一块还是要去啃《TCP/IP详解 卷2》,可以巩固大学时教的那些网络知识。

子网划分,网络分层模型,TCP/UDP 滑动窗口 写得比较生易懂。

只做了前几章的总结大致如下


协议

协议的出现,目的是为了统一标准。让不同厂商不同设备之前实现互通。

OSI产考模型

OSI 协议是为了让异构的设备之间实现通信而存在的保准化的网络体系结构

分层名称 功能
应用层 针对应用的协议
表示层 设备固有数据格式,和网络标准格式转换
会话层 通信连接管理,负责建立和断开连接
传输层 管理2个节点间的数据传输
网络层 管理地址和路由选择
数据链路层 互连设备之间传送和识别数据帧
物理层 0,1信号传输

应用层

为应用程序提供规定的相关协议,比如redis的通信协议,http协议等都是在应用层

表示层

将设备固有格式转化为网络标准格式,或讲下层的网络标准格式转为上层的设备固有格式

会话层

负责建立和断开通信连接,以及数据的分割等数据传输相关管理

传输层

起到可靠传输的作用,只在通信双方节点上进行处理,而无需在路由器上处理。是物理上的传输逻辑,有重发机制

网络层

将数据传输到目标地址。负责寻址,路由选择。交换机在这一层和下一层

数据链路层

数据帧的生成与接收,负责把0,1转换成电脉冲。网络层负责整个数据发送给目标,数据链路只负责一个分段的数据

物理层

负责0,1比特流

传输方式分类

有连接

TCP是比较典型的有连接方式,会2端对答后发生数据

无连接

UDP是比较典型的无连接方式,直接抛出数据

电路交互&&分组交换

电路交换

  • 交换机直接建立双端通信,直到连接断开
  • 不支持多端通信

分组交换

  • 在交换路由上有缓存队列
  • 多个数据包发送到对机,不直接连接对端而是通过路由交换转发数据

#地址

IP/MAC 地址都具有唯一性,但是只有IP地址具有层次性,MAC地址是真正负责通信的地址。他们通过路由映射表关联起来。

TCP/IP协议

TCP/IP最早用在军业用途,具有开放性和实用性,它的协议繁指协议群包括。
OSI之所以没普及是因为没有尽快制定可行性较强的协议,没有提出应对技术快速革新的协议,以及没有及时后期改良

应用协议

* HTTP
* SMTP
* FTP
* TELNET
* SNMP

传输协议

* TCP
* UDP

网际协议

* IP
* ICMP
* ARP

路由控制协议

* RIP
* OSPF
* BGP

RFC

是一系列以编号排定的文件。文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件

包,帧,数据报,段,消息

* 包 
    全能术语
* 帧
    表示数据链路中包的单位
* 数据报
    IP/UDP等网络层以上的包单位
* 段
    TCP数据流中的信息
* 消息
    协议中数据的单位
    

数据收发包

包流动时,从前往后依次附加 以太网包首部,IP包首部,TCP/UDP 包首部, 应用的包首部,数据
每个包首部至少包含两个信息 1.发生接收地址 2.上层协议类型 。经过每个协议分层时,必须有识别包发送端和接收端的信息


以太网帧格式

以太网帧本体前端是以太网首部占14字节
6个字节的目标MAC,6个字节源MAC 2字节类型
46~1500字节的数据
尾部一个FCS(帧验证序列)4字节

常见类型如下


TCP/IP 协议

TCP/IP在OSI参考模型中的 第3层-网络层
网络层的目的是为了实现点对点通信
他的下一层数据链路层是为了互连同一种数据链路的节点之间进行包投递
需要跨不同的数据链路就需要借助网络层

数据链路层负责直连两个网络进行通信传输
网络层IP负责在没有直连的两个网络之间进行通信

IP 采用面向无连接通信,原因有2,1为了简化 2为了提速

IP地址的定义

IP 地址由32位 每8位一组进行表示 ,及一类IP地址有 2 的 32立方个。用子网掩码的方式来进行分组标识

IP地址分类

  • A类

IP地址首位”0”开头 1-8位是网络标识。十进制表示 0.0.0.0 ~ 127.0.0.1,后24位是主机标识

  • B类

IP地址首2位”10” 开头 1-16位是网络表示。十进制表示 128.0.0.1 ~ 191.255.0.0 后 16位表示主机标识

  • C类

IP地址首3位”110” 开头 1-24位是网络表示。十进制表示 192.168.0.0 ~ 239.255.255.0 后 8位表示主机标识

  • D类

IP地址首3位”1110” 开头 1-32位是网络表示。十进制表示 224.0.0.0 ~ 239.255.255.255 没有主机标识,通常用于广播

ps:主机标识位不能全部为0或1,全部为0表示对应信息不可获知,全部为1表示广播

请我喝杯咖啡吧~