mirror of
https://gitee.com/52itstyle/spring-boot-seckill.git
synced 2025-12-30 10:22:26 +00:00
Merge branch 'master' of https://gitee.com/52itstyle/spring-boot-seckill
This commit is contained in:
135
Java Basis/对于HashMap的一些疑问.md
Normal file
135
Java Basis/对于HashMap的一些疑问.md
Normal file
@@ -0,0 +1,135 @@
|
||||
## HashMap的结构
|
||||
数组的寻址快,但是数据的插入与删除速度不行。 链表的插入与删除速度快,但是寻址速度不行。 那有没有一种两者兼具的数据结构,答案肯定是有的,那就是hash表。 HashMap 就是根据 数组+链表的方式组成了hash表:
|
||||
|
||||

|
||||
|
||||
## 对于HashMap的一些疑问
|
||||
|
||||

|
||||
|
||||
#### 一、HashMap的resize过程是什么样的?
|
||||
|
||||
HashMap在put的时候会先检查当前数组的length,如果插入新的值的时候使得length > 0.75f * size(f 为加载因子,可以在创建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,这将提高整个获取对象的速度,使用String,Interger这样的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更细一点。将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
|
||||
@@ -1,5 +1,7 @@
|
||||
## 注意事项
|
||||
|
||||
不要听信你看到的关于优化的“绝对真理”,包括本文所讨论的内容,而应该是在实际的业务场景下通过测试来验证你关于执行计划以及响应时间的假设。
|
||||
|
||||
- 单条查询最后添加 LIMIT 1,停止全表扫描。
|
||||
|
||||
- 对于char(4) 或者vachar(4),无论是中文还是英文都是存储四个字符,注意是字符而不是字节。
|
||||
|
||||
117
数据库/MySql_lock.md
Normal file
117
数据库/MySql_lock.md
Normal file
@@ -0,0 +1,117 @@
|
||||
## 数据库锁
|
||||
|
||||
### 概述
|
||||
|
||||
相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);InnoDB存储引擎既支持行级锁( 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
|
||||
|
||||
38
数据库/MySql_trasaction.md
Normal file
38
数据库/MySql_trasaction.md
Normal 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
|
||||
```
|
||||
|
||||
126
服务器/Nginx参数配置说明.md
Normal file
126
服务器/Nginx参数配置说明.md
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
137
服务器/Tomcat最佳实践.md
Normal file
137
服务器/Tomcat最佳实践.md
Normal file
@@ -0,0 +1,137 @@
|
||||
## 运行模式
|
||||
Tomcat Connector三种运行模式(BIO, NIO, APR)的比较和优化。
|
||||
|
||||
```
|
||||
org.apache.coyote.http11.Http11Protocol:BIO
|
||||
org.apache.coyote.http11.Http11NioProtocol:NIO
|
||||
org.apache.coyote.http11.Http11Nio2Protocol:NIO2
|
||||
org.apache.coyote.http11.Http11AprProtocol:APR
|
||||
```
|
||||
### 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和native,Tomcat直接启动就支持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,根据硬件设施和业务来判断
|
||||
- minSpareThreads,Tomcat 初始化时创建的线程数,默认设置 25
|
||||
- prestartminSpareThreads,在 Tomcat 初始化的时候就初始化 minSpareThreads 的参数值,如果不等于 true,minSpareThreads 的值就没啥效果了
|
||||
- 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"/>
|
||||
```
|
||||
|
||||
参数解释:
|
||||
- protocol,Tomcat 8 设置 nio2 更好:org.apache.coyote.http11.Http11Nio2Protocol
|
||||
- protocol,Tomcat 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,则年轻代与终身代所占比值为 1:4,年轻代占整个堆栈的 1/5
|
||||
-XX:MaxTenuringThreshold=10:设置垃圾最大年龄,默认为:15。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
|
||||
-XX:+DisableExplicitGC:这个将会忽略手动调用 GC 的代码使得 System.gc() 的调用就会变成一个空调用,完全不会触发任何 GC
|
||||
```
|
||||
74
架构之路/Dubbo.md
Normal file
74
架构之路/Dubbo.md
Normal 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
|
||||
72
架构之路/Dubbo知识精华.md
Normal file
72
架构之路/Dubbo知识精华.md
Normal 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 cloud,facebook的thrift,twitter的finagle等。
|
||||
43
架构之路/知识图谱.md
Normal file
43
架构之路/知识图谱.md
Normal file
@@ -0,0 +1,43 @@
|
||||

|
||||
## 阅读源码
|
||||
|
||||
阅读、分析源码是程序员最基本的码代码能力也是码农的根本所在,学习经典源码中所用到的经典设计思想及常用设计模式,能够帮你了解大牛是如何
|
||||
写代码的,从而吸收大牛的代码功力。
|
||||
|
||||

|
||||
|
||||
## 分布式架构
|
||||
|
||||
阿里巴巴有很多大团队,这种大团队里有很多小团队,到小团队之后,做的业务都不相同,如果想立足成为一线互联网公司中的万能选手,最主流的
|
||||
分布式架构中有很多知识都是必须要去了解与学习的。并且在阿里面试过程中,面试官会问到实际应用场景的问题:比如微服务化、用户量、并发量
|
||||
、业务复杂度以及可扩展程度等,这里不多赘述。
|
||||
|
||||

|
||||
|
||||
## 微服务架构
|
||||
|
||||
微服务是现在互联网架构技术中最火热的话题之一,作为一名开发者,一名有技术梦想的程序员微服务架构是现在必须要去了解的主流技术。
|
||||
|
||||

|
||||
|
||||
## 并发编程
|
||||
|
||||
并发编程几乎是所有互联网公司面试必问问题,并发编程是Java程序员最重要的技能之一,也是最难掌握的一种技能。它要求编程者对计算机最底层
|
||||
的运作原理有深刻的理解,同时要求编程者逻辑清晰、思维缜密,这样才能写出高效、安全、可靠的多线程并发程序。
|
||||
|
||||

|
||||
|
||||
## 性能优化
|
||||
|
||||
性能一直是让程序员比较头疼的问题。当系统架构变得复杂而庞大之后,性能方面就会下降,特别是阿里巴巴这样的一线互联网公司最为注重,因此想
|
||||
进入阿里,性能优化一定是要去深入学习与理解的一环。
|
||||
|
||||

|
||||
|
||||
## Java开发工具
|
||||
|
||||
一名开发人员必须有适合自己的兵器,也就是工欲善其事必先利其器,不管是小白,还是资深开发,都需要先选择好的工具。
|
||||
|
||||

|
||||
|
||||
更多IT架构师技术知识图谱:http://file.52itstyle.com
|
||||
Reference in New Issue
Block a user