This commit is contained in:
小柒2012
2018-08-31 15:23:37 +08:00
9 changed files with 744 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
## HashMap的结构
数组的寻址快,但是数据的插入与删除速度不行。 链表的插入与删除速度快,但是寻址速度不行。 那有没有一种两者兼具的数据结构答案肯定是有的那就是hash表。 HashMap 就是根据 数组+链表的方式组成了hash表
![输入图片说明](https://images.gitee.com/uploads/images/2018/0830/173130_ca792251_87650.png "1.png")
## 对于HashMap的一些疑问
![输入图片说明](https://images.gitee.com/uploads/images/2018/0830/173144_4d97d72f_87650.png "2.png")
#### 一、HashMap的resize过程是什么样的
HashMap在put的时候会先检查当前数组的length,如果插入新的值的时候使得length > 0.75f * sizef 为加载因子可以在创建hashMap时指定的话会将数组进行扩容为当前容量的2倍。 扩容之后必定要将原有hashMap 中的值拷贝到新容量的hashMap 里面HashMap 默认的容量为16加载因子为0.75 也就是说当HashMap 中Entry的个数超过 16 * 0.75 = 12时, 会将容量扩充为 16 * 2 = 32然后重新计算元素在数组中的位置这是一个非常耗时的操作所以我们在使用HashMap的时候如果能预先知道Map中元素的大小预设其大小能够提升其性能。 resize代码
```
//HashMap数组扩容
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
//如果当前的数组长度已经达到最大值,则不在进行调整
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
//根据传入参数的长度定义新的数组
Entry[] newTable = new Entry[newCapacity];
//按照新的规则,将旧数组中的元素转移到新数组中
transfer(newTable);
table = newTable;
//更新临界值
threshold = (int)(newCapacity * loadFactor);
}
//旧数组中元素往新数组中迁移
void transfer(Entry[] newTable) {
//旧数组
Entry[] src = table;
//新数组长度
int newCapacity = newTable.length;
//遍历旧数组
for (int j = 0; j < src.length; j++) {
Entry e = src[j];
if (e != null) {
src[j] = null;
do {
Entry next = e.next;
int i = indexFor(e.hash, newCapacity);//放在新数组中的index位置
e.next = newTable[i];//实现链表结构,新加入的放在链头,之前的的数据放在链尾
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
```
这是1.7中的代码1.8中引入了红黑树的概念,代码会相对复杂一些。
#### 二、HashMap在扩容的时候为什么容量都是原来的2倍即容量为2^n
HashMap 在计算数组中key的位置时使用的算法为
/* * Returns index for hash code h. */
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : “length must be a non-zero power of 2”; return h & (length-1); }
即对key的hashcode 与当前数组容量 -1 进行与操作 我们假设有一个容量为分别为15 和 16 的hashMap 有两个key的hashcode 分别为4和5进行indexFor操作之后
H & (length -1) hash & table.length-1 4 & (15 - 1) 0100 & 1110 = 0100 5 & 15 -1 0101 & 1110 = 0100
4 & (16 - 1) 0100 & 1111 = 0100 5 & 16 -1 0101 & 1111 = 0101
我们能够看到在容量为16时进行indexFor操作之后获得相同结果的几率要比容量为15时的几率要小这样能够减少出现hash冲突的几率从而提高查询效率。2 ^ n是一个非常神奇的数字。
#### 三、put时出现相同的hashcode会怎样
hashMap 里面存储的Entry对象是由数组和链表组成的当key的hashcode相同时数组上这个位置存储的结构就是链表这时会将新的值插入链表的表头。进行取值的时候会先获取到链表再对链表进行遍历通过key.equals方法获取到值。hashcode相同不代表对象相同不要混淆hashcode和equals方法 所以声明作final的对象并且采用合适的equals()和hashCode()方法的话将会减少碰撞的发生提高效率。不可变性使得能够缓存不同键的hashcode这将提高整个获取对象的速度使用StringInterger这样的wrapper类作为键是非常好的选择。
#### 四、什么是循环链表?
HashMap在遇到多线程的操作中如果需要重新调整HashMap的大小时多个线程会同时尝试去调整HashMap的大小这时处在同一位置的链表的元素的位置会反过来以为移动到新的bucket的时候HashMap不会将新的元素放到尾部为了避免尾部遍历这时可能会出现A -> B -> A的情况从而出现死循环这便是HashMap中的循环链表。 所以HashMap 是不适合用在多线程的情况下的可以考虑尝试使用HashTable 或是 ConcurrentHashMap
#### 五、如何正确使用HashMap提高性能
在设置HashMap的时候指定其容量的大小减少其resize的过程。
#### 六、JDK1.8对HashMap进行了哪些优化
jdk1.8在对hash冲突的key时如果此bucket位置上的元素数量在10以下时还是和原来一样使用链表来进行存储这时寻址的时间复杂度为O(n),当元素数量超过10时使用红黑树进行代替这时寻址的时间复杂度为O(n)
#### 七、HashMap 与 HashTable、ConcurrentHashMap的区别
1.HashTable的方法是同步的在方法的前面都有synchronized来同步HashMap未经同步所以在多线程场合要手动同步
2.HashTable不允许null值(key和value都不可以) ,HashMap允许null值(key和value都可以)。
3.HashTable有一个contains(Object value)功能和containsValue(Object value)功能一样。
4.HashTable使用Enumeration进行遍历HashMap使用Iterator进行遍历。
5.HashTable中hash数组默认大小是11增加的方式是 old*2+1。HashMap中hash数组的默认大小是16而且一定是2的指数。
6.哈希值的使用不同HashTable直接使用对象的hashCode而HashMap重新计算hash值用与代替求
7.ConcurrentHashMap也是一种线程安全的集合类他和HashTable也是有区别的主要区别就是加锁的粒度以及如何加锁ConcurrentHashMap的加锁粒度要比HashTable更细一点。将数据分成一段一段的存储然后给每一段数据配一把锁当一个线程占用锁访问其中一个段数据的时候其他段的数据也能被其他线程访问。

View File

@@ -1,5 +1,7 @@
## 注意事项
不要听信你看到的关于优化的“绝对真理”,包括本文所讨论的内容,而应该是在实际的业务场景下通过测试来验证你关于执行计划以及响应时间的假设。
- 单条查询最后添加 LIMIT 1停止全表扫描。
- 对于char(4) 或者vachar(4),无论是中文还是英文都是存储四个字符,注意是字符而不是字节。

117
数据库/MySql_lock.md Normal file
View File

@@ -0,0 +1,117 @@
## 数据库锁
### 概述
相对其他数据库而言MySQL的锁机制比较简单其最显著的特点是不同的存储引擎支持不同的锁机制。比如MyISAM和MEMORY存储引擎采用的是表级锁table-level lockingInnoDB存储引擎既支持行级锁 row-level locking也支持表级锁但默认情况下是采用行级锁。
MySQL主要的两种锁的特性可大致归纳如下:
- 表级锁: 开销小,加锁快;不会出现死锁(因为MyISAM会一次性获得SQL所需的全部锁);锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 行级锁: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
考虑上述特点表级锁使用与并发性不高以查询为主少量更新的应用比如小型的web应用而行级锁适用于高并发环境下对事务完整性要求较高的系统如在线事务处理系统。
### MyISAM锁细述
(1). 锁模式
MySQL的表级锁有两种模式 表共享读锁Table Read Lock和表独占写锁Table Write Lock
(2). 如何加锁
当MyISAM在执行查询语句时会自动给涉及到表加读锁在执行更新操作时会加写锁。当然用户也可以用LOCK TABLE 去显式的加锁。显式的加锁一般是应用于:需要在一个时间点实现多个表的一致性读取,不然的话,可能读第一个表时,其他表由于还没进行读操作,没有自动加锁,可能数据会发生改变。并且显示加锁后只能访问加锁的表,不能访问其他表。
(3). 并发插入
MyISAM存储引擎有个系统变量 concurrent_insert专门用来控制并发插入的行为可以取 0 1 2。
0表示不允许并发插入1表示表中间没有删除的行时可以在表末尾插入2表示总是可以插入。
一般如果对并发要求比较高的情况下可以设置为2总是可以插入然后定期在数据库空闲时间对表进行optimize。
(4). 锁的调度
需要注意的是其中读操作不会阻塞其他用户对同一表的读请求但会阻塞对同一表的写请求并且当写锁和读锁同时被申请时优先获得写锁这也这正是表级锁发生锁冲突概率最高的原因因为写锁可能会一直阻塞读锁所以不适合有大量写操作的环境下工作。这一问题可以通过设置low-priority-updates这一启动参数来降低写的优先级。
虽然写锁优先于读锁获取但是长时间的查询操作也可能会让写操作饿死所以尽量避免一条SQL语句执行所有的查询应该进行必要的分解。
### InnoDB锁细述
由于InnoDB支持事务并默认是使用行级锁所以InnoDB的锁问题和MyISAM锁问题还是有蛮大差别的。
(1). 锁模式
共享锁(S)和排他锁(X)分别类似于MyISAM的读锁和写锁。对于 UPDATE、 DELETE 和 INSERT 语句InnoDB会自动给涉及数据集加排他锁X);对于普通 SELECT 语句InnoDB不会加任何锁。
(2). 如何加锁
可以显式的加锁用lock in share mode 显式的加共享锁,用 for update 显式的加排他锁。
需要注意的是如果线程A加了共享锁后线程B对同一个表加了共享锁那么两个线程需要进行更新操作时会产生死锁。所以进行更新操作时最好加排他锁。
(3). InnoDB行锁的实现方式——索引加锁
这一点与Oracle不同所以这也意味着(重要)1. 只有通过索引条件检索数据时InnoDB才会使用行级锁否则会使用表级锁。 2. 即使是访问不同行的记录,如果使用的是相同的索引键,会发生锁冲突。 3. 如果数据表建有多个索引时,可以通过不同的索引锁定不同的行。
(4). 间隙锁
InnoDB支持事务为了满足隔离级别的要求InnoDB有个间隙锁当使用范围查找时InnoDB会给满足key范围要求但实际并不存在的记录加锁。例如select * from user where id > 100 for updata 会给ID>100的记录加排他锁满足这个范围但不存在的记录会加间隙锁这样可以避免幻读避免读取的时候插入满足条件的记录。
(5). 隔离级别与锁
一般来说,隔离级别越高,加锁就越严格。这样,产生锁冲突的概率就越大,一般实际应用中,通过优化应用逻辑,选用 可提交读 级别就够了。对于一些确实需要更高隔离级别的事务再通过set session transaction isolation level+"级别" 来动态改变满足需求。
### 死锁
MyISAM是没有死锁问题的因为他会一次性获得所有的锁。InnoDB发生死锁后一般能自动检测到并使一个事务释放锁并回退另一个事务获得锁继续完成事务。
在应用中,可以通过如下方式来尽可能的避免死锁:
(1) 如果不同的程序会并发的存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会。
(2) 在程序以批量方式处理数据时,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大的降低出现死锁的可能。
### 案例
#### 秒杀一
```
## 查询库存,由于是主键查询使用到了索引,所以是行级锁
SELECT number FROM seckill WHERE seckill_id=? FOR UPDATE
## 如果库存大于秒杀数则更新UPDATE 操作也是行级锁
UPDATE seckill SET number=number-1 WHERE seckill_id=?
```
#### 秒杀二
```
//直接更新数据如果count为1秒杀成功否则失败
UPDATE seckill SET number=number-1 WHERE seckill_id=? AND number>0
```
#### 秒杀三
```
//获取商品版本号以及剩余数量
SELECT version,number FROM seckill WHERE seckill_id=?
//判断剩余数量是否充足并更新
UPDATE seckill SET number=number-?,version=version+1 WHERE seckill_id=? AND version = ?
//如果更新数量等于1秒杀成功否则失败
```
Mysql innodb虽是锁行的但是如果没有索引或者索引唯一性不是特别强那就要锁表了。
加锁对并发访问的影响体现在锁的粒度上可见行锁粒度最小并发访问最好页锁粒度最大表锁介于2者之间。
锁有两种:悲观锁和乐观锁。悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好。
与悲观锁相反,乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。
## 参考
https://www.cnblogs.com/zhanht/p/5431273.html
https://www.cnblogs.com/liujiacai/p/7605612.html
https://www.cnblogs.com/claireyuancy/p/7258314.html

View File

@@ -0,0 +1,38 @@
## 事务的基本要素ACID
- 原子性Atomicity事务开始后所有操作要么全部做完要么全部不做不可能停滞在中间环节。事务执行过程中出错会回滚到事务开始前的状态所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体就像化学中学过的原子是物质构成的基本单位。
- 一致性Consistency事务开始前和结束后数据库的完整性约束没有被破坏 。比如A向B转账不可能A扣了钱B却没收到。
- 隔离性Isolation同一时间只允许一个事务请求同一数据不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱在A取钱的过程结束前B不能向这张卡转账。
- 持久性Durability事务完成后事务对数据库的所有更新将被保存到数据库不能回滚。
## 事务的并发问题
- 脏读事务A读取了事务B更新的数据然后B回滚操作那么A读取到的数据是脏数据
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中对数据作了更新并提交导致事务A多次读取同一数据时结果 不一致。
- 幻读系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级但是系统管理员B就在这个时候插入了一条具体分数的记录当系统管理员A改结束后发现还有一条记录没有改过来就好像发生了幻觉一样这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
## MySQL事务隔离级别
| 事务隔离级别 | 脏读 | 不可重复读 | 幻读|
| ------------ | ------------ |------------ |------------ |
| 读未提交read-uncommitted | 是 | 是 |是 |
| 不可重复读read-committed | 否 | 是 |是 |
| 可重复读repeatable-read | 否 | 否 | 是 |
| 串行化serializable | 否 | 否 | 否|
mysql默认的事务隔离级别为repeatable-read
```
SELECT @@tx_isolation
```

View File

@@ -0,0 +1,126 @@
```
#运行用户
user nobody;
#启动进程,通常设置成和cpu的数量相等
worker_processes 1;
#全局错误日志及PID文件
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
#工作模式及连接数上限
events {
#epoll是多路复用IO(I/O Multiplexing)中的一种方式,
#仅用于linux2.6以上内核,可以大大提高nginx的性能
use epoll;
#单个后台worker process进程的最大并发链接数
worker_connections 1024;
# 并发总数是 worker_processes 和 worker_connections 的乘积
# 即 max_clients = worker_processes * worker_connections
# 在设置了反向代理的情况下max_clients = worker_processes * worker_connections / 4 为什么
# 为什么上面反向代理要除以4应该说是一个经验值
# 根据以上条件正常情况下的Nginx Server可以应付的最大连接数为4 * 8000 = 32000
# worker_connections 值的设置跟物理内存大小有关
# 因为并发受IO约束max_clients的值须小于系统可以打开的最大文件数
# 而系统可以打开的最大文件数和内存大小成正比一般1GB内存的机器上可以打开的文件数大约是10万左右
# 我们来看看360M内存的VPS可以打开的文件句柄数是多少
# $ cat /proc/sys/fs/file-max
# 输出 34336
# 32000 < 34336即并发连接总数小于系统可以打开的文件句柄总数这样就在操作系统可以承受的范围之内
# 所以worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
# 使得并发总数小于操作系统可以打开的最大文件数目
# 其实质也就是根据主机的物理CPU和内存进行配置
# 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。
# ulimit -SHn 65535
}
http {
#设定mime类型,类型由mime.type文件定义
include mime.types;
default_type application/octet-stream;
#设定日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
#sendfile 指令指定 nginx 是否调用 sendfile 函数zero copy 方式)来输出文件,
#对于普通应用,必须设为 on,
#如果用来进行下载等应用磁盘IO重负载应用可设置为 off
#以平衡磁盘与网络I/O处理速度降低系统的uptime.
sendfile on;
#tcp_nopush on;
#连接超时时间
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
#开启gzip压缩
gzip on;
gzip_disable "MSIE [1-6].";
#设定请求缓冲
client_header_buffer_size 128k;
large_client_header_buffers 4 128k;
#设定虚拟主机配置
server {
#侦听80端口
listen 80;
#定义使用 www.nginx.cn访问
server_name www.nginx.cn;
#定义服务器的默认网站根目录位置
root html;
#设定本虚拟主机的访问日志
access_log logs/nginx.access.log main;
#默认请求
location / {
#定义首页索引文件的名称
index index.php index.html index.htm;
}
# 定义错误提示页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
#静态文件nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
#过期30天静态文件不怎么更新过期可以设大一点
#如果频繁更新,则可以设置得小一点。
expires 30d;
}
#PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
#禁止访问 .htxxx 文件
location ~ /.ht {
deny all;
}
}
}
```

View File

@@ -0,0 +1,137 @@
## 运行模式
Tomcat Connector三种运行模式BIO, NIO, APR的比较和优化。
```
org.apache.coyote.http11.Http11ProtocolBIO
org.apache.coyote.http11.Http11NioProtocolNIO
org.apache.coyote.http11.Http11Nio2ProtocolNIO2
org.apache.coyote.http11.Http11AprProtocolAPR
```
### BIO
一个线程处理一个请求。缺点并发量高时线程数较多浪费资源。Tomcat7或以下在Linux系统中默认使用这种方式。
### NIO
利用Java的异步IO处理可以通过少量的线程处理大量的请求。Tomcat8在Linux系统中默认使用这种方式。Tomcat7必须修改Connector配置来启动
```xml
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="8443"/>
```
Tomcat8以后NIO2模式
```xml
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000"
redirectPort="8443"/>
```
### APR
即Apache Portable Runtime从操作系统层面解决io阻塞问题。Tomcat7或Tomcat8在Win7或以上的系统中启动默认使用这种方式。Linux如果安装了apr和nativeTomcat直接启动就支持apr。
## 连接池
默认值:
```xml
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
```
修改为:
```xml
<Executor
name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="500"
minSpareThreads="100"
prestartminSpareThreads = "true"
maxQueueSize = "100"
/>
```
参数解释:
- maxThreads最大并发数默认设置 200一般建议在 500 ~ 800根据硬件设施和业务来判断
- minSpareThreadsTomcat 初始化时创建的线程数,默认设置 25
- prestartminSpareThreads在 Tomcat 初始化的时候就初始化 minSpareThreads 的参数值,如果不等于 trueminSpareThreads 的值就没啥效果了
- maxQueueSize最大的等待队列数超过则拒绝请求
默认的链接参数配置:
```xml
<Connector
port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
/>
```
修改为:
```xml
<Connector executor="tomcatThreadPool"
port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000"
redirectPort="8443"/>
```
参数解释:
- protocolTomcat 8 设置 nio2 更好org.apache.coyote.http11.Http11Nio2Protocol
- protocolTomcat 6、7 设置 nio 更好org.apache.coyote.http11.Http11NioProtocol
- enableLookups禁用DNS查询
- acceptCount指定当所有可以使用的处理请求的线程数都被使用时可以放到处理队列中的请求数超过这个数的请求将不予处理默认设置 100
- maxPostSize以 FORM URL 参数方式的 POST 提交方式,限制提交最大的大小,默认是 2097152(2兆)它使用的单位是字节。10485760 为 10M。如果要禁用限制则可以设置为 -1
- acceptorThreadCount用于接收连接的线程的数量默认值是1。一般这个指需要改动的时候是因为该服务器是一个多核CPU如果是多核 CPU 一般配置为 2
## 端口配置
Tomcat服务器需配置三个端口才能启动安装时默认启用了这三个端口当要运行多个tomcat服务时需要修改这三个端口。
```xml
<!-- 端口-1即可标识随机 -->
<Server port="-1" shutdown="SHUTDOWN">
```
```xml
<!-- 访问端口,必须配置 -->
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
connectionTimeout="20000"
redirectPort="8443"/>
```
```xml
<!-- 配置Apache使用如果使用Nginx代理注释掉即可 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
```
## JVM 优化
Java 的内存模型分为:
- Young年轻代易被 GC。Young 区被划分为三部分Eden 区和两个大小严格相同的 Survivor 区,其中 Survivor 区间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在 Young 区间变满的时候minor GC 就会将存活的对象移到空闲的Survivor 区间中,根据 JVM 的策略,在经过几次垃圾收集后,任然存活于 Survivor 的对象将被移动到 Tenured 区间。
- Tenured终身代。Tenured 区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在 Young 复制转移一定的次数以后,对象就会被转移到 Tenured 区,一般如果系统中用了 application 级别的缓存,缓存中的对象往往会被转移到这一区间。
- Perm永久代。主要保存 class,method,filed 对象,这部门的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到 java.lang.OutOfMemoryError : PermGen space 的错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的 class 没有被卸载掉,这样就造成了大量的 class 对象保存在了 perm 中,这种情况下,一般重新启动应用服务器可以解决问题。
Linux 修改 /tomcat/bin/catalina.sh 文件,把下面信息添加到文件第一行。
机子内存如果是 8G一般 PermSize 配置是主要保证系统能稳定起来就行:
```
JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms6144m -Xmx6144m -XX:NewSize=1024m -XX:MaxNewSize=2048m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:+DisableExplicitGC"
```
参数说明:
```
-Dfile.encoding默认文件编码
-server表示这是应用于服务器的配置JVM 内部会有特殊处理的
-Xmx1024m设置JVM最大可用内存为1024MB
-Xms1024m设置JVM最小内存为1024m。此值可以设置与-Xmx相同以避免每次垃圾回收完成后JVM重新分配内存。
-XX:NewSize设置年轻代大小
-XX:MaxNewSize设置最大的年轻代大小
-XX:PermSize设置永久代大小
-XX:MaxPermSize设置最大永久代大小
-XX:NewRatio=4设置年轻代包括 Eden 和两个 Survivor 区)与终身代的比值(除去永久代)。设置为 4则年轻代与终身代所占比值为 14年轻代占整个堆栈的 1/5
-XX:MaxTenuringThreshold=10设置垃圾最大年龄默认为15。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
-XX:+DisableExplicitGC这个将会忽略手动调用 GC 的代码使得 System.gc() 的调用就会变成一个空调用,完全不会触发任何 GC
```

74
架构之路/Dubbo.md Normal file
View File

@@ -0,0 +1,74 @@
### 【什么是 dubbo】
Dubbo 是阿里巴巴开发用来用来治理服务中间件,资源调度和治理中心的管理工具。
### 【ZooKeeper 节点类型】
ZooKeeper 节点是有生命周期的,这取决于节点的类型,在 ZooKeeper 中,节点类型可以分为:
- 持久节点PERSISTENT
是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点
- 临时节点EPHEMERAL
临时节点的生命周期和客户端会话绑定。如果客户端会话失效,那么这个节点就会自动被清除掉。在临时节点下面不能创建子节点。
- 持久顺序节点PERSISTENT_SEQUENTIAL
在持久节点的基础上在ZK中每个父节点会为他的第一级子节点维护一份时序会记录每个子节点创建的先后顺序
- 临时顺序节点EPHEMERAL_SEQUENTIAL
可以用来实现分布式锁【[从构建分布式秒杀系统聊聊分布式锁](https://blog.52itstyle.com/archives/3202/ "从构建分布式秒杀系统聊聊分布式锁")】
### 【dubbo节点角色说明】
- Provider: 暴露服务的服务提供方service 服务层)。
- Consumer: 调用远程服务的服务消费方(web 表现层)。
- Registry: 服务注册与发现的注册中心zookeeper
- Monitor: 统计服务的调用次调和调用时间的监控中心。
- Container: 服务运行容器(tomcat 容器spring 容器)。
### 【dubbo的注册原理】zookeeper流程
- 服务提供者启动时向/dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址。
- 服务消费者启动时订阅/dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。
- 并向/dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址。
- 监控中心启动时订阅/dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。
### 【注册中心包括】
- Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。
- Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用
- 基于 Redis 实现的注册中心
- Simple 注册中心本身就是一个普通的 Dubbo 服务,可以减少第三方依赖,使整体通讯方式一致。
### 【dubbo,zookeeper流程,从生产者到消费者】
- 生产者和消费者都要进行dubbo的配置 ,都需要注册zookeeper主机地址,
- 生产者要配置dubbo使用的协议(默认dubbo)和端口号用来暴露服务,
- 生产者定义接口和实现类,并在配置文件中进行注册服务,
- 生产者启动时会自动把注册的接口的信息转化为一个url,
- 并通过hessian二进制序列化存储到zookeeper的节点中
- 消费者在配置文件中引入要使用的服务接口,
- 消费者启动时会从zookeeper中获取与引用的接口匹配的url,
- 并把自己的信息留在zookeeper中
- 服务者和消费者在zookeeper中的信息都会被监控中心monitor获取到,
- 可以通过monitor服务对zookeeper中的内容进行管理
### 【服务的配置】
```xml
<!--【服务者】 给服务起一个名称,唯一标识,用来监控服务器调用关系,调用次数 -->
<dubbo:application name="provider" />
<!-- 使用dubbo官方推荐注册中心模式注册对象 -->
<dubbo:registry address="zookeeper://192.168.1.180:2181" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 发布服务:itemService 注册对象,通过接口来注册对象 -->
<!-- 和本地bean一样实现服务 -->
<bean id="xxx.xxx.xxx.XxxImpl" class="xxx.xxx.xxx.XxxImpl" />
<dubbo:service interface="xxx.xxx.xxx.Ixxx" ref="xxxImpl" />
<!-- 【消费者】给服务起一个名称,唯一标识,用来监控服务器调用关系,调用次数 -->
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="consumer" />
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry address="zookeeper://192.168.17.129:2181" />
<!-- 生成远程服务代理可以和本地bean一样使用itemService -->
<dubbo:reference id="xxx" interface="xxx.xxx.xxx.Ixxx" timeout="1000000" retries="2"/>
```
### 文档
http://dubbo.apache.org/zh-cn/docs/dev/design.html

View File

@@ -0,0 +1,72 @@
#### 1、默认使用的是什么通信框架还有别的选择吗?
默认也推荐使用netty框架还有mina。
#### 2、服务调用是阻塞的吗
默认是阻塞的,可以异步调用,没有返回值的可以这么做。
#### 3、一般使用什么注册中心还有别的选择吗
推荐使用zookeeper注册中心还有redis等不推荐。
#### 4、默认使用什么序列化框架你知道的还有哪些
默认使用Hessian序列化还有Duddo、FastJson、Java自带序列化。
#### 5、服务提供者能实现失效踢出是什么原理
服务失效踢出基于zookeeper的临时节点原理。
#### 6、服务上线怎么不影响旧版本
采用多版本开发,不影响旧版本。
#### 7、如何解决服务调用链过长的问题
可以结合zipkin实现分布式服务追踪。
#### 8、说说核心的配置有哪些
核心配置有
```
dubbo:service/
dubbo:reference/
dubbo:protocol/
dubbo:registry/
dubbo:application/
dubbo:provider/
dubbo:consumer/
dubbo:method/
```
#### 9、dubbo推荐用什么协议
默认使用dubbo协议。
#### 10、同一个服务多个注册的情况下可以直连某一个服务吗
可以直连修改配置即可也可以通过telnet直接某个服务。
#### 11、Dubbo集群容错怎么做
读操作建议使用Failover失败自动切换默认重试两次其他服务器。写操作建议使用Failfast快速失败发一次调用失败就立即报错。
#### 12、dubbo和dubbox之间的区别
dubbox是当当网基于dubbo上做了一些扩展如加了服务可restful调用更新了开源组件等。
#### 13、你还了解别的分布式框架吗
别的还有spring的spring cloudfacebook的thrifttwitter的finagle等。

View File

@@ -0,0 +1,43 @@
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150425_9e6a9814_87650.jpeg "1.jpg")
## 阅读源码
阅读、分析源码是程序员最基本的码代码能力也是码农的根本所在,学习经典源码中所用到的经典设计思想及常用设计模式,能够帮你了解大牛是如何
写代码的,从而吸收大牛的代码功力。
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150436_7a6cc8a1_87650.jpeg "2.jpg")
## 分布式架构
阿里巴巴有很多大团队,这种大团队里有很多小团队,到小团队之后,做的业务都不相同,如果想立足成为一线互联网公司中的万能选手,最主流的
分布式架构中有很多知识都是必须要去了解与学习的。并且在阿里面试过程中,面试官会问到实际应用场景的问题:比如微服务化、用户量、并发量
、业务复杂度以及可扩展程度等,这里不多赘述。
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150442_58903af9_87650.jpeg "3.jpg")
## 微服务架构
微服务是现在互联网架构技术中最火热的话题之一,作为一名开发者,一名有技术梦想的程序员微服务架构是现在必须要去了解的主流技术。
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150448_60e9803b_87650.jpeg "4.jpg")
## 并发编程
并发编程几乎是所有互联网公司面试必问问题并发编程是Java程序员最重要的技能之一也是最难掌握的一种技能。它要求编程者对计算机最底层
的运作原理有深刻的理解,同时要求编程者逻辑清晰、思维缜密,这样才能写出高效、安全、可靠的多线程并发程序。
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150454_5ee83c26_87650.jpeg "5.jpg")
## 性能优化
性能一直是让程序员比较头疼的问题。当系统架构变得复杂而庞大之后,性能方面就会下降,特别是阿里巴巴这样的一线互联网公司最为注重,因此想
进入阿里,性能优化一定是要去深入学习与理解的一环。
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150500_4f65a1ba_87650.jpeg "6.jpg")
## Java开发工具
一名开发人员必须有适合自己的兵器,也就是工欲善其事必先利其器,不管是小白,还是资深开发,都需要先选择好的工具。
![输入图片说明](https://images.gitee.com/uploads/images/2018/0829/150506_2fa6e158_87650.jpeg "7.jpg")
更多IT架构师技术知识图谱http://file.52itstyle.com