《程序员读》

《程序员读》

——精心为程序员打造的编程开发阅读软件。
挖掘细致的阅读习惯,
热门开发分类,
每日内容更新,
在线视频观看,
下载离线阅读,
片段选择分享,
网络收藏摘抄,
开发文章发表,
附近猿媛查看,
兴趣好友联系,
二维码快速扫描,
日夜间模式摇一摇轻松切换。


iPhone版 v1.82

扫一扫 http://t.cn/8k1VM8d
《程序员读》精心为程序员打造,隆重登陆AppStore


Android版 v1.4

扫一扫 http://t.cn/R74JjLj
《程序员读》精心为程序员打造


1136a1136d1136e1136c1136b

..

supervisor

Linux的后台进程运行有好几种方法,例如nohup,screen等,但是一个服务程序要可靠地在后台运行,就需要把它做成daemon,最好还能监控进程状态,在意外结束时能自动重启。
Supervisor就是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,能做开机启动,并监控进程状态,异常退出时能自动重启,它还提供了web管理界面。

安装supervisor
$ sudo apt-get install supervisor
卸载supervisor
$ sudo apt-get remove supervisor
安装完成后,查看其配置文件
$ vi /etc/supervisor/supervisord.conf
查看到自定义配置存放目录为 files=/etc/supervisor/conf.d/*.conf
$ cd /etc/supervisor/conf.d
创建要自启动的服务配置
$ sudo vi service1.conf

配置解释:

[program:service1] #服务名称,在supervisor中可以用这个名字来管理该程序。
command=  #启动程序的命令
directory=/home/user1 #相当于在该目录下执行程序
autostart=true #设置随supervisor的启动而启动
autorestart=true #程序停止之后是否需要重新将其启动
startsecs=10 #重新启动时,等待的时间
startretries=36 #重启程序的次数
redirect_stderr=true #是否将程序错误信息重定向的到文件
stdout_logfile=/home/user1/%(program_name)s_log.txt #将程序输出重定向到该文件
stderr_logfile=/home/user1/%(program_name)s_err.txt #将程序错误信息重定向到该文件
numprocs = 2
numprocs_start = 8850
user = #系统用户名
process_name = 163gs-%(process_num)s

配置好后重启supervisor服务

sudo service supervisor restart

supervisorctl 是命令行管理工具,它的查数查看

supervisorctl help
supervisorctl version
default commands (type help ):
=====================================
add    clear  fg        open  quit    remove  restart   start   stop  update 
avail  exit   maintail  pid   reload  reread  shutdown  status  tail  version

Supervisor3.0之前的旧版本会有不同:
配置文件位置 /etc/supervisord.conf
需要在supervisord.conf尾部按示例添加服务配置
服务名称是 supervisord
sudo service supervisord restart

更多配置参考
http://supervisord.org/configuration.html
http://www.jianshu.com/p/65f31f5a4e84
http://www.jianshu.com/p/805977544d7f
http://www.linuxidc.com/Linux/2015-04/116701.htm

SSH代理

通过ssh代理功能连接到内网linux服务器

需要通过一台公网主机代理

  1. 建立内服到云的反向代理
  2. 建立云服上的正向代理
  3. 在个人机器上通过云服ssh到内服

下面三行命令分别在内网服务器、云服务器、和个人电脑上执行。

ssh -fCNR 8081:localhost:22 云用户@云IP
ssh -fCNL "*:8082:localhost:8081" localhost
ssh -p 8082 内服用户@云IP

8081是云服上端口,用来与内服上的22端口绑定。
8082为云服上转发端口,将数据转发到8081端口。
*表示接受来自任意机器的访问。
https://segmentfault.com/a/1190000002718360

SSH 参数解释

  • -f 后台运行
  • -C 允许压缩数据
  • -N 不执行任何命令
  • -R 将端口绑定到远程服务器,反向代理
  • -L 将端口绑定到本地客户端,正向代理

sshkey免密码

ssh即secure shell,即安全shell。sshkey用来加密传输。
加密传输的算法有多种,git可使用rsa,rsa使用一对特定的数字,这两个数字就是在使用git和github的时候所遇到的public key(公钥)以及private key(私钥)。公钥就是那个用来加密的数字,在本机生成了公钥之后,要上传到github。从github发回来的,是用公钥加密过的数据,可以用本地的私钥来还原。不管是公钥还是私钥,丢失一个都不能用了,解决方法也很简单,重新再生成一次,然后在github.com里再设置一次。
方法一,密钥验证。安全、配置简单、同时ssh、scp、sftp等登录都免去了输入密码的麻烦。

查看~/.ssh下是否已经生成id_rsa.pub,如果已存在可以直接使用它,覆盖它可能会影响现有服务正常使用。
如果没有则客户端生成密匙对,最后在~/.ssh下生成id_rsa.pub公钥。
#ssh-keygen -t rsa
然后将公钥通过scp命令上传到服务器上,将本地生成的公钥拷贝到远程服务器的下并改名为authorized_keys。
#scp ~/.ssh/id_rsa.pub 用户名@服务器的ip:~/.ssh/authorized_keys

方法二,通过sshpass明文传送密码匙,安全性不高。

安装sshpass允许你用 -p 参数指定明文密码,然后直接登录远程服务器。例如:
# sudo apt-get install sshpass
sshpass允许用 -p 参数指定明文密码,然后直接登录远程服务器
# sshpass -p '密码' ssh 用户名@服务器ip

http://www.linuxidc.com/Linux/2011-02/31921.htm
http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646346.html

开机自动运行ssh命令建立反向代理

通过supervisor配置自启动,ssh服务配置示例:

[program:service2]
command=ssh -fCNR 8081:localhost:22 云用户@云IP
autorstart=true #设置随supervisor的启动而启动
directory=~ #相当于在该目录下执行程序
autorestart=true #程序停止之后是否需要重新将其启动
startsecs=10 #重新启动时,等待的时间
startretries=36 #重启程序的次数
redirect_stderr=true #是否将程序错误信息重定向的到文件
stdout_logfile=~/%(program_name)s_log.txt #将程序输出重定向到该文件
stderr_logfile=~/%(program_name)s_err.txt #将程序错误信息重定向到该文件
numprocs = 2
numprocs_start = 8850
user = #系统用户名
process_name = 163gs-%(process_num)s

如果日志中报ssh “permissions are too open” error
.ssh/id_rsa 不要设置为777权限,Chmod 400 即可。

Ubuntu

terminal中出现菱形乱码修改方法

vim /var/lib/locales/supported.d/local
里面有这行内容
en_US.UTF-8 UTF-8

sudo vim /etc/default/locale 修改为
LANG=”en_US.UTF-8″
LANGUAGE=”en_US:en”

重启动

nodejs

socket.connect参数顺序

创建TCP客户端的坑之connect的参数顺序是先port再host:

var net = require('net');
var HOST = '127.0.0.1';
var PORT = 8888;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
    console.log('CONNECTED TO: ' + HOST + ':' + PORT);
    // 建立连接后立即向服务器发送数据,服务器将收到这些数据 
    client.write('message');
});

pm2

Nodejs开发中修改完代码以后需要重启服务才能看到效果。
forever可以通过对资源变化的检测做到变化后自动重启,并且可以在异常后重启,保证服务一直在线。
pm2 = P (rocess) M (anager)2,是可以用于生产环境的Nodejs的进程管理工具,并且它内置一个负载均衡。它不仅可以保证服务不会中断一直在线,并且提供0秒reload功能,还有其他一系列进程管理、监控功能。并且使用起来非常简单。

pm2的安装和使用

pm2 start app.js
pm2 save
pm2 startup
pm2 reload all
pm2 list
pm2 restart [id]

~# ls -a显示所有包括隐藏的文件,可以看到.pm2文件夹

更多pm2常用的命令用法介绍
http://i5ting.github.io/node-deploy-practice/
http://www.111cn.net/sys/linux/120062.htm
http://www.pangjian.info/2016/12/02/deploy-nodejs-pm2-1/?utm_medium=referral

缓存

nodejs通过文件名缓存加载过的模块,这里是全局缓存,不是页面内缓存。

grpc

安装grpc-tools
$ sudo npm install grpc-tools -g
把proto文件生成js文件
$ /usr/local/lib/node_modules/grpc-tools/bin/protoc --js_out=import_style=commonjs,binary:. --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/lib/node_modules/grpc-tools/bin/grpc_node_plugin *.proto

http参数

参数名是区分大小写的,不存在的参数值是undefined
取get地址?后面的传参
var id=req.query.id;
取rest路径中的参数
var id = req.params.id;
取post时json对象中的参数
id=req.body.id;

网络基础

子网掩码

255.255.0.0 表示192.168.(0 – 255).(0 – 255)的IP地址都在同一网段内。
255.255.255.0 表示192.168.0.(0 – 255)的IP地址都在同一网段内。

A、B、C类IP地址

IP地址是互联网上进行寻址的地址编码,IP地址有4个字节,32位组成。为了区分IP地址,将IP地址划分为A类,B类,C类。
C类地址第1字节、第2字节和第3个字节为网络地址,第4个字节为主机地址。第1个字节的前三位固定为110。地址范围 192.0.0.0到223.255.255.255,私有地址 192.168.0.0到192.168.255.255,默认子网掩码 255.255.255.0,第1个字节的二进制值前三位位必须是“110”,即11000000开始到11011111结束,所以第一段的范围为192~223。
一个C类地址是由3个字节的网络地址和1个字节的主机地址组成。每个C类地址可连接254台主机(0是网络号不可用, 255是广播地址,除去这2个,可用的就是254个地址),Internet有2097152个C类地址段(32*256*256),有532676608个地址(32*256*256*254)。

如果电脑超过了254台,如果子网掩码设为255.255.255.0,需要有2个网段,不同网段下的电脑需要路由器或者三层交换机才能互相访问。

双网卡设置内外网

为默认网关(default gateway)只能是一个!删掉局域网的网关配置。
http://blog.csdn.net/chenlycly/article/details/52136960

Linux命令

安装命令

一般来说著名的linux系统基本上分两大类:

1.社区组织维护的发行版本 Debian系列:Debian、Ubuntu等
apt-get:Advanced Packaging Tool,apt是Debian Linux发行版中的APT软件包管理工具。所有基于Debian的发行都使用这个包管理系统。deb包可以把一个应用的文件包在一起,类似Windows上的安装文件。

一般可以通过使用ppa/源方式或下载两种方式安装软件。
Ubuntu 安装 JDK 7 / JDK8 的两种方式
http://www.cnblogs.com/a2211009/p/4265225.html
如果在ubuntu上报 add-apt-repository: command not found,先安装python。
sudo apt-get install python-software-properties
sudo apt-get install software-properties-common

2.商业公司维护的发行版本 RedHat系列:Redhat、Centos、Fedora等
Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器。基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。
简单点说, rpm 只能安装已经下载到本地机器上的rpm 包. yum能在线下载并安装rpm包,能更新系统,且还能自动处理包与包之间的依赖问题。

查看资源信息
yum info supervisor

修改网络

服务器地址设置为static,不使用dhcp
ifconfig
sudo vim /etc/network/interfaces
sudo vim /etc/resolv.conf
sudo /etc/init.d/networking restart 可能不起作用
sudo reboot

另外长按重置路由器要在带电时操作。

搜索

grep全称是Global Regular Expression Print,表示全局正则表达式版本,是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来。

$ grep 'abc' *.java
$ ps -ef | grep java

常用命令

netstat -anptl 查看服务监听端口
tar -zxvf xxxx.tgz 解压压缩包

JVM

JVM

JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言的一个非常重要的特点就是与平台的无关性,使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码,Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

http://www.mamicode.com/info-detail-1028149.html

HotSpot VM

HotSpot VM是绝对的主流的JVM。大家用它的时候很可能就没想过还有别的选择,或者是为了迁就依赖了Oracle/Sun JDK某些具体实现的烂代码而选择用HotSpot VM省点心。Oracle / Sun JDK、OpenJDK的各种变种(例如IcedTea、Zulu),用的都是相同核心的HotSpot VM。从Java SE 7开始,HotSpot VM就是Java规范的“参考实现”(RI,Reference Implementation)。把它叫做“标准JVM”完全不为过。当大家说起“Java性能如何如何”、“Java有多少种GC”、“JVM如何调优”云云,经常默认说的就是特指HotSpot VM。可见其“主流性”。JDK8的HotSpot VM已经是以前的HotSpot VM与JRockit VM的合并版,也就是传说中的“HotRockit”,只是产品里名字还是叫HotSpot VM。不过要留意的是,这里我说的HotSpot VM特指“正常配置”版,而不包括“Zero / Shark”版。Wikipedia那个页面上把后者称为“Zero Port”。用这个版本的人应该相当少,很多时候它的release版都build不成功…

JVM调优-分代垃圾回收

分代垃圾回收采用分而治之的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。

虚拟机中的共划分为三个代:年轻代(Young Generation)、年老代(Old Generation)和持久代
(Permanent Generation)。

年轻代
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。

老年代
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

持久代
持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。

什么情况下触发垃圾回收?

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。

Scavenge GC(搜寻)
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比ScavengeGC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用

从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

https://my.oschina.net/ydsakyclguozi/blog/404389

操作系统

线程原理

多线程是宏观上的并行,微观上依旧是串行
多线程之所以能够实现,第一是硬件的功劳,也就是CPU。现代的CPU有一个很重要的特性,就是时间片。每一段获得CPU的代码只能运行一个时间片规定的时间,例如10ms。到时间之后CPU就会把正在运行的代码暂停,然后发生一个中断。这个中断是系统级的事件,只有操作系统能够接手,这样控制权就落到了操作系统手里。
接下来就是操作系统的事情了。线程对操作系统来说就是一段代码+运行时数据(主要是寄存器数据,还有线程中与资源相关的数据,比如打开的文件等)。操作系统会为每个线程保存相关的这些数据,当接收到来自CPU的时间片中断事件时,就会按一定规则从这些线程中选择一个,恢复它的运行时数据,这样CPU就可以继续执行这个线程了。

选择一个合适的线程的过程称为线程调度,最简单的规则是FCFS,也就是按排队的时间先后顺序调度,谁先来排队,下一个就让谁运行。另外还有按优先级、按任务大小等调度策略,以及多种策略的组合等方式。详细的可以去了解一下操作系统的原理。

Java中线程实现主要依赖于操作系统,其本身不进行线程管理。它只是把线程相关的操作进行了封装和抽象,以方便我们使用。例如,Java中的线程可以设置优先级,但如果操作系统本身不支持优先级调度策略,那么为线程设置优先级是没有任何作用的。

操作系统维护了一个任务队列,Java调用.start()之后会把任务放进队列之中等待执行,CPU不会一直执行同一个任务,而是每执行一段时间,就保存当前任务的状态,然后从队列中取到下一个任务继续执行。

总之,操作系统基于cpu时间片通过线程调度,来达到轮换执行线程对应的代码。

对阻塞及异步的概念理解

阻塞指的是进程被阻塞,即等待任务处理完成后进程才再往下走。
同步指的是资源被按顺序使用。

反向代理

正向代理:代理服务器代理的是客户端,客户端知道服务器,也知道代理服务器。正向代理服务器帮助客户端连接目的服务端。
反向代理:代理服务器代理的是服务端,客户端不知道服务器,只知道代理服务器。反向代理服务器向客户端隐藏了真实的服务端。