supervisor

Linux的后台进程运行有好几种方法,例如nohup,screen,upstart,systemd等,但是一个服务程序要可靠地在后台运行,就需要把它做成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

更新配置到 supervisord:

supervisorctl update

Supervisor2.x版本有一些不同:

配置文件位置 /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
http://www.cnblogs.com/haoliansheng/p/6611714.html
http://www.cnblogs.com/zhangym/p/5833353.html

错误处理

报错 unix:///tmp/supervisor.sock no such file
出现上述错误的原因是supervisord并未正常启动,重新启动supervisord即可。

supervisord -c /etc/supervisord.conf 
或 supervisord -c /etc/supervisor/supervisord.conf

ubuntu 18.04中 apt找不到supervisor源,修改Ubuntu的apt-get源为国内镜像源。
https://blog.csdn.net/zgljl2012/article/details/79065174/

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

然后使用git clone git@gitee.com:xxx/
xxx.git 下载代码

方法二,通过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 即可。

自动ssh登录脚本

#!/usr/bin/expect
set timeout 10
spawn ssh root@ip
expect {
    "*yes/no*" {
        send "yes\n";
        exp_continue;
    }
    "*password:*" {
        send "密码\r";
        exp_continue;
    }
}
interact

Ubuntu

http://cn.ubuntu.com/

切换桌面
启动到登录界面时,可以ctrl+alt+F1…7,切换桌面

解压tgz
tar -zxvf xxxx.tgz

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”

重启动

安装boost

# apt-cache search boost
# apt-get install libboost-dev

安装最新的版本
到https://sourceforge.net/projects/boost/files/boost/1.63.0/boost_1_63_0.tar.bz2/download下载
https://jaist.dl.sourceforge.net/project/boost/boost/1.63.0/boost_1_63_0.tar.bz2

# tar --bzip2 -xvf boost_1_63_0.tar.bz2
# cd boost_1_63_0
# ./bootstrap.sh  可以看到在当前目录下生成了安装工具bjam和b2
# ./bjam  编译时间比较长
# ./bjam install 安装完毕后的头文件默认是在/usr/local/include目录下,.a和.so在/usr/local/lib目录下。

http://blog.csdn.net/chinawangfei/article/details/50351371
http://blog.csdn.net/sinat_23857245/article/details/45225147
http://blog.csdn.net/yhrun/article/details/8099630

nginx的安装目录

sudo apt-get install nginx
完成后的安装目录在 /etc/nginx, nginx命令文件位置在 /etc/init.d/中
nginx -v #nginx version: nginx/1.4.6 (Ubuntu)
可能新的安装版本位置在/usr/local/nginx,mac上版本是 nginx version: nginx/1.10.2

查看版本
cat /etc/issue

ubuntu16.04 安装ssh
sudo apt-get install openssh-server
sudo service ssh start
sudo ps -e | grep ssh 或者 sudo service ssh status
管理ubuntu server自启程序
http://yaxin-cn.github.io/Linux/manage-startup-program-on-ubuntu-server.html

Ubuntu16.04使用shadowsocks GUI
http://blog.csdn.net/alextan_/article/details/61618073
Ubuntu 16.04下安装64位谷歌Chrome浏览器
http://www.linuxidc.com/Linux/2016-05/131096.htm
设置chrome翻墙,可以在终端启动chrome的时候附带上参数设置代理
/usr/bin/google-chrome-stable –proxy-server=socks5://127.0.0.1:1080
ubuntu16.04 chrome WebGL 不受支持,在Ubuntu下开启Chrome的WebGL功能(开启GPU加速)
http://tieba.baidu.com/p/3253462709

nodejs

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。V8引擎速度非常快,不用担心多线程,锁,并行计算产生的问题。
如果你编码不认真,java会给你丘,而nodejs会给你很多坑,使用WebStorm开发有的错误会报错,有的错误则会运行通过,产生意料之外的行为。

node擅长处理io密集型业务,在处理CPU密集计算的时候可选择以下方案:
1.node业务逻辑都以单线程进行处理,那如果其中包括耗时的复杂计算,可以以rpc 的方式让其他编译型语言处理,node 只负责搬运。 也就是说node负责接受请求、执行io、发送回复,其他逻辑由擅长计算的语言处理。
2.业务逻辑交给node,便于快速开发。其他内容交给其他语言,或者直接交给c++,用node-gyp编译一下。

Node.js Manual & Documentation
http://nodejs.cn/api/
http://shouce.qdfuns.com/nodejs/index.html

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

pm2 = P (rocess) M (anager)2,是可以用于生产环境的Nodejs的进程管理工具,它内置一个负载均衡,可以保证服务不会中断一直在线,并且提供0秒reload功能,还有其他一系列进程管理、监控功能。

pm2的安装和使用

npm install -g 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

不同操作系统平台下需要重新下载grpc,即重新执行 npm install grpc

http参数

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

http响应内容重写

router.get('/', function(req, res, next) {
    var content=getNowFormatDate();
    //重写,去掉了其它的头
    res.connection.write('HTTP/1.1 200 \r\n\r\n' + content, 'ascii', function(){
        res.connection.emit('end');
    });
    
    //res.send(content);
});

function getNowFormatDate() {
    var date = new Date();
    var seperator1 = "-";
    var seperator2 = ":";
    var currentdate = date.getFullYear() + seperator1 + addZero(date.getMonth() + 1) + seperator1 + addZero(date.getDate())
        + " " + addZero(date.getHours()) + seperator2 + addZero(date.getMinutes()) + seperator2 + addZero(date.getSeconds());
    return currentdate;
}

function addZero(num) {
    if (num >= 0 && num <= 9) {
        num = "0" + num;
    }
    return num;
}

express超时重复请求

3种解决办法
1.httpserver设置超时函数
var app = express();
var http = require('http');
var server = http.createServer(app);
server.on('timeout', onTimeout);
function onTimeout(err) {
}
2.加大超时时间
server.timeout=10*60*1000;
3.request或response设置超时处理方法
router.post('/xxx',function(req,res,next){
req.setTimeout(240000,function () {
});
}
http://blog.csdn.net/puncha/article/details/9378197

async

npm install async --save,其中--save参数会在工程package.json中dependencies中增加一条依赖;
async主要实现了三个部分的流程控制功能:
集合: Collections
流程控制: Control Flow
工具类: Utils

Nodejs异步流程控制Async


Async提供了很多针对集合的函数,简化对集合的异步操作步骤
https://my.oschina.net/chinacaptain/blog/469810

常用的流程控制:
series 串行,保证顺序,不依赖
parallel 并行
waterfall 依赖串行、瀑布流,之间需要传递结果时使用
auto 并行加依赖串行
apply 给函数预置第一个参数,然后返回一个新的函数。

async.series({
	one: function(callback){
		callback(null, 1);
	},
	two: function(callback){
		callback(null, 2);
	}
},function(err, results) {
	console.log(results);
});
或者
async.series([
            function(callback){
                callback(null, 1);
            },
            function(callback){
                callback(null, 2);
            }
        ],function(err, results) {
            console.log(results);
        });

socket超时

var socket = new net.Socket(); 创建后
socket.setTimeout(10 * 1000, function () {
if(!idle) {
console.log('timeout not idle');
if (callback) {
callback(false, {message: '请求超时'});
callback = null;
}
}else{
console.log('timeout idle');
}
});
不执行connect或write,10秒后会打印timeout idle。
等待9秒后再执行connect,如果connect很久,会在connect开始10秒后打印timeout not idle。
即创建socket和connect或write会开始新一轮超时计时。但不能多次设置settimeout,会引发emit数量警告。
idle为自定义的状态,在connect或wirte时设置idle=false。

安装最新稳定版本
https://segmentfault.com/a/1190000007542620

淘宝NPM镜像CNPM
http://npm.taobao.org/
npm install -g cnpm --registry=https://registry.npm.taobao.org

Promise

Promise.reduce 方法才是一个顺序执行 Promise 的方法,一些顺序执行的方法,比如 Promise.mapSeries 和 Promise.each ,都是基于 Promise.reduce 来实现的,这里的 reduce 和 Array.reduce 一样。

环境变量 get-env

#app.js中代码:
app.env = require('get-env')({
  init_env1:'init_env1',
  init_env2:'init_env2',
});

#根据以上代码,在命令行中调用时有效的NODE_ENV值有:dev,prod,init_env1,init_env2
export NODE_ENV=init_env1
node app.js 

传递参数

例如:node const-generate.js cx-kqy.txt
process.argv的值相当于是把整个命令按空格分割成字符串数组。

var arguments = process.argv.splice(2);
console.log('所传递的参数是:', arguments);
process.argv.forEach(function (val, index, array) {
    console.log(index + ': ' + val);
});

node端的缓存策略

如果缓存数据量不大,不需要多进程共享,可以node-cache方案或自己实现。如果需要多node.js进程共享并持久化,可以考虑用redis来实现,Redis的优势在于能够供多进程共享,有完善的备份和恢复机制。

网络基础

子网掩码

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

笔记本同时连接宽带和WIFI,设置上网优先顺序:打开网络偏好设置/设定服务顺序

Linux命令

wget是一种下载软件类似与迅雷 如果要下载一个软件 我们可以直接: wget 下载地址
apt-get是ubuntu下的一个软件安装方式,它是基于debain。
yum是redhat、centos下的一个软件安装方式,它是基于Linux的。
aptitude与 apt-get 一样,是 Debian 及其衍生系统中功能极其强大的包管理工具。与 apt-get 不同的是,aptitude在处理依赖问题上更佳一些。举例来说,aptitude在删除一个包时,会同时删除本身所依赖的包。这样,系统中不会残留无用的包,整个系统更为干净。

安装命令

一般来说著名的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
//移除
sudo apt-get remove xxx

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

#查看资源信息
yum info supervisor
#查找软件包
yum search ~
#卸载
yum remove ~

修改网络

服务器地址设置为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默认对字母大小写敏感,选项-i可以指定为不敏感

grep 'abc' *.java
#递归查找所有js文件中的setting
grep -r 'setting' ./ --include='*.js'
$ ps -ef | grep java

常用命令

netstat -anptl 查看服务监听端口
netstat -tunpl 查看服务监听端口
ss -tunpl (ss:socket statistics)

netstat属于net-tools工具集,ss属于ipoute工具集。替换方案如下:

tar -zxvf xxxx.tgz 解压压缩包
#移动当前目录下的全部内容到另一个目录中
mv ./* ../dir2/
合并目录
cp -frap source_dir/* dest_dir/
拷贝目录,结果为dest_dir/source_dir/
cp -r source_dir dest_dir/
查看目录下各目录文件大小:
cd到上级目录,然后输入一条命令即可查询每个子目录所占用的空间大小,可以更改–max-depth参数的值,该参数表示查询子目录的层级,当前为1层。
du -h –max-depth=1

查看系统信息

cat /etc/issue
cat /etc/os-release
head -1 /etc/issue
lsb_release -a  #发行版信息,prints certain LSB (Linux Standard Base) and Distribution information。
lsb_release -d -s 
cat /etc/redhat-release #查看os版本
free -h #查看内存使用
uname -a #查看内核/操作系统/CPU信息
uname -r #查看内核版本,4.2.0-27-generic

其它软件信息

#glibc库版本
strings /lib64/libc.so.6 |grep GLIBC_
#下载文件
wget url1
curl -O url1

http://www.centoscn.com/CentOS/help/2013/0728/738.html

tcpdump抓包工具

tcpdump是linux命令行下常用的的一个抓包工具
http://www.45it.com/linux/201409/37882.htm

vi命令

:$ 跳到文件最后一行,:0或:1 跳到文件第一行
s
hift + g 跳到文件最后一行,gg 跳到文件第一行。
tail -n 20 xx.txt 查看最后20行

性能查看

Linux服务器I/O过高原因及解决方法
http://www.linuxidc.com/Linux/2016-09/134813.htm

统计代码行数

find . -name "*.java"|xargs wc -l|grep "total"|awk '{print $1}'  
find . -name "*.java"|xargs cat|wc -l
#排除node_modules、bower_components等目录
find . ! -path '*node_modules*' ! -path '*bower_components*' ! -path '*libs*' ! -path '*lib*' ! -path '*grunt*' ! -path '*angular*' -name "*.js" | xargs cat | wc -l

查看端口及服务

lsof(list open files)是一个列出当前系统打开文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。

#查看端口是否在使用中
lsof -i :14000

linux sed

sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换、删除、新增、选取等特定工作

写内容到文件
echo ‘内容’ > 文件

查看进程
ps 或 top

查看当前系统环境变量
env

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

Java调优之jvm和线程的内存分析

在内存小的机器上经常出现的问题就是Cann’t allocate memory和OutOfMemoryError错误。在jvm内存调整过程中,我们经常使用的参数有:
-Xms 为jvm启动时分配的堆内存,比如 -Xms200m 表示分配200M。
-Xmx 为jvm运行过程中分配的最大堆内存,比如 -Xms500m 表示jvm进程最多只能够占用500M内存。
-Xss 为jvm启动的每个线程分配的栈内存大小,默认JDK1.4中是256K,JDK1.5+中是1M。
-Xmn 年轻代大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。比如-Xmn2g设置年轻代大小为2G。

一般jvm出现Cannt’ allocate memory的错误是机器的内存不够,导致系统无法为jvm分配给定的内存,这个在启动时犹为突出,所以会在启动参数中设置-Xms来指定。
OutOfMemoryError错误则一般会在系统运行一段情况后出现,绝大部分也是机器内存不够或是JVM本身的内存空间已被用尽,这时就要根据情况进行调整:如果是JVM本身的内存空间用尽,则需要调整-Xmx参数来分类jvm的可用内存,如果是机器内存不够则要增加内存或是调优程序。

Xms、Xmx主要是来设置jvm的最小可用内存和最大可用内存,属于进程级别的内存控制。在java中每个线程需要分配线程内存,用来存储自身的线程变量,在java中每new一个线程,jvm都是向操作系统请求new一个本地线程,此时操作系统会使用剩余的内存空间来为线程分配内存,而不是使用jvm的内存。这样,当操作系统的可用内存越少,则jvm可用创建的新线程也就越少。操作系统对一个进程内的线程数是有限制的,不能无限生成。经验值在3000~5000左右。

Server端-Xms值最好设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存,同时最好让-Xmn值约等于-Xmx的1/3。
增加Heap的大小虽然会降低GC的频率,但也增加了每次GC的时间。GC运行时所有的用户线程将暂停,也就是GC期间Java应用程序不做任何工作。
Heap大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack。

运行时配置内存

当内存申请出现问题时,会出现错误日志:
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000f7d00000, 106430464, 0) failed; error=’Cannot allocate memory’ (errno=12)。
运行时配置最小运行内存、最大运行内存:java -Xms64m -Xmx512m -jar xxx.jar
-Xss:指定每个线程的栈大小。Xss越大,每个线程占用的内存越多, 总线程数就越少;Xss越小,则递归的深度越小,容易出现栈溢出StackOverflowError 问题。适量减少局部变量的声明,可以节省栈帧大小,增加调用深度。

https://stackoverflow.com/questions/31002612/java-hotspottm-64-bit-server-vm-warning

JAVA_OPTS设置 https://blog.csdn.net/kongls08/article/details/8468713

垃圾回收基础
https://segmentfault.com/a/1190000004638653

操作系统

线程原理

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

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

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

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

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

对阻塞及异步的概念理解

阻塞是对线程调用的方法和线程进度而言,指的是线程调用的方法是阻塞的,线程即被阻塞,等待某一步任务处理完成后进程才再往下走。
同步是对任务的执行时间顺序而言,指的是资源被按时间顺序使用。
异步或非阻塞必然有多线程存在。

反向代理

正向代理

代理服务器代理的是客户端,客户端知道目的服务器,也知道代理服务器,服务端不知道真实的客户端。客户端主动请求代理服务器帮助访问目的服务端。
举例:局域网内的VPN代理服务器,正向代理往往处在客户端局域网内。

反向代理

代理服务器代理的是服务端,客户端不知道目的服务器,只知道代理服务器。代理服务器向客户端隐藏了真实的服务端。
反向代理一般是负载均衡(load balance)的实现原理。反向代理往往处在服务端局域网内。

负载均衡的好处:
单个应用宕机不会停止服务,从而升级应用可以逐个升级而不必停止服务。
提高了应用整体的吞吐量。