mycat 是数据库中间件,提供高可用性数据分片集群。
水平切分: 把同一个表拆到不同的数据库中
垂直切分 :把不同的表拆到不同的数据库中
Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL 语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
能满足数据库数据大量存储;提高了查询性能 ; 实现读写分离,分库分表
数据库是对底层存储文件的抽象,而Mycat是对数据库的抽象。
分库:从单个数据库拆分成多个数据库的过程,将数据散落在多个数据库中。
分表:从单张表拆分成多张表的过程,将数据散落在多张表内。
1.需求目标:
数据是否永久存储?
表日增数据量是多少?
分表数据是否需要读写负载?
2.如何分?
- 关键字取模的方式
- 垂直和水平
MongoDB是一个基于分布式文件存储的数据库,MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
- 网站实时数据处理。它非常适合实时的插入、更新与查询.
- 用于对象及JSON 数据的存储和查询.
好处: 高性能、易部署、易使用,存储数据非常方便
- 文档 : 文档是 MongoDB 中数据的基本单位,类似于关系数据库中的行,多个键及其关联的值有序地放在一起就构成了文档。
- 集合: 就是一组文档,类似于关系数据库中的表。
- 域 数据字段
- 索引
- 主键 mongdb 自动将 _id 字段设置为主键
好处:
1). 隔离作用
2). 无侵入地进行某些业务逻辑的执行
两大类 :
1). 静态代理2). 动态代理 --> JDK , Cglib
无论是静态代理还是动态代理都有基于接口实现 和 基于继承实现两类 , 动态代理典型的如: JDK 动态代理(基于接口实现) , Cglib动态代理 (基于继承)
JDK 代理只能针对有接口的类的接口方法进行动态代理 . 由于接口里面是不能有private 方法的, 所以JDK 无法对private 的方法进行代理
由于Cglib 是基于继承来实现代理 , 所以无法对static , final 类进行代理 , 也无法对private , static 方法进行代理 .
spring动态代理
1). 如果目标类 , 有接口 ----> JDK
2). 如果目标类 , 没有接口 —> Cglib
创建索引
为 user 表中的 name 字段创建索引 ;
create index idx_user_name on user(name);
示例:查看 user 表中的索引信息;
show index from user;
想要删除user表上的索引idx_user_name,可以操作如下:
drop index idx_user_name on user;
1.查看SQL执行频率
通过 show [session|global] status 命令可以提供服务器状态信息。show [session|global] status
可以根据需要加上参数“session”或者“global”来显示 session 级(当前连接)的计结果和 global 级(自数据库上次启动至今)的统计结果。如果不写,默认使用参数是“session” 查看查询 插入 删除等语句执行的次数2.定位低效率执行SQL
可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化
1) id列,用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id()查看
2) user列,显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句
3) host列,显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户
4) db列,显示这个进程目前连接的是哪个数据库
5) command列,显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等
6) time列,显示这个状态持续的时间,单位是秒
7) state列,显示使用当前连接的sql语句的状态,很重要的列。state描述的是语句执行中的某一个状态。一个sql语句,以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成
8) info列,显示这个sql语句,是判断问题语句的一个重要依据3.explain分析执行计划
通过 EXPLAIN或者 DESC命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。explain select * from tb_item where id = 1;
字段 含义 id select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。 select_type 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(子查询中的第一个 SELECT)等 table 输出结果集的表 type 表示表的连接类型,性能由好到差的连接类型为( system —> const -----> eq_ref ------> ref -------> ref_or_null----> index_merge —> index_subquery -----> range -----> index ------> all ) possible_keys 表示查询时,可能使用的索引 key 表示实际使用的索引 key_len 索引字段的长度 rows 扫描行的数量 extra 执行情况的说明和描述
1). 全值匹配 ,对索引中所有列都指定具体值。
该情况下,索引生效,执行效率高。
2). 最左前缀法则
如果索引了多列,要遵守最左前缀法则。指的是查询索引包含最左列,并且不跳过索引中的列。
3). 范围查询右边的列,不能使用索引 。
4). 不要在索引列上进行运算操作, 索引将失效。
5). 字符串不加单引号,造成索引失效。(由于,在查询是,没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效。)
6). 尽量使用覆盖索引,避免select *
尽量使用覆盖索引(只访问索引的查询(索引列完全包含查询列)),减少select * 。
7). 用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
8). 以%开头的Like模糊查询,索引失效。
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
解决方案 :
通过覆盖索引来解决
9). 如果MySQL评估使用索引比全表更慢,则不使用索引。
10). 单列索引和复合索引。
尽量使用复合索引,而少使用单列索引 。
2.SQl语句优化
1.大批量数据插入
主键顺序插入
因为InnoDB类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效的提高导入数据的效率。如果InnoDB表没有主键,那么系统会自动默认创建一个内部列作为主键,所以如果可以给表创建一个主键,将可以利用这点,来提高导入数据的效率。
关闭唯一性校验
在导入数据前执行 SET UNIQUE_CHECKS=0,关闭唯一性校验,在导入结束后执行 SET UNIQUE_CHECKS=1,恢复唯一性校验,可以提高导入的效率。
手动提交事务
如果应用使用自动提交的方式,建议在导入前执行 SET AUTOCOMMIT=0,关闭自动提交,导入结束后再执行 SET AUTOCOMMIT=1,打开自动提交,也可以提高导入的效率。
优化嵌套查询
子查询是可以被更高效的连接(JOIN)替代
连接(Join)查询之所以更有效率一些 ,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上需要两个步骤的查询工作。
优化insert语句
同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,效率比分开执行的单个insert语句快,注意数据有序插入.
什么是索引?
是存储引擎用于快速找到记录的一种数据结构。默认都是使用B+树结构组织(多路搜索树,并不一定是二叉的)的索引. MySQL 使用索引的最左前缀。
MySQL采用b+树原理查询,每次查询复合字段从左到右的属性。最左优先,以最左边的为起点任何连续的索引都能匹配上。
3.4.1 按属性
分类 含义 特点 关键字 主键索引 针对于表中主键创建的索引 默认自动创建, 只能有一个 PRIMARY 唯一索引 避免同一个表中某数据列中的值重复 可以有多个 UNIQUE 常规索引 快速定位特定数据 可以有多个 全文索引 全文索引查找的是文本中的关键词,而不是比较索引中的值 可以有多个 FULLTEXT 3.4.2 按数据存储方式
分类 含义 特点 聚簇 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 必须有,而且只有一个 非聚簇索引 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 可以存在多个
1.聚簇索引与非聚簇索引的区别?
优点:
1.数据访问更快,因为聚簇索引将索引和数据保存在同一个B+树中,因此从聚簇索引中获取数据比非聚簇索引更快.
2.聚簇索引对于主键的排序查找和范围查找速度非常快
缺点:
插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键
更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB表,我们一般定义主键为不可更新。二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
示例 : 为 user 表中的 name 字段创建索引 ;
create index idx_user_name on user(name);
3.5.2 查看索引
语法:
show index from table_name;
示例:查看 user 表中的索引信息;
show index from user;
3.5.3 删除索引
语法 :
DROP INDEX index_name ON tbl_name;
示例 : 想要删除user表上的索引idx_user_name,可以操作如下:
drop index idx_user_name on user;
索引的设计可以遵循一些已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引。
2.为什么会产生页分裂?
这是因为聚簇索引采用的是平衡二叉树算法,而且每个节点都保存了该主键所对应行的数据,假设插入数据的主键是自增长的,那么根据二叉树算法会很快的把该数据添加到某个节点下,而其他的节点不用动;但是如果插入的是不规则的数据,那么每次插入都会改变二叉树之前的数据状态。从而导致了页分裂。
索引结构
索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型的。MySQL目前提供了以下4种索引:
索引结构 描述 B+TREE索引 最常见的索引类型,大部分索引都支持 B+ 树索引; HASH索引 只有Memory引擎支持,底层数据结构是用哈希表实现的, 只有精确匹配索引所有列的查询才有效, 不支持范围查询 R-tree(空间索引) 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少,不做特别介绍 Full-text(全文索引) 全文索引查找的是文本中的关键词,而不是比较索引中的值,类似于Lucene,Solr,ES
特征 :
B+Tree为BTree的变种 ,B+Tree与BTree的区别为:
1). B+Tree的叶子节点保存所有的key信息,依key大小顺序排列。
2). B+Tree叶子节点元素维护了一个单向链表。
3). 所有的非叶子节点都可以看作是key的索引部分。
由于B+Tree只有叶子节点保存key信息,查询任何key都要从root走到叶子。所以B+Tree的查询效率更加稳定。
- B+ 树可以进行范围查询,Hash 索引不能。
- B+ 树支持联合索引的最左侧原则,Hash 索引不支持。
- B+ 树支持 order by 排序,Hash 索引不支持。
- Hash 索引在等值查询上比 B+ 树效率更高。
- B+ 树使用 like 进行模糊查询的时候,like 后面(比如%开头)的话可以起到优化的作用,Hash 索引根本无法进行模糊查询。
MySql索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
InnoDB存储引擎 : InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁和外键,InnoDB是默认的MySQL引擎。
MyISAM存储引擎: MyISAM拥有较高的插入、查询速度快,但不支持事务 ;
Memory 内存存储,不支持事务
InnoDB和MyISAM有什么区别?
- InnoDB支持事物,而MyISAM不支持事物
- InnoDB支持行级锁,而MyISAM支持表级锁774
- InnoDB支持MVCC, 而MyISAM不支持
MVCC ---> 多版本并发控制 提高数据库并发性能,实现事务内存。
- InnoDB支持外键,而MyISAM不支持
MVCC (Multiversion Concurrency Control),即多版本并发控制技术。
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
事务是一系列SQL语句操作,要么全部执行成功,要么全部执行失败.
ACID
A=Atomicity 原子性: 要么全部成功,要么全部失败.不可能只执行一部分操作.
C=Consistency 一致性: 系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态.
I= Isolation 隔离性: 指的是一个事务的修改在最终提交前,对其他事务是不可见的。
D=Durability 持久性: 事务一旦提交,所做的操作都会永久保存数据库中.
read uncommit 读未提交: 可能会读到其他事务未提交的数据,也叫做脏读
read commit 读已提交 : 两次结果不一致, 会出现不可重复读(解决脏读)
repeatable read 可重复读 , mysql (默认级别) 每次读取结果都一样,但是有可能产生幻读.
serializable 串行化, 一个事务执行给每一行加锁,会导致性能问题。
脏读: 一个事务处理过程里读取了另一个未提交的事务中的数据
可重复读: 一个事务在它运行期间,两次查找相同的表,出现了不同的数据
虚读/幻读: 在一个事务中读取到别的事务插入数据,导致数据不一致
A,原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql
C一致性由其他三大特性保证、程序代码要保证业务上的一致性I隔离性由MVCC来保证
D持久性由 内存 + redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,宕机的时候可
以从redo log恢复 redolog的刷盘会在系统空闲时进行
从锁的粒度划分: 行锁, 表锁, 页锁
1.行锁 粒度最小,能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。有可能会出现死锁的情况。
2.表锁 粒度最大,资源开销比行锁少,不会出现死锁的情况,但是发生锁冲突的概率很大。
3.页锁: 介于中间一锁, 表级锁速度快,但冲突多,行级冲突少,但速度慢。
从锁的类别上来讲,有共享锁和排他锁.
共享锁=读锁, 当一个事务为数据加上读锁之后,其他事务只能对该数据加读锁,而不能对数据加写锁,避免出现重复读的问题。
排他锁=写锁 排他锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取。避免了出现脏数据和脏读的问题。
MySQL 遇到过死锁问题吗,你是如何解决的?
(1)查看死锁日志 show engine innodb status;
(2)找出死锁Sql
(3)分析sql加锁情况
(4)模拟死锁案发
(5)分析死锁日志
(6)分析死锁结果
说说数据库的乐观锁和悲观锁是什么以及它们的区别?
(1)悲观锁: 一个事务处理完,其他任何事务都不能对数据进行修改啦,只能等待锁被释放才可以执行。
(2)乐观锁: 它允许多个事务同时对数据进行变动。
实现方式:乐观锁一般会使用版本号机制或CAS算法实现。
满足三大范式:
1.第一范式(确保每列保持原子性) ,第一范式是最基本的范式。数据库表中的所有字段值都是不可分解的原子值.
用户提交带有恶意的数据与SQL语句进行字符串方式的拼接,从而影响了SQL语句的语义,最终产生数据泄露的现象. 通过SQL语句,实现无账号登录,甚至篡改数据库。
- 检查变量数据类型和格式
- 过滤特殊符号
- 绑定变量,使用预编译语句
insert into 插入
create table a1 like a
insert into a1 select * from a; 复制一个表
alter table 添加表字段
- 内连接查询 inner join 指所有查询出的结果都是能够在连接的表中有对应记录的。
- 左外连接查询 left join 以左边的表的数据为基准,去匹配右边的表的数据
- 右外连接查询 right join 以右表的数据去匹配左表,所以左外连接能做到的查询,
左右连接的区别:
左连接,以左表为参照,显示所有数据,右表中没有则以null显示
右连接,以右表为参照显示数据,左表中没有则以null显示
左连接where只影向右表,右连接where只影响左表。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的一站式容器框架
- 独立性强,能独立于各种应用服务器
- 兼容性强,能融合各种框架模块
- 分层结构,模块化,可扩展性更好, 降低耦合
- 开发效率高,易维护,易测试
IOC(核心容器)
IOC:即控制反转。实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点、 @repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的 bean节点,根据全限定类名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创建对象放到map里
这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入
如何实现:
1、配置文件配置包扫描路径
配置文件中指定需要扫描的包路径 定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注入注解、获取配置文件注 解
2、递归包扫描获取.class文件
从配置文件中获取需要扫描的包路径,获取到当前路径下的文件信息及文件夹信息,我们将当前路 径下所有以.class结尾的文件添加到一个Set集合中进行存储
3、反射、确定需要交给IOC管理的类
遍历这个set集合,获取在类上有指定注解的类,并将其交给IOC容器,定义一个安全的Map用来 存储这些对象
4、对需要注入的类进行依赖注入
遍历这个IOC容器,获取到每一个类的实例,判断里面是有有依赖其他的类的实例,然后进行递归 注入
DI --------------------> (实现关系维护的一种技术)
控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器 主动注入。就是由IOC容器在运行期间,动态地将某种依赖关系注入到对 象之中。
依赖注入有如下实现方式:
- 基于接口。实现特定接口以供外部容器注入所依赖类型的对象。
- 基于 set 方法。实现特定属性的 public set 方法,来让外部容器调用传入所依赖类型的对象。
- 基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。
- 项目启动的时候会读取xml节点ref属性 根据id注入
- 基于注解。 @Autowired @resource
AOP ----------------->
AOP:面向切面编程, 将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到具体业务逻辑中去。AOP可以对 对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。底层是动态代理实现.
底层实现:
JDK 原生动态代理和 cglib 动态代理。JDK 原生动态代理是基于接口实现的,
而 cglib 是基于继承当前0类的子类实现的。
AOP怎么用:
前置通知: 某方法调用之前发出通知
后置通知: 某方法完成之前发出通知
返回后通知: 方法调用完,正常退出,发出通知
环绕通知: 通知包裹在被通知的方法都可以做到
使用:
项目中,日志处理和事务处理
返回值统一处理
多数据源切换 (放在配置文件,加载到map中,)
- @Configuration 相当于Ioc容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean,与xml中配置的bean意思一样。
- @Value : 给变量赋了初值
- @Component, @Controller, @Service, @Repository, : 实现bean的注入
- @Autowired:自动根据属性的类型注入
- @Resource: 默认按属性的名称自动注入
- @Scope: 标识 bean 的作用域
- @Bean spring配置的类注解
Spring事务本质是对数据库事务的支持,如果数据库不支持事务(例如MySQL的MyISAM引擎不支持事务),则Spring事务也不会生效。
编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难以维护。
声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。
在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象 作为bean,当在使用这个代理对象的方法时,如果这个方法上存@Transactional注解,那么代理逻 辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果没有 出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚。
可以利用@Transactional注解中的rollbackFor属性进行
配置,默认情况下会对RuntimeException和Error进行回滚。
spring事务隔离级别就是数据库的隔离级别:外加一个默认级别
read uncommitted(未提交读)
read committed(提交读、不可重复读)
repeatable read(可重复读)
serializable(可串行化)
spring事务什么时候会失效
spring事务的原理是AOP失效的根本原因是这个AOP不起作用了
1.发生自调用,类里面使用this调用本类的方法(this通常省略),此时这个this对象不是代理类,而是UserService对象本身!
2.@Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可 以开启 AspectJ 代理模式。
3、数据库不支持事务
4、没有被spring管理
5、异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)
多个事务方法相互调用时,事务如何在这些方法间传播
REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事 务,则加入这个事务
SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务。
NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务
NEVER:不使用事务,如果当前事务存在,则抛出异常
NESTED:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。该 对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)。
prototype:为每一个bean请求提供一个实例。在每次注入时都会创建一个新的对象
request:bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。
session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean 会随之失效。
application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
websocket:bean被定义为在websocket的生命周期中复用一个单例对象。global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器 中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么 这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
- 实例化bean对象(通过构造方法或者工厂方法)
- 设置对象属性(setter等)(依赖注入)
- 如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。(和下面的一条均属于检查Aware接口)
- 如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身
- 将Bean实例传递给Bean的前置处理器的postProcessBeforeInitialization(Object bean, String beanname)方法
- 调用Bean的初始化方法
- 将Bean实例传递给Bean的后置处理器的postProcessAfterInitialization(Object bean, String beanname)方法
- 使用Bean
- 容器关闭之前,调用Bean的销毁方法
不是
Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。
如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作 用域 把 "singleton"改为’‘protopyte’ 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的 安全了。
如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用 synchronized、lock、CAS等这些实现线程同步的方法了。
开启自动装配,只需要在xml配置文件中定义“autowire”属性。
<bean id="cutomer" class="com.xxx.xxx.Customer" autowire="" />
autowire属性有五种装配的方式:
1.byName-根据bean的属性名称进行自动装配
Cutomer的属性名称是person,Spring会将bean id为person的bean通过setter方法进行自动装 配。
2.byType-根据bean的类型进行自动装配。
Cutomer的属性person的类型为Person,Spirng会将Person类型通过setter方法进行自动装配。
3.constructor-类似byType,不过是应用于构造器的参数。如果一个bean与构造器参数的类型形 同,则进行自动装配,否则导致异常。
Cutomer构造函数的参数person的类型为Person,Spirng会将Person类型通过构造方法进行自动装 配。
4.no – 缺省情况下,自动配置是通过“ref”属性手动设定.
手动装配:以value或ref的方式明确指定属性值都是手动装配。 需要通过‘ref’属性来连接bean。
5.@Autowired自动装配bean,可以在字段、setter方法、构造函数上使用。
简单工厂:由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象
工厂方法:实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调 用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个 bean.getOjbect()方法的返回值。
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点
spring对单例的实现: spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没 有从构造器级别去控制单例,这是因为spring管理的是任意的java对象。
动态代理:切面在应用运行的时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建动态的创建一个代理对象。SpringAOP就是以这种方式织入切面的。
织入:把切面应用到目标对象并创建新的代理对象的过程。
springmvc是spring对web框架的一个解决方案,提供了一个总的前端控制器Servlet,用来接收请求, 然后定义了一套路由策略(url到handle的映射)及适配执行handle,将handle结果使用视图解析技术 生成视图展现给前端
1.用户发送请求至前端控制器DispatcherServlet(也叫中央处理器).
2.DispatcherServlet收到请求调用HandlerMappering处理器映射器
3.处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet.
4.DispatcherServlet调用HandlerAdapter处理器适配器。
5.HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6.Controller执行完成返回ModelAndView.
7.HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet.
8.DisPatcherServlet将ModelAndView传给ViewReslover视图解析器。
9.ViewReslover解析后返回具体View.
10.DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11.DispatcherServlet响应用户。
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中 的所有响应请求的方法都是以该地址作为父路径。
@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。
@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
@PathVariable: 获取URL中的动态参数
@Conntroller:控制器的注解,表示是表现层,不能用用别的注解代替
@RestController: 相当于 @ResponseBody + @Controller,表示是表现层.
通过Jackson框架就(序列化)可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 :
(1)加入Jackson.jar
(2)在配置文件中配置json的映射
(3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。
1.它是基于组件技术的。全部的应用对象,无论控制器和视图,还是业务对象之类的都是java组件。并且和Spring提供的其他基础结构紧密集成。
2.不依赖于ServletAPI(目标虽是如此,但是在实现的时候确实是依赖于Servlet的)
3.可以任意使用各种视图技术,而不仅仅局限于JSP。比如:PDF,Excel
4.支持各种请求资源的映射策咯
5.它是易于扩展
Spring Boot 是 Spring 开源组织下的子项目,是 Spring 组件一站式解决方案,主要是简化了使用 Spring 的难度,简省了繁重的配置,提供了各种启动器,使开发者能快速上手。
好处: 快速开发,快速整合,配置简化、内嵌服务容器
- @SpringBootConfiguration**:注解中引入了**@Configuration**,表明SpringBootApplication是一个配置类
- @EnableAutoConfiguration:开启自动配置功能。
-----------@AutoConfigurationPackage
-----------@Import(AutoConfigurationImportSelector.class)其导入的AutoConfigurationImportSelector的selectImports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有META-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件
这个spring.factories文件也是一组一组的key=value的形式,其中key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔;在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
- @ComponentScan:自动扫描
@Value注解
@ConfigurationProperties注解
Environment接口
starter就是定义一个starter的jar包,写一个@Configuration配置类、将这些bean定义在里面,然后在 starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类 开发人员只需要将相应的starter包依赖进应用,进行相应的属性配置(使用默认配置时,不需要配 置),就可以直接进行代码开发,使用对应的功能了,比如mybatis-spring-boot–starter,springboot-starter-redis
单点应用架构—>垂直应用架构---->分布式架构—>SOA架构—>微服务架构
微服务化的核心就是将传统的应用模块,根据业务拆分成一个一个的服务,彻底地解耦,每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,拥有独立的数据库。
你觉得微服务的优缺点有哪些?
好处:
1)服务的独立部署
2)服务的快速启动
3)更加适合敏捷开发
4)代码的复用,可以动态扩容
缺点:
1)分布式部署,调用的复杂性高
2)独立的数据库,分布式事务的挑战
3)测试与运维的难度提高
Spring Cloud是一系列框架的有序集合。Spring Cloud 是一套完整的微服务解决方案,基于 Spring Boot 框架,准确的说,它不是一个框架,而是一个大的容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量。
Eureka:服务治理组件,可以注册服务接口中心和客户端的服务发现机制;(高可用)
Hystrix:容错管理组件,实现断路器模式,服务依赖中出现的延迟和故障提供容错性(断路器),用于隔离访问远程服务、第三方库,防止出现级雪崩问题: 解决: 服务降级 包括线程隔离, 目的: 给客户端较好的体验而不是一堆异常栈。例如在秒杀系统中出现异常返回固定的排队页面。
Ribbon:客户端负载均衡服务调用组件,它有助于控制Http和TCP客户端的行为。为Ribbon配置服务提供者地址列表后.
Feign:基于 Hystrix 和 Ribbon 声明式服务调用组件,声明性的Web服务客户端,Feign也叫伪装,可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。
Zuul(1版本):提供路由,访问过滤(网关),API网关组件,对请求提供路由及过滤功能。
getway: Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。
cloud Config: 云配置中心,把配置文件放到云端统一管理, 然后用版本控制工具维护.
1.SpringBoot可以离开SpringCloud独立使用开发项目, 但是SpringCloud离不开SpringBoot ,属 于依赖的关系 .
2.SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。
3.SpringCloud 是关注全局微服务的协调整治框架, 它将springboot 开发的一个个单体微服务整合并管理起来.
网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的服务。
作用: 统一管理前端向后端的微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等
- gateway 是前端工程到后台服务器之间的一个对内网关, nginx是用户到 前端工程 的网关,对外网关
- gatway 处理并发量有限,Nginx 处理并发高(50000),
- gatway 网关可以鉴权,限流
客户端在首次登陆以后,服务端再次接收http请求的时候,就只认token了,请求只要每次把token带上就行了,服务器端会拦截所有的请求,然后校验token的合法性,合法就放行,不合法就返回401.
负载均衡可以将工作任务分摊到多个处理单元,从而提高网站、应用、数据库或其他服务的性能和可靠性。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。
负载均衡算法:
轮询: 连接被依次轮询分发到各个服务器上;
加权轮询: 根据服务器不同的处理能力,给服务器分配不同的权值,使其能接受相应权值的服务器请求。
随机:将新连接请求随机分发给各个服务器 ;
最少连接:把请求分发给连接数最少的服务器,使均衡更加符合实际情况,负载更均衡 ;
- Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。
- Gateway 提供两种过滤器方式:“pre”和“post”
pre 过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。
post 过滤器,在响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。
- Gateway 还提供了两种类型过滤器
GatewayFilter:局部过滤器,针对单个路由
GlobalFilter :全局过滤器,针对所有路由
Redis 是一个 开源的高性能key-value非关系缓存数据库。可以将数据写入内存中.
好处: 速度快,支持类型多,支持事务,操作原子性, 可以用高速缓存和消息队列代理,
string
1. 单质缓存
2. 分布式锁
3. 计数器
4. 分布式全局唯一id
Hash
1. 频繁改动的对象 优点: 省去序列化带来的内存消耗、避免并发带来的保护问题
2. 结构化数据
list
1. 消息队列
2. 评论列表、点赞列表
set:
1. 无序集合 , 交集,并集,差集的操作
2. 电商商品筛选
3. 唯一性
zset:
1. 查看排行榜
2. 同分数排名问题
1.完全基于内存,非常快速。
2.数据结构简单。
3.采用单线程,避免了不必要的上下文切换和竞争条件,不存在锁问题.
4.使用多路 I/O 复用*模型,非阻塞 IO;
IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程.
RDB(快照)和 AOF(日志)
RDB(快照): Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中.
优点:
1、整个Redis数据库将只包含一个文件 dump.rdb,方便持久化。
2、容灾性好,方便备份。
4.相对于数据集大时,比 AOF 的启动效率更高。
缺点:
1、数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢
失。所以这种方式更适合数据要求不严谨的时候)
AOF(日志) : 将Redis执行的每次写命令记录到单独的日志文件中
优点:
1、数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也 是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据 将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁 盘中。。
3、AOF 机制的 rewrite 模式。定期对AOF文件进行重写,以达到压缩的目的
缺点:
1、AOF 文件比 RDB 文件大,且恢复速度慢。
2、数据集大的时候,比 rdb 启动效率低。
3、运行效率没有RDB高
删除策略就是针对已过期数据的处理策略,
过期策略通常有以下三种:
1.定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。CPU压力很大
2.惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化 地节省CPU资源,却对内存非常不友好。
3.定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其 中已过期的key。(达到最优的平衡效果)
Redis中同时使用了惰性过期和定期过期两种过期策略。
可以在redis.conf中有一行配置
maxmemory-policy volatile-lru
Redis的内存淘汰策略是指 在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。
1.检测易失数据
volatile-lru:挑选最近最少使用的数据淘汰
volatile-lfu:挑选最近使用次数最少的数据淘汰
volatile-ttl:挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰2.检测全库数据
allkeys-lru:挑选最近最少使用的数据淘汰
allkeLyRs-lfu::挑选最近使用次数最少的数据淘汰
allkeys-random:任意选择数据淘汰,相当于随机3.放弃数据驱逐
no-enviction(驱逐)
为了避免单点Redis服务器故障,多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的有一台服务器宕机,其他服务器依然可以继续提供服务,实现高可用,同时实现数据冗余备份。
Master(主节点) & Slave(从节点)是什么?
主从复制即将主服务器master中的数据即时、有效的复制到从客户端slave中,Master以写为主,Slave以读为主。
特征: 一个master可以拥有多个slave,一个slave只对应一个master
主从复制的作用:
1.读写分离 :master写、slave读,提高服务器的读写负载能力
2.负载均衡 :根据需求的变化,改变slave的数 量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
3.容灾恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复
主从复制工作流程
- 建立连接阶段(即准备阶段)
- 数据同步阶段
- 命令传播阶段(反复同步)
哨兵模式是一种特殊的模式,是一个独立的进程,它会向redis服务器发送命令,等待redis服务器响应,从而监控多个redis服务器的运行状态,一般哨兵为了高可用,也是集群模式,如果监控到主服务器宕机,(分为主管下线和客观下线),哨兵之间通过流言协议确定这台master是否为客观下线,如果是,则会通过选举算法将一台从机器选举为新的主机,然后通过发布订阅模式通知其他服务器并修改他们的配置文件,设置新的主机信息。
redis-sentinel sentinel.conf 开启哨兵
哨兵的作用:
1.监控: 不断的检查master和slave是否正常运行
2.通知: 当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知
3.自动故障转移:
通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了 16384 个槽位
每份数据分片会存储在多个互为主从的多节点上
数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
同一分片多个节点间的数据不保持强一致性 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
扩容时需要需要把旧节点的数据迁移一部分到新节点
在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端 口号,比如 16379。
作用:
- 分散单台服务器的访问压力,实现负载均衡
- 分散单台服务器的存储压力,实现可扩展性
- 降低单台服务器宕机带来的业务灾难
什么是心跳机制?
就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。
发包方:可以是客户也可以是服务端,看哪边实现方便合理。 心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。
解决方案大概有以下几种:
- 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。
- 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。
- 给所有的缓存一个失效期。
并发高的情况:
读: 读redis->没有,读mysql->把mysql数据写回redis,有的话直接从redis中取;
写:异步话,先写入redis的缓存,就直接返回;定期或特定动作将数据保存到mysql,可以做到多次更新,一次保存;
场景: 宕机
原因: 请求数量高,吞吐量过大
缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
场景: 查询数据量巨大, 数据库服务器崩溃, 服务器无效等问题
原因: 短时间范围内,大量key集中过期
解决方案:
1.原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件
2.根据一些关键数据进行自动降级
3.快速预热,缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统
4.缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待
缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的出现(约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。
场景: 系统平稳运行过程中,数据库崩溃
原因: 单个key高热数据,key过期,未命中redis
解决方案:
设置热点数据永远不过期,
加锁,当某个热点key过期时,大量的请求会进行资源竞争,当某个请求成功执行时,其它请求就需要等待,
应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可。
场景: 系统平稳运行过程中,数据库崩溃
原因:1.Redis中大面积出现未命中 2.出现非正常URL访问
解决:
比如某个请求需要的数据是不存在的,那么仍然将这个数据的key进行存储,这样下次请求时就可以从缓存中获取,但若是每次请求数据的key均不同,那么Redis中就会存储大量无用的key,所以应该为这些key设置一个指定的过期时间,到期自动删除即可。
//client获取锁对象 RLock lock1 = redissonClient1.getLock(resourceName); //向redis实例加锁 RedissonRedLock redLock = new RedissonRedLock(lock1) //可以一次加多个 // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。 isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
核心API: RedissonRedLock
Dubbo是阿里巴巴公司开源的一个高性能、轻量级的开源框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案.
RPC:基于tcp远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。
1、Dubbo采用全spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可;
2、软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。
3、服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
服务容器Container负责启动,加载,运行服务提供者。
服务提供者Provider在启动时,向注册中心注册自己提供的服务。
服务消费者Consumer在启动时,向注册中心订阅自己所需的服务。
注册中心Registry返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者Consumer,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者Consumer和提供者Provider,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心Monitor。
- 远程通讯: 提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。
- 集群容错: 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
- 自动发现: 基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
序列化
dubbo 内部已经将序列化和反序列化的过程内部封装了
我们只需要在定义pojo类时实现seriali zable接口即可
一般会定义一 个公共的pojo模块,让生产者和消费者都依赖该模块。
地址缓存
注册中心挂了,服务是否可以正常访问?
可以,因为dubbo服务消费者在第一-次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
当服务提供者地址发生变化时,注册中心会通知服务消费者。
超时和重试
- 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会直等待下去。
- 在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
- dubbo利用超时机制来解决这个问题,设置-个超时时间, 在这个时间段内,无法完成服务访问,则自动断开连接。
- 使用timeout属性配置超时时间,默认值1000,单位毫秒
//timeout 超时时间 单位毫秒 retries 重试次数 @Service(timeout = 3000,retries=0)
灰度发布
当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
dubbo中使用version属性来设置和调用同一个接口的不同版本
@Service(version=“v2.0”)
public class UserServiceImp12 implements UserService {…}@Reference(version = “v2.0”)//远程注入
private UserService userService;负载均衡(四种方案)
**Random:**按权重随机,默认值。按权重设置随机概率。
RoundRobin: 按权重轮询。
LeastActive: 最少活跃调用数,相同活跃数的随机。
**ConsistentHash:**一 致性Hash,相同参数的请求总是发到同一提供者。
集群容错:
Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作
Failfast Cluster :快速失败,发起-次调用,失败立即报错。通常用于写操作。
Failsafe Cluster:失败安全,出现异常时,直接忽略。返回一个空结果。
Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。
Forking Cluster :并行调用多个服务器,只要一个成功即返回。
Broadcast Cluster: 广播调用所有提供者,逐个调用,任意一台报错则报错。@Reference(cluster = "failover")//远程注入 private UserService userService;
服务降级
mock= force:return null:表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
mock=fail:return null:表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响
@Reference(mock =“ force :return null")//不再调用userService的服务 private UserService userService;
Zookeeper是什么?
它是一个分布式服务框架Zookeeper维护一个类似文件系统的数据结构, 分布式应用配置管理、统一命名服务、状态同步服务、集群管理、分布式锁等功能.
结构
- zookeeper用于内存存储的一个树形结构的文件系统,每个节点都可以用来存储数据.
- 在根目录root下,有两个逻辑命名空间config和workers(下面存每一个机器的ip),在config下每个znode最多存储1MB数据。这么做的目的是存储同步数据并描述znode的元数据。
- 数据节点
分为持久节点(persistent)和临时节点(ephemeral)
持久节点:创建到树上,就会一直存在,除非主动进行操作;
临时节点:生命周期跟客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的节点就会被剔除。
监听事件
使用事件监听器Watcher,在zk发挥注册中心作用上很重要的特性,zk允许用户在指定节点上注册一些Watcher,并且事件触发时(例如dubbo提供者增加服务),zk服务端就会将事件通知给订阅的客户端。这是发布/订阅机制重要组成部分。
1.作为注册中心需要注意什么
1.客户端与服务端超时时间
Zookeeper的Client与Zookeeper之间维持的是长连接,并且保持心跳,Client会与Zookeeper之间协商出一个Session超时时间出来,如果在Session超时时间内没有收到心跳,则该Session过期
2.问题:一个项目肯定能有很多服务,那么zk监听哪些具体的节点?
Client可以在某个ZNode上设置一个Watcher,来Watch该ZNode上的变化。如果该ZNode上有相应的变化,就会触发这个Watcher,把相应的事件通知给设置Watcher的Client。
需要注意的是,ZooKeeper中的Watcher是一次性的,即触发一次就会被取消,如果想继续Watch的话,需要客户端重新设置Watcher
2.Zookeeper集群
Leader选举:
•Serverid:服务器ID
比如有三台服务器,编号分别是1,2,3。
编号越大在选择算法中的权重越大。
•Zxid:数据ID
服务器中存放的最大数据ID.值越大说明数据 越新,在选举算法中数据越新权重越大。
•在Leader选举的过程中,如果某台ZooKeeper
获得了超过半数的选票,
则此ZooKeeper就可以成为Leader了。
Zookeepe集群角色
•Leader 领导者 :
1. 处理事务请求
2. 集群内部各服务器的调度者
•Follower 跟随者 :
- 处理客户端非事务请求,转发事务请求给Leader服务器
- 参与Leader选举投票
•Observer 观察者:1. 处理客户端非事务请求,转发事务请求给Leader服务器
1.一旦客户端被连接,节点将向特定客户端分配SessionID并向该客户端发送确认。如果客户端没有收到确认,它将尝试连接ZooKeeper集合中的另一个节点。 一旦连接到节点,客户端将以有规律的间隔向节点发送心跳,以确保连接不会丢失
3.zookeeper分布式锁
核心API InterProcessMutex可重入锁
//创建分布式锁, 锁空间的根节点路径为/curator/lock InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock"); //获取锁 mutex.acquire() mutex.release();//释放锁
1. 应用解耦:将消息写入消息队列,需要消息的时候自己从消息队列中订阅,从而原系统不需要做任何修改。
2. 流量削峰:原系统慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息。
3. 异步分发:将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度
- 发布/订阅模型。
发布者-Topic-订阅者
消息过滤
对于一些简单的消息的过滤可以使用分类过滤。
创建消息的时候除了制定topic,还可以指定tag,接收消息的时候,除了制定topic,还可以指定接收的tag,*代表任意tag
sql过滤 RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。只支持一些常用的数据类型
- 数值比较,比如:>,>=,<,<=,BETWEEN,=;
- 字符比较,比如:=,<>,IN;
- IS NULL 或者 IS NOT NULL;
- 逻辑符号 AND,OR,NOT;
开启对sql筛选,到
conf/broker.conf
配置文件添加如下配置消息延时
目前支持的消息时间
◆ 秒级: 1, 5, 10, 30
◆ 分级: 1~10, 20, 30
◆ 时级: 1, 2API:setDelayTimeLevel(3);
高性能、高可靠、高实时、分布式
Producer:消息的发送者;举例:发信者
Consumer:消息接收者;举例:收信者
Broker:暂存和传输消息;举例:邮局
NameServer:管理Broker;举例:各个邮局的管理机构
Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EgHBNcEf-1638260218205)(C:\Users\hxy77\Desktop\image-20210311154735277.png)]
Producer通过网络将消息发送给Broker,这个发送可能会发生丢失,比如网络延迟不可达等.
- 有三种send方法,同步发送、异步发送、单向发送。我们可以采取同步发送的方式进行发送消息,发消息的时候会同步阻塞等待broker返回的结果,如果没成功,则不会收到SendResult,这种是最可靠的。其次是异步发送,再回调方法里可以得知是否发送成功。
- **发送者端:**发送消息如果失败或者超时了, 则会自动重试, 默认是重试三次,可以根据api进行更改,比如改为10次.
- 消息发送成功仅代表消息已经到了 Broker 端,Broker 在不同配置下,可能会返回不同响应状态SendResult:
3.Broker端:
集群部署:一主(master)多从(slave)部署方式
分同步和异步(建议开启同步,性能会下降10%,但不会丢失消息)如果 slave节点未在指定时间内同步返回响应,生产者将会收到SendStatus.FLUSH_SLAVE_TIMEOUT 返回状态。
**4.消费者端:**只有当业务逻辑真正执行成功,我们才能返回 ConsumeConcurrentlyStatus.CONSUME_SUCCESS。否则我们需要返回 ConsumeConcurrentlyStatus.RECONSUME_LATER,稍后再重试。
RocketMQ可以严格的保证消息有序,可以分为:
- 分区有序. 当发送和消费参与的队列只有一个,则是全局有序。
- 全局有序。 如果多个队列参与,则为分区有序,即相对每个队列,消息都是有序的。
存入消息对列之前,可以对装信息的集合遍历,里面的元素进行一次hash,得到的indexID
//服务提供方 //根据发送的信息不同,选择不同的消息队列 //根据id来选择一个消息队列的对象,并返回->id得到int值 int mqIndex = order.getId().hashCode() % list.size(); return list.get(mqIndex); //消费者方 //使用单线程的模式从消息队列中取数据,一个线程绑定一个消息队列 MessageListenerOrderly consumer.registerMessageListener(new MessageListenerOrderly() { //使用MessageListenerOrderly接口后,对消息队列的处理由一个消息队列多个线程服务,转化为一个消息队列一个线程服务 public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) { for(MessageExt msg : list){ System.out.println(Thread.currentThread().getName()+" 消息:"+new String(msg.getBody())); } return ConsumeOrderlyStatus.SUCCESS; } });
先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;
原因: 网络故障, 生产者多次发送消息给消费者
解决问题: 保证消息的唯一性,设置数据库主键id 算是多次传输,不要让消息的多次消费带来影响;
消息消费的过程:
1.消费端采用轮询的方式,从mq服务中拉取消息进行消费
2.消费完成通知mq删除已消费成功的消息
3.继续拉取消息消费
对于消费者来说,是主动方,可以采用线程池的方式,根据机器的性能来增加或缩小线程池的大小,控制拉取消息的速度,可以很好的控制自身的压力。
免消息丢失
不使用producer.send(msg),而使用带回调的producer.send(msg, callback)方法;
设置retries为一个较大的值,retries表示Producer发送消息失败后的重试次数
- 性能卓越,单机写入TPS约在百万条/秒,最大的优点,就是吞吐量高。
- 时效性:ms级
- 可用性:非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
- 消费者采用Pull方式获取消息, 消息有序, 通过控制能够保证所有消息被消费且仅被消费一次;
- 有优秀的第三方Kafka Web管理界面Kafka-Manager;
- 在日志领域比较成熟,被多家公司和多个开源项目使用;
- 功能支持:功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用
缺点:
- Kafka单机超过64个队列/分区,Load会发生明显的飙高现象,队列越多,load越高,发送消息响应时间变长
- 使用短轮询方式,实时性取决于轮询间隔时间;
- 消费失败不支持重试;
- 支持消息顺序,但是一台代理宕机后,就会产生消息乱序;
- 社区更新较慢;
特点:
高吞吐量、低延迟 可扩展性:kafka集群支持热扩展 容错性:允许集群中节点失败 (n个节点,n-1个失败) 高并发:支持数千个客户端同时读写
2.kafka的结构
Producer :消息生产者,就是向 kafka broker 发消息的客户端;
Consumer :消息消费者,向 kafka broker 取消息的客户端;
Offset:消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从 消费位置继续消费。
Consumer Group (CG ):消费者组,由多个 consumer 组成。 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个 组内 消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即 消费者组是逻辑上的一个订阅者。
Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker可以容纳多个 topic。
Topic :可以理解为一个队列, 生产者和消费者面向的都是一个 topic;
Partition :为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列;
Replica:副本,为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka 仍然能够继续工作,kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower。
leader :每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 leader。
follower :每个分区多个副本中的“从”,实时从 leader 中同步数据,保持和 leader 数据的同步。leader 发生故障时,某个 follower 会成为新的 leader。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYvL7UI5-1638260218230)(C:\Users\hxy77\AppData\Roaming\Typora\typora-user-images\1629170231865.png)]
Nginx是一个 轻量级/高性能的反向代理Web服务器,他实现非常高效的反向代理、负载平衡,他可以处理2-3万并发连接数,官方监测能支持5万并发.
- http服务器。Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
- 反向代理,简单来说就是真实的服务器不能直接被外部网络访问,所以就需要一台代理服务器
- 负载均衡 , 是指分摊到多个操作单元上进行。
ES=elaticsearch简写, Elasticsearch是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;
1)分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到。
2)实时分析的分布式搜索引擎。
3)可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。
- 可以运行在单机,也可以扩展到上百台服务器
- 把搜索模块独立出一个模块,用ElasticSearch等来实现。
- 新系统开发尝试使用ES作为存储和检索服务器;
Docker 是一个开源的应用容器引擎, 可以让开发者把应用打包,到一个轻量级、可移植的容器中,发布到liunx机器上.
作用:
- 应用更快速的交付和部署
- 更便捷的升级和扩缩容
- 更简单的系统运维
- 更高效的计算资源利用
二叉树简单来说就是 每一个节上可以关联俩个子节点
红黑树是一种平衡的特殊二叉查找树。红黑树的每个结点上都有存储位表示结点的颜色,可以是红 (Red)或黑(Black)。
红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。确保没有一条路径会比其他路径长出俩倍
红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,都会用到左旋和右旋方法。
作用: 减少二叉树元素查找的深度,从而提升平均查找效率。
它是一款半自动的ORM持久层框架,具有较高的SQL灵活性,支持高级映射(一对一,一对多),动态SQL(if,which,foreach),延迟加载和缓存等特性,但它的数据库无关性较低.
对象关系映射。对象指的是Java对象,关系指的是数据库中的关系模型,对象关系映射,指的就是在Java对象和数据库的关系模型之间建立一种对应关系.
- #{}是预编译处理、是占位符, ${}是字符串替换、是拼接符。
- #{} 是以参数的形式, 使用#{}可以有效的防止 SQL 注入, 提高系统安全性。
${}
直接输出参数值,mybatis不做转义,可能引发SQL注入问题。- #{} 比 ${} 安全,
Mybatis 只支持针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件, Mybatis 使用 JDK 的动态代理, 为需要拦截的接口生成代理对象以实现接口方法拦 截功能, 每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke() 方法, 拦截那些你指定需要拦截的方法。
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。
、、、、,加上动态sql的9个标签 trim | where | set | foreach | if | choose | when | otherwise | bind 等;
其中 为sql片段标签,通过标签引入sql片段,为不支持自增的主键生成策略标签。
Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
Mapper.xml文件中的namespace即是mapper接口的类路径。
第1种:在Java代码中添加sql通配符。
string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like #{value}
</select>
第2种:在sql语句中拼接通配符,会引起sql注入
string wildcardname = “smi”;
list<name> names =mapper.selectlike(wildcardname);
<select id=”selectlike”>
select * from foo where bar like "%"${value}"%"
</select>
1.通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。
- 通过来映射字段名和实体类属性名的一一对应的关系。
<id property="id" column="order_id">
使用 @param 注解
(@param(“username”) string username,...)
**第一种:接口实现类继承SqlSessionDaoSupport:**使用此种方法需要编写mapper接口,mapper接口实现类、mapper.xml文件。
第二种:使用MapperFactoryBean:
(1)在sqlMapConfig.xml中配置mapper.xml的位置,如果mapper.xml和mappre接口的名称相同且在同一个目录,这里可以不用配置 <mappers> <mapper resource="mapper.xml 文件的地址" /> <mapper resource="mapper.xml 文件的地址" /> </mappers> (2)定义mapper接口: ① mapper.xml中的namespace为mapper接口的地址 ② mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致 ③ Spring中定义: <bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="mapper 接口地址" /> <propertyname="sqlSessionFactory"ref="sqlSessionFactory" /> </bean>
第三种:使用mapper扫描器:
(1)mapper.xml文件编写: mapper.xml中的namespace为mapper接口的地址; mapper接口中的方法名和mapper.xml中的定义的statement的id保持一致; 如果将mapper.xml和mapper接口的名称保持一致则不用在sqlMapConfig.xml中进行配置。 (2)定义mapper接口: 注意mapper.xml的文件名和mapper的接口名称保持一致,且放在同一个目录 (3)配置mapper扫描器: <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="mapper接口包地址" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患
一级缓存介绍
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能
每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成
MappedStatement
,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache
,最后返回结果给用户二级缓存介绍
在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库
<setting name="cacheEnabled" value="true"/>
1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建jdbc statement对象 statement对象 -----> 作用:用于向数据库发送SQL语句
4、 设置sql语句
5、 设置sql语句中的参数(使用preparedStatement)
6、 通过statement执行sql并获取结果
7、 对sql执行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)
程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程 私有。
Java虚拟栈:存放基本数据类型、对象的引用、方法出口等,线程私有。
本地方法栈:和虚拟栈相似,只不过它服务于Native方法,线程私有。
Java堆:java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享。
方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。(即永久带),回收目标主要是常量池的回收和类型的卸载,各线程共享
1.引用计数法
有一个地方引用对象,计数加一,当计数为零表示可以回收;
缺点是难以解决对象之间的循环引用问题
- 可达性分析算法
1). java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象。它从一系列 GC Roots 出发,边标记边探索所有被引用的对象。
2). 从 GC Root对象 为起点,看是否能沿着引用链找到该对象,找不到,表示可以回收。
3). GC Root对象 包括栈帧中的局部变量、方法区中的静态变量、方法区中的常量、本地方法栈中JNI引用的对象。
堆空间内存分配(默认情况下)
老年代 : 三分之二的堆空间
年轻代 : 三分之一的堆空间
eden区: 8/10 的年轻代空间
survivor From : 1/10 的年轻代空间
survivor To : 1/10 的年轻代空间
垃圾回收算法
- 标记清除法 ;标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间
- 标记整理算法;
- 复制算法; 。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,
垃圾回收器配置
1.单线程/串行收集器
-XX:+UseSerialGC
1). Serial 工作在新生代的单线程收集器,采用『复制算法』,垃圾回收发生时,会暂停所有用户线程
2). SerialOld 工作在老年代的单线程收集器,采用『标记-整理算法』,垃圾回收发生时,会暂停所有用户线程(stop-the-world)
2.多线程回收器-吞吐量优先
配置 -XX:+UseParallelGC -XX:+UseParallelOldGC
1). Parallel Scavenge 工作在新生代的多线程收集器,采用『复制算法』,垃圾回收发生时,会暂停所有用户线程 单核cpu并不能工作地比Serial好,它的特点是一个以吐量优先 的回收器,下面选项打开 Parallel Scavenge + SerialOld。
2). Parallel Old 工作在老年代的多线程收集器,采用『标记-整理算法』,垃圾回收发生时,会暂停所有用户线程,也是以 吞吐量优先 的回收器,下面选项打开 Parallel Scavenge + Parallel Old。
3.G1收集器
JDK7后引入的垃圾收集器 ,它的设计目标是为了适应现在不断扩大的内存和不断增加的处理器数量,进一步降低暂停时间(pause time),同时兼顾良好的吞吐量。
配置 : -XX:+UseG1GC
如果是在Tomcat中运行 , 需要在bin/catalina.sh的脚本中 , 追加如下配置 :
JAVA_OPTS="-XX:+UseConcMarkSweepGC -XX:+PrintGCDetails"
继承Thread类 ,重写run()
实现Runnable接口 ,重写run()
实现 Callable接口, 调用call()方法,call()方法的返回值类型即为Callable接口的泛型
线程安全
线程安全
不是线程安全、应该是内存安全,堆是共享内存,可以被所有线程访问
所谓内存安全又分:
堆是进程和线程共有的空间。是所有线程共享的一块内存区域,在虚拟机启动时创建。堆所存在的内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存,这是线程安全问题;
栈是每个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈 互相独立,因此,栈是线程安全的。操作系统在切换线程的时候会自动切换栈。
- 类不同: sleep 是 Thread 类的静态本地方法,wait 则是 Object 类的本地方法。
- 释放锁: sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
- 用法不同,: sleep 时间到自动恢复,wait 要使用notify() notifyAll ()方法 唤醒
如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。
- 降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗。
- 提高响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,再执行。
- 提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。
1.corePoolSize(线程池的基本大小)
当提交一个任务到线程池时,线程池会创建一个线程来执行任务。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
2.runnableTaskQueue(任务队列)
用于保存等待执行的任务的阻塞队列。
3.maximumPoolSize(线程池最大大小)
线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
4.ThreadFactory:用于设置创建线程的工厂
可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常又帮助。
5.keepAliveTime(线程活动保持时间)
线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
6.TimeUnit(线程活动保持时间的单位)
可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
7.RejectedExecutionHandler(拒绝策略)
当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,
AbortPolicy:直接抛出异常。
- CallerRunsPolicy:只用调用者所在线程来运行任务。
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- DiscardPolicy:不处理,丢弃掉。
- CPU 密集型任务
对于 CPU 密集型的计算场景,理论上线程的数量 = CPU 核数就是最合适的,不过通常把线程的数量设置为CPU 核数 +1,会实现最优的利用率。
IO 密集型任务
对于 IO 密集型任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费。
线程数 = CPU 核心数 / (1 - 阻塞系数)
其中计算密集型阻塞系数为 0,IO 密集型阻塞系数接近 1,一般认为在 0.8 ~ 0.9 之间。比如 8 核 CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 个线程数
线程池中线程复用原理
线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的 一个线程必须对应一个任务的限制。
在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去 执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有则直接执行,也就 是调用任务中的 run 方法,将 run 方法当成一个普通的方法执行,通过这种方式只使用固定的线程就 将所有任务的 run 方法串联起来。
hreadLocal的作用主要是做数据隔离, 在多线程环境下,如何防止自己的变量被其它线程篡改。
每一个 Thread 对象均含有一个 ThreadLocalMap 类型的成员变量 threadLocals ,它存储本线程中所 有ThreadLocal对象及其对应的值 ThreadLocalMap 由一个个 Entry 对象构成 .
当执行set方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对 象。再以当ThreadLocal对象为key,将值存储进ThreadLocalMap对象中。 get方法执行过程类似。
由于每一条线程均含有各自私有的ThreadLocalMap容器,这些容器相互独立互不影响,不会存在线程安全性问题
使用场景:
1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
2、线程间数据隔离
3、进行事务操作,用于存储线程事务信息。
4、数据库连接,Session会话管理。
ThreadLocal正确的使用方法
1.每次使用完ThreadLocal都调用它的remove()方法清除数据
2.将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任 何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
Java通过Executors提供⑤种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个工作线程数量的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
newSingleThreadScheduledExecutor 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。线程池中最多执行1个线程.
线程池的作用:
1.降低资源消耗
2.提高响应速度。
3.提高线程的可管理性
- 常见的浏览器、web处理请求
- 数据库的数据分析
- Tomcat内部采用多线程
- 异步处理, 记录日志
- 分布式计算
死锁的原因
1)是多个线程涉及到多个锁,这些锁存在着交叉,所以可能会导致了一个锁依赖的闭环。
例如:线程在获得了锁A并且没有释放的情况下去申请锁B,这时,另一个线程已经获得了锁B,在释放锁B之前又要先获得锁A,因此闭环发生,陷入死锁循环。
2)默认的锁申请操作是阻塞的。
所以要避免死锁,就要在一遇到多个对象锁交叉的情况,就要仔细审查这几个对象的类中的所有方法,是否存在着导致锁依赖的环路的可能性。总之是尽量避免在一个同步方法中调用其它对象的延时方法和同步方法。
尽量使用 tryLock的方法,设置超时时间,超时可以退出防止死锁。
尽量使用 Java. util. concurrent 并发类代替自己手写锁。
尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
尽量减少同步的代码块。ThreadTocal
1.线程通常有五种状态,创建,就绪,运行、阻塞和死亡状态。
1.新建状态(New):新创建了一个线程对象。
2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于
可运行线程池中,变得可运行,等待获取CPU的使用权。
3.运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4.阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进
入就绪状态,才有机会转到运行状态。
5.死亡状态(Dead):线程执行完了或者因异常退出了run方法,该线程结束生命周期
- Lock锁是对象,而synchronized是java关键字
- Lock锁需要手动加锁释放锁,synchronized的该操作是全自动的。对于异常前者需要手动释放锁,不然容易导致死锁。而后者由jvm自动管理。
- Lock锁的属性可以配置,而synchronized只能是可重入的、非公平的、不可中断的。
机制上:
- synchronized 原始采用的是 CPU 悲观锁机制,即线程获得的是独占锁。独占锁意味着其 他线程只能依靠阻塞来等待线程释放锁
- Lock 用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是 CAS 操作(Compare and Swap)
CAS中ABA问题:
通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行 +1 操作,否则就执行失败。
- 乐观锁
CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
synchronized和ReentrantLock的区别
synchronized是关键字,ReentrantLock是类,这是二者的本质区别。ReentrantLock是类,那么它就更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁
2)ReentrantLock可以获取各种锁的信息
3)ReentrantLock可以灵活地实现多路通知
- 保证被volatile修饰的共享变量对所有线程总是可见的,但不保证原子性
- 禁止指令重排序优化;
- 使用volatile关键字会强制将修改的值立即写入主存;
1.使用Redis缓存,存储热点数据
2.消息队列限流,流量削峰
3.分布式和集群 水平拆分就是将每个微服务进行分层,实现微服务内部的解耦合。
4.将常问的数据,页面静态化
串行在时间上不可能发生重叠,前一个任务没搞定,下一个任务就只能等着
并行在时间上是重叠的,两个任务在同一时刻互不干扰的同时执行。
并发允许两个任务彼此干扰。统一时间点、只有一个任务运行,交替执行
13.常用的并发工具类有哪些?
CountDownLatch
CyclicBarrier
Semaphore
ExchangerCyclicBarrier和CountDownLatch的区别
1)CountDownLatch简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用countDown()方法发出通知后,当前线程才可以继续执行。
2)cyclicBarrier是所有线程都进行等待,直到所有线程都准备好进入await()方法之后,所有线程同时开始执行!
3)CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
4)CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。如果被中断返回true,否则返回false。
只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。
因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap
同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。请知道一条原则:同步的范围越小越好。
1)线程的生命周期开销非常高
2)消耗过多的CPU资源
大量空闲的线程会占用许多内存,大量的线程在竞争CPU资源时还将产生其他性能的开销。
3)降低稳定性
A:浅复制(浅克隆):浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法。
B:深复制(深克隆):深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
现在为了要在clone对象时进行深拷贝, 那么就要Clonable接口,覆盖并实现clone方法,除了调用父类中的clone方法得到新的对象, 还要将该类中的引用变量也clone出来
String是final修饰的,不可变,每次操作都会产生新的String对象
StringBuffer和StringBuilder都是在原对象上操作
StringBuffer是线程安全的,StringBuilder线程不安全的 StringBuffer方法都是synchronized修饰的
性能:StringBuilder > StringBuffer > String
场景:经常需要改变字符串内容时使用后面两个
优先使用StringBuilder,多线程使用共享变量时使用StringBuffer
1.指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。
2.若子类重写了父类中的某些方法,在调用该方法的时候,必定是使用子类中定义的这些方法。多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。
继承,方法重写,父类引用指向子类对象
父类类型 变量名 = new 子类对象 ;
变量名.方法名();
- 实现原理不同,
过滤器
是基于函数回调的,拦截器
则是基于Java的反射机制(动态代理)实现的。- 注入Bean情况不同
- 控制执行顺序不同
Integer是int的包装类,int则是java的一种基本数据类型
Integer的默认值是null,int的默认值是0
Integer变量必须实例化后才能使用,而int变量不需要
1、由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
2、Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
缓冲区就是一段特殊的内存区域,很多情况下当程序需要频繁地操作一个资源(如文件或数据库)则性能会很低,所以为了提升性能就可以将一部分数据暂时读写到缓存区,以后直接从此区域中读写数据即可,这样就显著提升了性。
对于 Java 字符流的操作都是在缓冲区操作的,所以如果我们想在字符流操作中主动将缓冲区刷新到文件则可以使用 flush() 方法操作。
字符流和字节流的使用非常相似,但是实际上字节流的操作不会经过缓冲区(内存)而是直接操作文本本身的,而字符流的操作会先经过缓冲区(内存)然后通过缓冲区再操作文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zwrPmFYt-1638260218241)(C:\Users\hxy77\Desktop\微信截图_20210817091533.png)]
ArrayList和LinkedList区别
ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问)
扩容机制:因为数组长度固 定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会 涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚 至超过linkedList(需要创建大量的node对象) 初始容量10,扩容到原来的1.5倍,全量复制转移
LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询:需要逐 一遍历 遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时都需 要对list重新进行遍历,性能消耗极大。
- HashMap和HashTable区别 :
(1)HashMap方法没有synchronized修饰,线程非安全,HashTable线程安全;
(2)HashMap允许key和value为null,而HashTable不允许
2.底层实现:数组+链表实现
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在
计算key的hash值,二次hash然后对数组长度取模,对应到数组下标, 如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组, 如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表 key为null,存在下标0的位置
数组扩容 :
jdk7:初始容量16加载因子是0.75,大于16*0.75=12的时候触发扩容,上限是Integer的长度2的31次-1,
第一次扩容扩容大小是2的幂数 , 非第一次扩容旧容量*2;
jdk8:
- 空参数的构造函数:实例化的HashMap默认内部数组是null,即没有实例化。第一次调用put方法时,则会开始第一次初始化扩容,长度为16。
- 有参构造函数:用于指定容量。会根据指定的正整数找到不小于指定容量的2的幂数,将这个数设置赋值给阈值(threshold)。
- 如果不是第一次扩容,则容量变为原来的2倍,阈值也变为原来的2倍。**
此外还有几个细节需要注意:
- 首次put时,先会触发扩容(算是初始化),然后存入数据,然后判断是否需要扩容;
- 不是首次put,则不再初始化,直接存入数据,然后判断是否需要扩容;
- 继承不同。 ashTable继承Dictionary类,而hashMap继承了AbstractMap类,但是二者都实现了map接口。
- 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
ConcurrentHashMap为啥线程安全? 性能高
JDK1.7 中的 ConcurrentHashMap 是ReentrantLock+Segment+HashEntry,一个Segment中包含一个HashEntry数组,每个 HashEntry又是一个链表结构
元素查询:二次hash,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部
JDK1.8 中的ConcurrentHashMap 选择了与 HashMap 相同的Node数组+链表+红黑树结构;在锁的实现上,抛弃了原有的 Segment 分段锁,采用
CAS + synchronized
实现更加细粒度的锁。
有了基本类型,为什么还会产生包装类型:
(1)什么是包装类:
包装类型相当于将基本类型包装起来,使其具有对象的性质,并且可以添加属性和方法,丰富了基本类型的操作。
(2)基本类型直接声明而包装类型需使用new关键字来在堆中分配内存空间
(3)基本类型存储在栈中而包装类型存储在堆中通过引用
(4)基本类型初始值,int为0,boolean为false。包装类型初始值为null
(5)基本类型直接赋值使用就好,包装类型需要在集合如Collection、map时会使用
相同点:
1)都可以定义方法和属性。
2)都不能被实例化。
3)都实现了多态。
不同点:
接口可以多实现,但是接口只能继承接口。抽象类只能单继承,但是抽象类可以继承普通类,也能继承抽象类。
实现接口的关键字为
implements
,继承抽象类的关键字为extends
。3)一个类可以实现多个接口,但一个类只能继承一个抽象类。
- 接口强调特定功能的实现,而抽象类强调所属关系。
- 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
- 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
- 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。
检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等
a)需求评审,熟悉业务和需求
b)开发提供接口文档(必须提供接口说明、url、请求方法、请求参数、参数类型、请求参数说明及返回参数说明)
c)编写接口测试用例
d)进行用例评审
e)提测后开始测试
f)提交测试报告
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
GET产生一个TCP数据包;POST产生两个TCP数据包(
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次)。
不同的业务模块部署在不同的服务器上或者同一个业务模块分拆多个子业务,部署在不同的服务器上,解决高并发的问题,提供可扩展性以及高可用性.
一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性
1 .CAP
一致性, 可用性, 分区容错性
CA ,CP, AP 只能保持一种,不能全部保证.
在分布式系统设计中AP的应用较多,即保证分区容错性和可用性,牺牲数据的强一致性
基于CAP理论的, BASE是权衡一致性 与 可用性 的 ;
解决事务:
- 2PC-两阶段提交 目的: 保证的是强一致性 ;
- 若所有参与者都返回yes,说明事务可以提交:
- 协调者向所有参与者发送commit请求。
- 参与者收到commit请求后,将事务真正地提交上去,并释放占用的事务资源,并向协调者返回ack。
- 协调者收到所有参与者的ack消息,事务成功完成。
若有参与者返回no或者超时未返回,说明事务中断,需要回滚:
- 协调者向所有参与者发送rollback请求。
- 参与者收到rollback请求后,根据undo日志回滚到事务执行前的状态,释放占用的事务资源,并向协调者返回ack。
- 协调者收到所有参与者的ack消息,事务回滚完成。
Seata 解决分布式事务 2019年开源
Seata有三个组成部分:事务协调器TC:协调者、事务管理器TM:发起方、资源管理器RM:参与方
第一阶段
Seata 的 JDBC 数据源代理通过对业务 SQL 的解析,把业务数据在更新前后的数据镜像组织成回滚日志
第二阶段
如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志)
如果决议是全局回滚,RM(类似于注册中心,收集状态信息并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚) 收到协调器发来的回滚请求,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚
分布式ID:Snowflake 雪花算法
snowflake的格式为:1位保留位置,41位时间戳,10位机器序列号,12位序列号。snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。(mybatisplus 中有雪花算法)
mybatis-plus 可以通过***@TableId***注解指定主键生成策略
@TableId(value="id",type=IdType.ASSIGN_ID)
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。
分布式锁应该用来解决分布式情况下的多进程并发问题
- 基于数据库实现分布式锁; 和
- 创建一个锁表记录
CREATE TABLE `methodLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '锁定的方法名', `desc` varchar(1024) NOT NULL DEFAULT '备注信息', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存数据时间,自动生成', PRIMARY KEY (`id`), UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';
当想要锁住某个方法时执行insert方法,插入一条数据,method_name有唯一约束,可以保证多次提交只有一次成功,而成功的这次就可以认为其获得了锁,而执行完成后执行delete语句释放锁
当我们想要获得锁时,可以插入一条数据:
INSERT INTO database_lock(resource, description) VALUES (1, 'lock');
当需要释放锁的时,可以删除这条数据:
DELETE FROM database_lock WHERE resource=1;
缺点:
1.这种锁没有失效时间,一旦释放锁的操作失败就会导致锁记录一直在数据库中,其它线程无法获得锁。这个缺陷也很好解决,比如可以做一个定时任务去定时清理;
2.这种锁的可靠性依赖于数据库。建议设置备库,避免单点,进一步提高可靠性;
3.这种锁是非阻塞的,因为插入数据失败之后会直接报错,想要获得锁就需要再次操作。如果需要阻塞式的,可以弄个for循环、while循环之类的,直至INSERT成功再返回;4.这把锁是非重入的,同一个线程在没有释放锁之前无法再次获得该锁。因为数据中数据已经存在了。
Redis实现分布式锁;
1.获取当前Unix时间,以毫秒为单位。
2.使用key和具有唯一性的value(例如UUID)获取锁。当向Redis请求获取锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应,客户端应该尽快尝试去另外一个Redis实例请求获取锁。
3.客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数(N/2+1,这里是3个节点)的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。
4.如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)。
5.如果获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。
4、Redlock
//client获取锁对象 RLock lock1 = redissonClient1.getLock(resourceName); //向redis实例加锁 RedissonRedLock redLock = new RedissonRedLock(lock1) //可以一次加多个 // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。 isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
基于Zookeeper实现分布式锁;
(1)创建一个目录mylock;
(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
(3)获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁//创建客户端 CuratorFramework client = CuratorFrameworkFactory.newClient(ip:port) //创建分布式锁, 锁空间的根节点路径为/curator/lock InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock"); //获取锁 mutex.acquire() mutex.release();//释放锁
创建节点
create(final String path, byte data[], List<ACL> acl,CreateMode createMode)
path:-znode路径
data:节点数据内容
acl:访问控制列表
createMode:节点的类型,枚举类
微服务是架构设计方式,分布式是系统部署方式
分布式属于微服务.
微服务是将模块拆分成一个独立的服务单元通过接口来实现数据的交互。但是微服务不一定是分布式,因为微服务的应用不一定是分散在多个服务器上,他也可以是同一个服务器。服务微服务化后带来的挑战也是显而易见的,例如服务粒度小,数量大,后期运维将会很难.
区别
分布式部署: 必须是每个模块或者节点实现的不同的功能。
微服务: 可以将多个不同功能模块 部署在一个服务器或者一个节点上.
1.架构设计复杂
2.系统的吞吐量变大,响应时间过长
3.维护的复杂度上升
jdk1.8以上
Spring Boot中只用在Controller类上添加一个“@CrossOrigin“注解就可以实现对当前controller 的跨域 访问了,当然这个标签也可以加到方法上。
mkdir 创建目录
-p 创建目录,若无父目录,则创建p(parent)
cd 切换目录
touch 创建空文件
echo 创建带有内容的文件
cat 查看文件内容
tar 打包压缩
-c 建立压缩档案
-x 解压缩文件
-z gzip压缩文件
-j bzip2压缩文件
-v 显示所有过程
-f 使用档名
cp 拷贝
-r 递归拷贝目录
mv 移动或重命名
rm 删除文件
-r 递归删除,可删除子目录及文件
-f 强制删除
chmod 变更文件或目录的权限
kill 杀进程
find 在文件系统中搜索某文件
wc 统计文本中行数、字数、字符数
grep 在文本文件中查找某个字符串
rmdir 删除空目录
pwd 显示当前目录
more / less 分页显示文本文件内容
head / tail 显示文件头、尾内容
sudo 用来以其他身份来执行命令,预设的身份为root
su 换当前用户身份到其他用户身份
stat 显示指定文件的详细信息,比ls更详细
who 显示在线登陆用户
whoami 显示当前操作用户
hostname 显示主机名
uname 显示系统信息
top 动态显示当前耗费资源最多进程信息
ps 显示瞬间进程状态
-e 显示所有进程
-f 全格式
du 查看目录大小
-s 只显示目录大小的总合
-h 带单位显示目录大小
df 查看磁盘大小df
-h 带有单位显示磁盘信息
free 查看内存情况
-b 单位(bytes)
-k 单位(KB)
ifconfig 查看网络情况
ping 测试网络连通
netstat 显示网络状态信息
-ano 查看某个端口是否被占用
-tlnp 根据端口查找PID
man 查看Linux中的指令帮助
reboot 重启系统
shutdown
-r 关机重启
-h 关机不重启
now 立刻关机