Java课程大纲(详解)
2024-11-11 16:58:19 1 举报
AI智能生成
Java自学一套
作者其他创作
大纲/内容
Java Se核心基础
Java入门基础
JDK,JRE,JVM区别与联系
区别
JDK:java开发工具包,针对Java开发人员,可以编译运行Java程序
JDK是Java的核心,包含运行Java运行环境(JRE)和一些Java工具及Java基础类库 。
JRE:java运行时环境,针对使用java程序的客户,可以运行字节码(.class),但不能编译Java源码
JRE是Java运行环境,所有Java程序必须依赖JRE才能运行.只有JVM是不能运行字节码文件的(.class),因为解释的字节码的时候需要lib库. JRE里面有两个文件夹bin/,lib/。bin/就是JVM,lib就是JVM所需要库。JVM+lib=JRE
JVM:用来执行解释执行字节码文件(.class),但不能正确的执行
JVM是JRE的一部分,是虚拟出来的一台计算机.通过实体计算机仿真各种计算功能来实现,JVM有自己完善的硬件架构,如处理器,堆栈,寄存器等,还有相应的指令集.JVM是Java跨平台的核心,Java程序通过JVM的跨平台,从而使Java程序跨平台.Java程序首选会被编译成字节码文件(.class),JVM的核心任务就是解释字节码文件(.class)并映射到真实CPU指令集或者系统调用.JVM不关系上层Java程序,只关系字节码(.class).
联系
JDK包含JRE,JRE包含JVM
单行和多行注释的使用
单行注释
//
多行注释
/**/
关键字,标识符
标识符
标识符由数字(0~9)和字母(A~Z和a~z),美元符号($),下划线(_)以及Unicode字符集种符号大于0xC0的说有符号组合构成(各符号之间没有空格)
标识符的第一个符号为字母,下划线和美元符,后面可以是任何字母,数字,美元符号或下划线。
关键字
数据类型
boolean
布尔数据类型
该类型只有两个字面值:true和false
int
整数数据类型
4个字节,32位,整数类型默认是int类型,取值范围约21亿
long
整数数据类型
8个字节,64位,long类型表示long类型常量,要加L或者l,建议加大L
short
整数数据类型
2个字节,16位,取值范围为【-32768,32767】
byte
整数数据类型
1个字节,8位,取值范围为【-128,127】
float
小数数据类型
4个字节,32位,单精度,能精确到6~7位,声明一个小数类型,要加F或者f,建议加F
double
小数数据类型
8个字节,64位,双精度,能精确到15~16位,小数类型默认是double类型
char
字符数据类型
占1位,有true和false2个值,一个表示真,一个表示假,一般用于表示逻辑运算
流程控制
if
else
do
while
for
switch
case
default
break
continue
return
try
catch
finally
修饰符
public
对所有类可见。使用对象:类、接口、变量、方法
protected
对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
private
在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类
default
(即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
final
final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
transient
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
void
void关键字,它代表的意思是什么也不返回,我们在开发过程中经常会用到,如一个方法不需要返回值时可以使用void关键字,在main方法中也是void关键字
static
静态的变量,或者静态的方法
abstract
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
synchronized
synchronized 关键字声明的方法同一时间只能被一个线程访问
volatile
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。
包
package
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
import
导入你需要的包
throw
throws
extends
implements
继承接口
this
supper
instanceof
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
new
保留字
true
false
null
goto
const
变量,数据类型,运算符
变量定义
String name="张三";
数据类型
整型
字节型
byte
1字节8位
短整型
short
2字节16位
整型
int
4字节32位
长整型
long
8字节64位
浮点型
单浮点型
float
4字节32位浮点数
双浮点型
double
8字节64浮点数
字符型
char
布尔型
boolean(true,false)
字符串型
一串字符
运算符
表达式就是符号(如加号,减号)与操作数(如b,3等)的组合
算数运算符
基本的算数运算符
+、-、*、/、%、++、- -
复合算数运算符
+=、-=、*=、/=、%=
关系运算符
==
等于
!=
不等于
>
大于
<
小于
>=
大于等于
<=
小于等于
逻辑运算符
&&
与
两个为真才是真,由一个为假则是假
||
或
有一个为真就是真,两个为假则是假
^
非
取反
条件运算符
条件?表达式1:表达式2
?:表达式的取决于条件的计算结果。如果条件为真,则表达式1的值,且表达式1的计算结果则为整个?:表达式的值。如果表达式的值。如果表达式1为假,则计算表达式2的值,表达式2的计算结果为整个?:表达式的值
?被称为三元运算符,因为它需要三个操作数
分支语句(if,switch)
if
在某些情况下,需要当某一条件满足时才执行响应代码,条件不满足则执行其他代码。
分支
单分支
if (布尔表达式) {
//布尔表达式为true时需执行的代码;
}
//布尔表达式为true时需执行的代码;
}
双分支
if (布尔表达式) {
//布尔表达式为true时需执行的代码;
}else {
//布尔表达式为false时需执行的代码;
}
//布尔表达式为true时需执行的代码;
}else {
//布尔表达式为false时需执行的代码;
}
多分支
if(布尔表达式1){
//布尔表达式1为true时执行的代码
}else if(布尔表达式2){
//布尔表达式1为false但是布尔表达式2为true时执行的代码
}else if(布尔表达式3){
//布尔表达式1和布尔表达式2为false但是布尔表达式3为true时执行的代码
}else{
//布尔表达式1、布尔表达式2和布尔表达式3均为false时执行的代码
}
//布尔表达式1为true时执行的代码
}else if(布尔表达式2){
//布尔表达式1为false但是布尔表达式2为true时执行的代码
}else if(布尔表达式3){
//布尔表达式1和布尔表达式2为false但是布尔表达式3为true时执行的代码
}else{
//布尔表达式1、布尔表达式2和布尔表达式3均为false时执行的代码
}
嵌套if
if(布尔表达式1){
//布尔表达式1为true时执行的代码
if(布尔表达式2){
//布尔表达式1和布尔表达式2均为true时执行的代码
}else{
//布尔表达式1为true,但布尔表达式2均为false时执行的代码
}
}else {
//布尔表达式1为false时执行的代码
}
//布尔表达式1为true时执行的代码
if(布尔表达式2){
//布尔表达式1和布尔表达式2均为true时执行的代码
}else{
//布尔表达式1为true,但布尔表达式2均为false时执行的代码
}
}else {
//布尔表达式1为false时执行的代码
}
switch
switch分支与发放
switch (date){
case 1:
System.out.println("星期一");
break; //break结束当前switch,return结束当前方法
case 2:
System.out.println("星期二");
default: //当以上条件均不满足时执行default
System.out.println("###");
}
case 1:
System.out.println("星期一");
break; //break结束当前switch,return结束当前方法
case 2:
System.out.println("星期二");
default: //当以上条件均不满足时执行default
System.out.println("###");
}
循环语句(for,forEach,while,do...while)
for
先判断后执行
for(int x = 10; x < 20; x = x+1) {
System.out.print("value of x : " + x );
System.out.print("\n");
}
System.out.print("value of x : " + x );
System.out.print("\n");
}
forEach
for(类型 变量名:集合) {
语句块;
}
语句块;
}
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
while
先判断后执行
while( x < 20 ) {
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}
do...while
线执行在判断
do{
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}while( x < 20 );
System.out.print("value of x : " + x );
x++;
System.out.print("\n");
}while( x < 20 );
特殊流程控制continuc,brcak使用
break
作用于循环中,表示跳出当前循环
continue
作用于循环中,表示跳过当前循环剩余的部分,进入到下一次循环
return
作用于方法中,表示结束该方法
数组的简介及作用
简介
数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式;
数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。数组时一种将一组数据存储在单个变量名下的优雅方式
作用
数组的创建及基本语法
语法
数据类型 [] 数组名字 int[] a;
数据类型 数组名字 [] int a[];
数据类型 数组名字 [] int a[];
创建
声明数组的同时,根据指定的长度分配内存,单数组中元素值都为默认的初始化值;(动态创建)
Int[] ary0 = new int[10];//创建一个长度为10的连续空间。
声明数组并分配内存,同时将其初始化;
int[] ary1 = new int[]{1,2,3,4,5};
与前一种方式相同,仅仅只是语法相对简略;
int[] ary2 = {1,2,3,4,5};
数组元素的获取和遍历
遍历
使用for循环
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.print(arr[i] + " ");
}
获取
int num = arrayC[3];
数组的排序算法(冒泡排序法)
冒泡排序
int[] numbers=new int[]{1,5,8,2,3,9,4};
//需进行length-1次冒泡
for(int i=0;i<numbers.length-1;i++)
{
for(int j=0;j<numbers.length-1-i;j++)
{
if(numbers[j]>numbers[j+1])
{
int temp=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=temp;
}
}
}
System.out.println("从小到大排序后的结果是:");
for(int i=0;i<numbers.length;i++)
System.out.print(numbers[i]+" ");
}
//需进行length-1次冒泡
for(int i=0;i<numbers.length-1;i++)
{
for(int j=0;j<numbers.length-1-i;j++)
{
if(numbers[j]>numbers[j+1])
{
int temp=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=temp;
}
}
}
System.out.println("从小到大排序后的结果是:");
for(int i=0;i<numbers.length;i++)
System.out.print(numbers[i]+" ");
}
Java面向对象
面向对象和面向过程概述
面向过程
优点
性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机,嵌入式开发,Linux/Unix等一般采用面向过程开发,性能是最重要的因素
缺点
没有面向对象易维护,易复用,易扩展
面向对象
优点
易维护,易复用,易扩展,由于面向对象有封装,继承,多态性的特性,可以设计出低耦合的系统,使系统更加灵活,更加易于维护
缺点
缺点
性能比面向过程低
面向对象的三大特征
封装
private
类内部
default
类内部,同一个包
protected
类内部,同一个包,子类
public
类内部,同一个包,子类,任何地方
继承
接口可以继承接口,但用extends而不是implement
接口不能继承抽象类,抽象类可以实现(implement)接口。原因使接口的实现和抽象类的继承都要重写父类的抽象方法,而接口里只能有抽象方法,抽象类允许有抽象方法和非抽象方法
抽象类可以继承实体类
多态
允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性易不同的方式运作。这就意味着虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式给予调用。
//父类
public class Base {
protected void show() {}
}
//子类
class Kid extends Base {
public void show() {
System.out.println(" i am kid");
}
}
public static void main( String[] args )
{
Kid kid = new Kid();
Base base = kid;
base.show();
}
public class Base {
protected void show() {}
}
//子类
class Kid extends Base {
public void show() {
System.out.println(" i am kid");
}
}
public static void main( String[] args )
{
Kid kid = new Kid();
Base base = kid;
base.show();
}
类和对象
类
是对象的抽象
对象
是对客观事物的抽象
成员变量使用
方法的四要素及方法重载
四要素
类名
对于所有的类来说,类名的首字母因该大写。如果类名由若干单词,则后面的每个单词首字母大写。
方法名
所有的方法名都因该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
源文件名
源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记Java是大小写敏感的,)文件的后缀为.java(如果文件名和类名不相同则会导致编译错误)
主方法入口
所有的Java程序都由public static void main(String[] args) 方法开始执行。{%note success no-icon %}在Java小程序中,applet小程序没有main()方法,是调用init()或者run()方法来启动。{%endnote}
构造器特征
信息的封装和隐藏
访问修饰符
package/import语句
类的继承
方法重写,重写与重载的区别
方法参数的传递
值传递和引用地址传递
多态
Object简介及相关方法使用
this,super,abstract,static,final关键字
抽象类
接口
Java常用包与类
API帮助文档使用
java long,java util,java io, java net, java,sql包
八种基本类型对应的包装类使用
String类,StringBuffer类,StringBuilder类
Date类,DateFormat类,Calendar类
Math类,Random类
Biginteger类,BigDecimal
Java集合,IO
java异常概述
处理机制
try...catch...finally
throw,throws
Java集合概述
Collection接口,List接口,Set接口
List接口的学习,Set接口的学习,TreeSet排序
Map接口
Collections工具类
泛型
迭代器
IO概述
File类
IO分类,InputStrcam,OutputStrcam
Beade,Writcr
对象序列化和反序列化
Java高级
线程的概述
线程的创建与启动(Thread)
线程的创建与启动(Runnable)
线程的常用方法
线程的生命周期
线程同步Lock
线程的调度和通信
生产者和消费者模式
线程池
枚举
反射
注解
XML的基本语法
网络编程的概述及作用
TCP/IP模型
Socket和ServerSocket
数据库(mySQL)
数据库常用语句
查看所有的数据库
show databases
创建数据库
create database 数据库命;
删除数据库
drop database 数据库名;
选择数据库use数据库名
use 库名;
显示数据库中所有的表
show tables;
创建一张表
当前键值不能为空
NOT NULL
默认值为null
DEFAULT NULL
字段为int类型且占用11位
INT(11)
位当前字段添加备注
COMMENT'备注内容'
设置为主键,同时设置为外键约束
PRIMARY KEY(ID)
设置数据库引擎为innodb并且默认编码方式为UTF-8.
ENGINE = INNODB DEFAULT CHARSET = UTF8;
自增
auto_increment
子主题
显示表中字段
desc 表名;
desc user1
字符类型
int
整数类型
datatime
可以使用‘now()‘来获取现在的时间
删除表
drop table 表名()
查看数据库的详细信息
show create database 库名;
show create database user1;
操作表的结构常用指令
修改字段的类型
alter table 表名 modify 字段 字段类型;
alter table user1 modify age int(11);
添加新字的字段
alter table 表名 add 字段 字段类型;
alter table user add age int(8);
添加字段到指定位置后
alter table 表名 add 字段 字段类型 after 字段;
alter table user add sex int(8) after age;
删除表中字段
alter table 表名 drop 字段;
alter table user drop sex;
修改指定的字段
alter table 表名 change 原字段名 新字段名 字段的类型;
alter table user change uid userid int(8);
操作表中数据的常用指令
增加数据
insert into 表名 value(值1,值2,值3);
insert into user1 value (1,'root','root');
insert into 表名(字段1,字段2,字段3) value (值1,值2,值3,);
insert into user1(id,username,password) values (2,'admin','admin');
insert into 表名(字段1,字段2,字段3) value (值1,值2,值3),(值1,值2,值3);
insert into user1(id,username,password) values (3,'Alascanfu','123456'),(4,'phone','7900');
删除数据
delete from 表名 where 字段 = 值;
delete from user1 where id = 2;
delet from 表名
delet from user1;
更新数据
update 表名 set 字段1 = 值1,字段2 = 值2 where 字段3 = 值3;
update user1 set password = '123' where id = 3;
查询数据
select * from 表名;
select * from user1;
select 字段 from 表名;
select username from user1;
select 字段1 from 表名 where 字段2 = 值;
select username from user1 where password = 123;
排序查询
select * from 表名 order by 字段 排序关键词(desc 降序 \ asc 升序)
select * from user1 order by id desc;
多字段排序
select 字段 from 表名 order by 字段1 desc/asc, 字段2 desc/asc....;
select * from user1 order by id desc,password asc;
常用函数查询
去重 distinct()
select distinct 字段 = 值 from 表名;
select distinct username = 'phone' from user1;
统计总数 sum()
select sum(字段) as 显示名(课随便设置) 库名
select sum(password) as sum_ from user1;
计算个数 count()
select count(字段) from 表名;
select count(*) from user1;
平均数 avg()
select avg(字段) as 查询命名 from 表名;
select avg(id) as id_avg from user1;
最大值 max()
select max(字段) from 表名;
select max(id) from user1;
最小值 min()
select min(字段) from 表名
select min(id) from user1;
mysql查询,mysql多表查询
https://blog.csdn.net/fuijiawei/article/details/122432727
https://blog.csdn.net/fuijiawei/article/details/122432727
关联查询
select user.username,role.name,role.nameZh from user,role,user_role where user.id = user_role.uid and user_role.rid = role.id;
内链接
inner join
内链接时一种映射关系,如果两张表都存在相同的内容,则回显示出来。(在数学中一般称为“交集”)
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
inner join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
from 表名1 快捷表名
inner join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
通过字段1进行来连接
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> inner join table_b b
-> on a.pk=b.pk;
-> from table_a a
-> inner join table_b b
-> on a.pk=b.pk;
table_a 用a代替,table_b用b代替
连接使用table_a的pk和table_b的pk作为连接的基础经行连接
连接使用table_a的pk和table_b的pk作为连接的基础经行连接
左连接
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk;
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk;
右连接
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
right join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
from 表名1 快捷表名
right join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> right join table_b b
-> on a.pk = b.pk;
-> from table_a a
-> right join table_b b
-> on a.pk = b.pk;
全连接
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
union
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
right join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
union
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
right join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk
-> union
-> select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk;
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk
-> union
-> select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk;
就是在左连接和右连接中间加一个union
全连接 = 左连接+右连接+去重(union)
全连接 = 左连接+右连接+去重(union)
左连接不包含内连接(左表独有数据)
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where b.value is null;
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where b.value is null;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk
-> where b.value is null;
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk
-> where b.value is null;
右连接不包含内连接(右表独有数据)
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
right join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where b.value is null;
from 表名1 快捷表名
right join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where b.value is null;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> right join table_b b
-> on a.value = b.value
-> where a.pk is null;
-> from table_a a
-> right join table_b b
-> on a.value = b.value
-> where a.pk is null;
外连接|全连接 不包含内连接(全集-交集=两集各不同的数据)
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where 表名2.字段1 is null
union all
select * from
表名1 快捷表名
right join 表名2 快捷表名 on 表名1.字段1 = 表名2.字段1
where 表名1.字段1 is null;
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where 表名2.字段1 is null
union all
select * from
表名1 快捷表名
right join 表名2 快捷表名 on 表名1.字段1 = 表名2.字段1
where 表名1.字段1 is null;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.value as B_Value
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk
-> where b.pk is null
-> union all
-> select * from table_a a right join table_b b on a.pk = b.pk where a.pk is null;
-> from table_a a
-> left join table_b b
-> on a.pk = b.pk
-> where b.pk is null
-> union all
-> select * from table_a a right join table_b b on a.pk = b.pk where a.pk is null;
select 表名1.字段1 as A_PK, 表名.字段2 as A_Value,表名2.字段1 as B_PK,表名2.字段2 as B_Value
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where 表名2.字段1 is null
union all
select * from
表名1 快捷表名
right join 表名2 快捷表名 on 表名1.字段1 = 表名2.字段1
where 表名1.字段1 is null;
from 表名1 快捷表名
left join 表名2 快捷表名
on 表名1.字段1 = 表名2.字段1
where 表名2.字段1 is null
union all
select * from
表名1 快捷表名
right join 表名2 快捷表名 on 表名1.字段1 = 表名2.字段1
where 表名1.字段1 is null;
select a.pk as A_PK,a.value as A_Value,b.pk as B_PK,b.Value as B_Value
-> from table_a a
-> left join table_b b
-> on a.value = b.value
-> where b.pk is null
-> union all
-> select * from table_a a right join table_b b on a.value = b.value where a.pk is null;
-> from table_a a
-> left join table_b b
-> on a.value = b.value
-> where b.pk is null
-> union all
-> select * from table_a a right join table_b b on a.value = b.value where a.pk is null;
子查询,分组,排序,分页查询
分组数据查询
group by
分组,就是将相同的分到一组,或者说融合;
having
筛选相当与一个if(判断)
select 字段1,字段2,字段3,from 表名 group by password having 判断条件;
select id,password,username from user1 group by password having id > 2;
分页数据查询
select * from 表名 limit 偏移量,数量;
不写偏移量的话就是默认的为0
实现分页的时候必须写偏移量
偏移量怎么计算?:
limit (n-1)*数量 ,数量
实现分页的时候必须写偏移量
偏移量怎么计算?:
limit (n-1)*数量 ,数量
select * from user1 limit 2,3;
从第二个开始,显示后面三个数据
mysql事务,事务的ACID
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。
https://blog.csdn.net/dengjili/article/details/82468576
https://blog.csdn.net/dengjili/article/details/82468576
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency)
事务前后数据保持一致
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
myslq索引
https://developer.aliyun.com/article/831250
https://developer.aliyun.com/article/831250
索引分类
主键索引(primary key)
设定为主键后,数据库自动建立索引,InnoDB为聚簇索引,主键索引列值不能为空(Null)。
唯一索引
索引列的值必须唯一,单允许有空值(Null),单只允许有一个空值(Null)。但只允许有一个空值(Null)。
复合索引
一个索引可以包含多个列,多个列共同构成一个复合索引。
全文索引
Full Test(MySQL5.7之前,只有MYISAM储存引擎支持全文搜索)。
全文索引类型为FULLTEXT,在定义索引的列上支持值的全文查找允许在这些索引列中查找重复值和空值,全文索引可以在Char,VarChar上创建
全文索引类型为FULLTEXT,在定义索引的列上支持值的全文查找允许在这些索引列中查找重复值和空值,全文索引可以在Char,VarChar上创建
空间索引
MySQL在5.7之后的版本支持OpenGIS几何数据模型,MySQL在空间索引这方面遵循OpenGIS几何数据模型规则。
前缀索引
在文本类型为char,varchar,text类列上创建索引时,可以指定索引列的长度,但是数值类型不能指定。
索引的优缺点
优点
大大提高数据查询速度
可以提高数据检索的效率,降低数据的io成本,类似于书的目录
可以通过索引对数据进行排序,降低数据的排序成本降低了CPU的消耗
被索引的列会自动进行排序,包括(单列索引)和(组合索引),只是组合索引的排序需要复杂一些。
如果按照索引列的循序尽心排序,对order不用语句来说,效率就会提高很多。
缺点
索引会占据磁盘空间。
索引虽然会提高查询效率,但是会降低更新表的效率,比如每次对表进行增删改查操作,MySQL不仅要保持数据还有保存或者更新对应的索引文件。
维护索引需要消耗数据库资源。
综合索引的优缺点
数据库中不是索引越多越好,而是仅为那些常用的搜索字段建立索引效果最佳!
索引语句
查看索引
show index from 表名;
show index from user1;
创建索引
创建主键索引
字段 类型 primary key
id int primary key,
创建单例索引
create index 索引名(自定义) on 表名(字段);
create index password_index on user1(password);
创建唯一索引
create unique index 索引名(自定义) on 表名(字段);
create unique index img1_index on java1(img1);
创建复合索引
create index 索引名(自定义) on 表名(字段1,字段2);
create index user_pass_index on user1(username,password);
满足复合索引的查询的两大原则
加入创建的复合索引为三个字段,按顺序分表是(name,age,sex);
在查询时能利用复合索引的查询条件如下
最左前缀原则(如下四种都满足条件)
select * from user where name = ?
select * from user where name = ? and age = ?
select * from user where name = ? and sex = ?
select * from user where name = ? and age = ? and sex = ?
select * from user where name = ? and age = ?
select * from user where name = ? and sex = ?
select * from user where name = ? and age = ? and sex = ?
如下是不满足最左前缀条件(但是不是全部都不生效,如下解释)
select * from user where name = ? and sex = ? and age = ?
select * from user where age = ? and sex = ? and name = ?
select * from user where sex = ? and age = ? and name = ?
select * from user where age = ? and sex = ?
…………等等
select * from user where age = ? and sex = ? and name = ?
select * from user where sex = ? and age = ? and name = ?
select * from user where age = ? and sex = ?
…………等等
MySQL引擎在执行查询时,为了更好利用索引,在查询过程中会动态调整查询直段的顺序!(也就是说,当条件中的字段全部达到复合索引中的字段时,可以动态调整字段顺序,使起满足最左前缀)
#可以使用复合索引:索引中包含的字段数都有,只是顺序不正确,在执行的时候可以动态调整为最前左缀
select * from user where sex = ? and age = ? and name = ?
select * from user where age = ? and sex = ? and name = ?
#不可以使用复合索引:因为缺少字段,并且顺序不正确
select * from user where sex = ? and age = ?
select * from user where age = ? and name = ?
select * from user where age = ?
select * from user where sex = ?
select * from user where sex = ? and age = ? and name = ?
select * from user where age = ? and sex = ? and name = ?
#不可以使用复合索引:因为缺少字段,并且顺序不正确
select * from user where sex = ? and age = ?
select * from user where age = ? and name = ?
select * from user where age = ?
select * from user where sex = ?
索引的数据结构
MySQL索引使用数据结构主要有B-Tree索引和hash索引。
https://blog.csdn.net/yb546822612/article/details/108659260
https://blog.csdn.net/yb546822612/article/details/108659260
Hash索引
优点
Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
缺点
Hash索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询。
Hash索引无法被用来避免数据的排序操作。
Hash素银不能利用部分索引键查询。
Hash索引在任何时候都不能避免表扫描。
Hash索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。
B-Tree索引
优点
B-Tree 索引是 MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 B-Tree 索引。不仅仅在 MySQL 中是如此,实际上在其他的很多数据库管理系统中B-Tree 索引也同样是作为最主要的索引类型,这主要是因为 B-Tree 索引的存储结构在数据库的数据检 索中有非常优异的表现。
B-tree和hash性能比较
10万*30万数据查询,member_id,buyer_id均加了索引
执行引擎InnoDB,索引类型btree
使用时间:5.025s
执行引擎MEMORY,索引类型hash
使用时间:3.742s
mysql索引与引擎关系
存储引擎,允许索引类型
InnoDB
BTREE
MyISAM
BTREE
MEMORY/HEAP
HASH,BTREE
NDB
HASH,BTREE(see note in text)
对于哈市索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选着哈希索引,查询性能最快;其余大部分场景建议选着BTree索引。
JDBC的学习
自己之前有写过
https://blog.csdn.net/qq_53573029/article/details/123907172?spm=1001.2014.3001.5502
myslq优化
https://blog.csdn.net/zhangbijun1230/article/details/81608252
https://blog.csdn.net/zhangbijun1230/article/details/81608252
选取最适用的字段属性
不用设置过多的字段空间
如邮政编码,设置为char(255)就明显多了,可以直接设置为char(6)就可以了
应该尽量把字段设置为NOTNULL,这样在将来执行查询的时候,数据库不用去比较NULL值
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
使用连接(JOIN)来代替子查询(Sub-Queries)
MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,
使用联合(UNION)来代替手动创建临时表
MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要想同
事务
尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态
insert into 表名(字段1,字段2,字段3) value (值1-1,值1-2,值1-3),(值2-1,值2-2,值2-3);commit;
insert into user1(id,username,password) value (6,'caca',3223),(7,'baii',367890);commit;
锁定表
尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的新能,尤其是在很大的应用系统中,由于在事务执行过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同是访问一个数据库系统,列如访问一个电子商务网站,就会产生比较严重的响应延迟。
具体操作
lock tables 表名 write
使用完后释放该锁
unlock tables
使用外键
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。
具体操作
使用索引
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。
该对哪些字段建立索引?
一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况
优化得查询语句
绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。
注意的几个方面
第一:最好是在相同类型的字段间进行比较的操作
第二:在建有索引的字段上尽量不要使用函数进行操作
第三:在搜索字符型字段时,我们有时会使用LIKE关键字和通配符,这种做法虽然简单但时以牺牲系统新能为代价的
优化方案
1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。
3.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,
数据链接池
手写ORM
web前端
HTML+CSS
HTML基本标签使用
表格及表单标签
DIV标签
CSS的学习
CSS常用选择器
CSS常用样式表
CSS盒子模型
CSS布局,浮动
H5+CSS新特性
JavaScript+Jquery
JavaScript的概述
JavaScript基础语法
JavaScript函数
JavaScript的DOM编程
定时器,考试
JQuery的概述
JQuery的基本选择器,属性选择器,过滤选择器
JQuery数组的迭代
JQuery的常用方法find(),children(),parent()
JQuery操作dom
JQuery表单验证
正则表达式
JSON概述与使用
JSON字符串与JSON对象互相转换
JSON循环遍历
FastJSON使用
Layui
Layui的初步认识
Layui栅格布局
LayUI常用的页面元素(颜色,图标,按钮,导航)
Laycr弹出层
日期与时间选择
分页组件
文件上传
模板引擎
表单
数据表格
Vue
javaEE 企业级开发
Java Web环境搭建
Tomcat服务的安装与使用
Tomcat目录结构
Tomcat项目发布
Tomcat启动与关闭
Tomcat整合IDEA
Maven简介与概述
Maven下载安装及环境变量搭建
Maven本地仓库配置
Maven配置阿里云镜像
Maven常用的命令
Maven项目的目录结构
Maven整合IDEA
JSP+Servlet
Servlet的概述及作用
Servlet的三种创建方式
Servlet的生命周期
HttpServletRequest,HttpServletResponse对象
Servlet参数的接收和编码处理
Servlet请求转发和重定向
会话技术(Cookie/Session)
Servlet三大域对象
JSP概述及作用
JSP组成部分
JSP的执行原理
JSP九大内置对象
JSP四大域对象
EL表达式
JSTL标签
监听器(Listener)域过滤器(Filter)
Ajax概述及原理
Ajax常用的实现方法
项目
使用:Servlet+JDBC+DBUtils+C3P0+Maven+Layui实现RBAC项目实战
Java EE 高级框架阶段
Mybatis
ORM及MyBatis概述
ORM思想
对象关系映射(Object Relational Mapping,简称,ORM):是一种为了解决面向对象与关系数据库存在的互不匹配的问题的技术。
简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将Java程序中的对象自动持久化到关系数据库中
常见的ORM框架
JPA:本身是一种ORM规范,不是ORM框架,由各大ORM框架提供实现。(类似日志门面Slf4j)
Hibernate
以前最流行的ORM框架
MyBatis
是目前最受欢迎的持久层ORM框架
概述
MyBatis是一款优秀的持久层框架
它支持定制化SQL,存储过程以及高级映射
MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
MyBatis可以使用简单的XML或注解来配置和映射原生类型,接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录。
理解Mybatis的实现原理
对比
JDBC有四个核心对象:
1.DriverManager
用于注册数据库连接
2.Connection
与数据库连接对象
3.Statement/PrepareStatement
操作数据库SQL语句的对象
4.ResultSet
结果集或一张虚拟表
Mybatis也有四个核心对象:
1.SqlSession对象
该对象中包含了执行SQL语句的所有方法。类似于JDBC里面的Connection
2.Executor接口
它将根据SqlSession传递的参数动态地生成需要执行地SQL语句,同时负责查询缓存的维护。类似于JDBC里面地Statemnt/PrepareStatemnt
3.MappedStatement对象
该对象是对映射SQL的封装,用于存储映射的SQL语句的id,参数等信息
4.ResultHandler对象,用于返回的结果进行处理,最终得到自己想要的数据各式或类型。可以自定义返回类型
Mybatis入门配置
1.在maven中构建配置文件
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
基本的CRUD操作
核心配置文件及Mapper.xml详解
Dao层开发及常用规则
动态Sql使用
一对多,多对一的关系处理
一级缓存和二级缓存说明及使用
分页的实现及插件PageHelper的使用
generator逆向工程使用
Spring
Spring框架介绍
Spring Framework 是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,以Apache License 2.0开源许可协议的形式发布,也有.NET平台上的移植版本。该框架基于 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7)一书中的代码,最初由Rod Johnson和Juergen Hoeller等开发。Spring Framework提供了一个简易的开发方式,这种开发方式,将避免那些可能致使底层代码变得繁杂混乱的大量的属性文件和帮助类。
IOC/DI解偶合及实现原理
Spring的配置详解
SpringAOP与动态代理
AOP具体实现(XML和注解)
Spring-JdbcTemplete
Spring编程式事务与申明事务管理
Spring源码解析
Spring MVC
MVC设计模式
Spring MVC概述和入门
Spring MVC的执行原理及运行流程
Spring MVC数据绑定
Spring MVC视图的跳转,往域对象传值
Spring MVC常用的注解
Spring MVC和Ajax通过JSON进行互交
Spring MVC+Restful+相关注解实现
Spring MVC上传文件/下载文件
拦截器的创建于配置
I18N国际化实现
SpringBoot自定义starters
SpringBoot
SpringBoot简介于入门
Spring和SpringBoot对比和介绍
SpringBoot的配置类和配置文件
SpringBoot核心开发
SpringBoot整合Web开发
SpringBoot整合日志
SpringBoot整合缓存
SpringBoot与安全
SpringBoot跨域请求
SpringBoot Stater原理
SpringBoot自定义starters
SpringBoot的注解使用和作用
@SpringBootApplication
这个注解是SpringBoot最核心的注解,用在SpringBoot的主类上,标识这是一个Springboot应用,用来开启SpringBoot的各项能力。实际上这个注解是@Configuration,@EnableAutoConfiguration,@ComponentScan三个注解的组合。
@EnableAutoConfiguration
允许SpringBoot自动配置注解,开启这个注解之后,SpringBoot就能够根据当前类路径下的包或者类来配置SpringBean
@Configuration
用于定义配置类,指出该类是Bean配置的信息源,相当于传统的xml配置文件,一般加在主类上。如果有一些第三方库需要用到xml文件任然建议通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
@ComponentScan
组件扫描。让Spring Boot扫描到Configuration类并把它加入到程序上下文。
@ComponentScan注解默认就会配置标识符了@Controller,@Service,@Repository,@Component注解的类到Spring容器中
@ComponentScan注解默认就会配置标识符了@Controller,@Service,@Repository,@Component注解的类到Spring容器中
@Repository
用于标注数据访问组件,及DAO组件
使用@Repository注解可以确保DAO或者repositoriees提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要它们提供XML配置项。
使用@Repository注解可以确保DAO或者repositoriees提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要它们提供XML配置项。
@Service
一般用于修饰service层的组件
@RestController
用于标注控制层组件(如struts中action),表示这是个控制器bean,并且是将函数的返回值直接填入HTTPX响应体中,是REST风格的控制器;它是@Controller和@ResponseBody的集合。
@ResponseBody
表示该方法的返回结果直接写入HTTP response body中
一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后会返回结果不会被解析为跳转路径,而是直接写入HTTP response body中,比如异步获取json数据,加上@responsebody后会直接返回json数据。
一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@responsebody后会返回结果不会被解析为跳转路径,而是直接写入HTTP response body中,比如异步获取json数据,加上@responsebody后会直接返回json数据。
@Conmponent
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
@Bean
相当于XML中的,放在方法上面,而不是类,意思是产生一个bean,并交给Spirng管理。
@AutoWired
byType方式。把配置好的bean拿来用,完成属性,方法的组装,它可以对类成员变量,方法及构造函数进行标注,完成自动装配的工作。
当加上(required=false)时,就算找不到bean也不报错。
当加上(required=false)时,就算找不到bean也不报错。
@Qualifier
当有多个同一类型的bean时,可以用@Qualifier(“name”)来指定。于@Autowired配合使用
@Resource(name="name",type="type")
没有括号内内容的话,默认byName。与@Autowired干类似的事。
@RequestMapping
RequestMapping是一个用来处理请求地址映射的注解;提供路由信息,负责URL到Controller中的具体函数的映射,可用于类或方法上。用于类上,表示类汇总的所有响应请求的方法都是以该地址作为路径。
@RequestParam
用在方法的参数前面。例:
@RequestParam String a = request.getParamenter("a")
@RequestParam String a = request.getParamenter("a")
@PathVariable
路径变量。参数与大括号里的名字一样要相同。例:
RequestMapping("user/get/mac/{macAddress}")
public String getByMacAddress(@PathVariable String macAddress){
//do something;
}
RequestMapping("user/get/mac/{macAddress}")
public String getByMacAddress(@PathVariable String macAddress){
//do something;
}
@Profiles
Spring Profiles提供了一种隔离应用程序配置的方式,并让这些配置只能在特定的环境下生效。
任何@Component或@Configuration都能被@Profile标记,从而限制加载它的时机。
@Configuration
@Profile("prod")
public class ProductionConfiguration{
//...
}
任何@Component或@Configuration都能被@Profile标记,从而限制加载它的时机。
@Configuration
@Profile("prod")
public class ProductionConfiguration{
//...
}
@ConfigurationProperties
Spring Boot可以使用注解的方式将自定义的properties文件映射到实体bean中,比如config.properties文件
@Data
@ConfigurationProperties("rocketmq.consumer")
public class RocketMQConsumerProperties extends RocketMQProperties{
private int consumeThreadMin = 20;
private int consumeThreadMax = 64;
private int consumeConcurrentlyMaxSpan = 2000;
private int pullThresholdForQueue = 1000;
private int pullnterval = 0;
pricate int consumeMessageBatchMaxSize = 1;
pricate int pullBatchSize =32;
}
@Data
@ConfigurationProperties("rocketmq.consumer")
public class RocketMQConsumerProperties extends RocketMQProperties{
private int consumeThreadMin = 20;
private int consumeThreadMax = 64;
private int consumeConcurrentlyMaxSpan = 2000;
private int pullThresholdForQueue = 1000;
private int pullnterval = 0;
pricate int consumeMessageBatchMaxSize = 1;
pricate int pullBatchSize =32;
}
@Entity
注解指定该类是一个实体类,并映射到数据表
@Table(name = "user")
注解指定要用于映射数据表名称(name=“user“意思是表名为”user“)
@Id
注解指定实体类的主键
Mybatis-Plus
MP的简介与快速入门
简介
Mybatis-Plus是Mybatis增强工具,只做增强,不作改变,简化开发,提高效率
快速入门
常见注解
@Getter/@Setter
可以作用在类上何属性上,放在类上,会对所有的非静态(non-static)属性生成Getter/Setter方法,放在属性上,就会对该属性生成Getter/Setter方法。并可以指定Getter/Setter方法的访问级别。
@EqualsAndHashCode
默认情况下,会使用所有份瞬态(non-transient)和非静态(non-static)字段来生成equals和hascode方法,也可以指定具体使用那些属性。
@ToString
生成toString方法,默认情况下,会输出类名,所有属性,属性会按照顺序输出,已逗号分隔。
@NoArgsConstructor,@RequireArgsConstructor and @AllArgsConstructor
无参构造器,部分参数构造器,全参构造器,当我们需要重载多个构造器的时候,Lombok就无能为力了。
@Data
@ToString,@EqualsAndHashCode,所有属性的@Getter,所有non-final属性的@Setter和@RequiredArgsConstructor的组合,通常情况下,我们使用这个注解就足够了。
具体步骤
MP和SpringBoot的集成配置
MP和SpringBoot完成基本的CRUD
MP的分页插件
MP条件构造器Wrapper
MP代码生成器
Quartz
Quartz任务调度简介
Quartz基本使用
Quartz核心类讲解
掌握Quartz触发规则
Quartz Trigger触发器
Quartz Job&JobDetai
Quartz Calendars日历讲解
JobListener监听器和TriggerListener监听器
Spring整合Quartz
Shiro
Shiro概述与入门
Shiro执行流程
Shiro.ini文件说明
权限URL配置
自定义Realm实现权限与授权
标签与权限注解
SSM+Shiro的集成配置
缓存机制
记住我功能的使用
单点登录以及其他应用场景
Git/Github
Git的下载和安装
Git和SVN对比
Git创建版本库
Git版本控制
Git远程仓库
Git分支管理
Git标签管理
Git常用命令总结
GitHub简介与账号注册
Git与Github远程仓库配置
Spring Security
一个高定制化,身份认证,权限控制框架
项目实战
SpringBoot+SpringMVC+Spring+Mybatis+Mybatis-Plas+Quartz+Git多模块电商项目
分布式
Oracle
Oracle的概述,Oracle的安装,新建用户,授权,表空间
Oracle的DQL
Oracle的PL/SQL语句块(语句块,判断,循环)
Oracle的子程序(存储过程和函数)
事务,视图
索引,游标
Oracle调优
Oracle架构设计
Linux/CentOS
虚拟机介绍和应用场景
VMware的安装和使用
Linux/CentOS系统介绍
CentOS系统目录结构
CentOS常用命令
CentOS文件权限管理
CentOS环境搭建(jdk,tomcat,mysql)
LinuxShell编程
Linux运维指南
Linux网络配置
/etc/sysconfig/network-scripts/ifcfg-ens33
ONBOOT=yes
具体符号
保存并退出
esc+:wq
打开可视化界面
startx
查看我们现在所在的目录位置
pwd
查看网络设备信息
ifconfig
检查(查看)服务器下的内存是否足够
free -f
新建文件夹
mkdir 文件夹名字
新建文件
touch 文件名.文件类型
删除文件
rm 文件名.文件类型
删除文件夹
rm -r 文件夹名字
初始化本地仓库
git init
切换至该目录
cd ..
拉取项目代码到指定位置
git clone ..
查看当前项目所有文件
ls
项目占用空间
du-sh*
查看文件
cat ..
系统自带的软件管理器
java8:yum install java-1.8.0-openjbk* -y
openjdk* -y
java -version: 查看版本号
java8:yum install java-1.8.0-openjbk* -y
openjdk* -y
java -version: 查看版本号
yum
存放位置
which ..
来下载压缩包(安装Maven)构建项目
wget ..
解压压缩包
tar -zxvf ..
查看帮助手册
help
安装依赖,构建
.. install
查找jia包
find -name '*jar'
复制
cp ..(地址)
改名字
mv 旧名 新名
后台启动命令
nohup java-jar ..&
查看任务
jobs
查看所有进程
ps -eflgrep 'java' : 只筛选出java进程
ps -eflgrep 'java' : 只筛选出java进程
ps
占用端口
netstat
访问端口
curl ..
下载或上传命令
sz ..
vim编辑器,快速修改代码
vim ..
返回上级目录
cd ~
查看系统网络设备信息
ip add
进行筛选,筛选有6的数字
grep 6 筛选路径 > 导入筛选值
文件下所有子文件夹里的图片全部到执行命令的文件夹的根目录
运行程序
./程序名
浏览器访问位置
curl localhost:3344
访问端口3344
查看端口是否开放
firewall-cmd --list-ports
开启端口
firewall-cmd --zone=public --add-port=3310/tcp --permanent
开启端口3310
颜色区分文件类型
灰色
普通文件
蓝色
目录文件
绿色
可执行文件
红色
压缩文件
浅蓝色
链接文件
粉红色
图片文件
黄色
设备文件
Nginx
Nginx的简介和安装
简介
子主题
安装
Niginx 安装简单,配置灵活
安装向导
如果在网页用ip加端口打不开
检查
防火墙里80端口有没有开
阿里云或腾讯云需要把安全组的80放开
linux启动
./nginx -s stop
停止执行文件
./nginx
启动
vim nginx.conf
修改端口位置
./nginx -s reload
重新启动
文件启动位置
cd /usr/local/nginx/sbin
docker 启动
docker run --name=nginx -d -p 4030:80 nginx
1
Nginx的原型图
Nginx架构模型
Nginx负载均衡
Nginx+Vsftpd文件服务器
Nginx实现反向代理服务器
Nginx详细配置信息
LVS+Keepalived+Nginx安装及配置
Dubbo
Dubbo简介
Dubbo架构分析
Dubbo的入门案例
Dubbo-admin的安装和使用
Dubbo注册中心
Dubbo提供者以及配置优化
Dubbo消费者和配置优化
Dubbo高可用,Dubbo负载均衡
SpringBoot整合Dubbo
解释:Apache Dubbo是一款高性能Java RPC框架
Redis
Nosql简介和Redis的安装
什么是NoSQL
Not Only SQL
NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。
NoSQL的特点
解耦,没有太大的关联
方便扩展(数据之间没有关系,很好扩展!)
大数据量高性能(Redis一秒写8万次,读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能回比较高)
传统类型是多样性的(不需要事先设计数据库,随取随用)
传统RDBMS和NoSQL
传统关键型数据库(MySQL,Oracle等)RDBMBS:
比较
传统的RDBMS
结构化组织
SQL
数据和关系都存在单独的表中操作,数据定义语言
基础的事务
关系型数据库遵循ACID规则
....
NoSQL
代表着不仅仅是SQL
没有声明性查询语言
没有预定义的模式
键值对存储,列存储,文档存储,图形数据库
键值对存储,列存储,文档存储,图形数据库
最终一致性,而非ACID属性
非结构化和不可预知的数据
CAP定理
高性能
...
Redis了解
单线程
核心:redis是将所有所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下分切换,耗时的操作)
同时设置多个值,如果存在一个值,就都不成功。如果不存在,就都成功,保证原子性
Redis不区分大小写命令
docker启动Redis
sudo docker run -p 6379:6379 --name redis -v /data/redis/redis.conf:/etc/redis/redis.conf -v /data/redis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
20
开始
连接Redis
redis-cli -p 6379
退出Redis
ctrl+c
Redis的数据模型
五大基本数据类型
String命令
清除当前数据库
flushdb
清除全部数据库的内容
FLUSHALL
查看数据库所有的key
keys *
切换数据库
select 3
查看数据库大小
DBSIZE
端口
6379
移除数据第一个
move name 1
10秒后过期
EXPIRE name 10
查看当前类型
type name
views自加一
incr views
views自加10
incrby views 10
views自减一
decr views
views自减10
decrby views 10
过期
ttl key3
多数据存储
mest k1 v1 k2 v2 k3 v3
设置多个值
mset user:1:name zhangsan user:1:age 2
是否存在key1的值
EXISTS key1
追加字符串
APPEND key1 "hello"
截取字符串长度
STRLEN key1
如果存在mykey,创建失败
sentnx mykey "MongDB"
先get然后set。如果不存在值,则返回nill。如果存在值,获取原来的值,并设置新的值
getset db redis
小结
String类似的使用场景:value除了是我们的字符串还可以是我们的数字!
使用场景
计数器
统计多单位的数量
粉丝数
对象缓存存储
List命令
所有list命令都是用l开头的
获取全部的值
LRANGE list 0 -1
将一个或多个值,插入到列表头部(左)
LPUSH list one
将一个或多个值,插入到列表尾部(右)
Rpush list right
将一个或多个值,移除到列表头部(左)
lpop list
将一个或多个值,移除到列表头部(右)
rpop list
通过下表获得list中某一个值
lindex list 1
移除一个精确的值
lrem list 1 one
移除list中一个值“one”
通过下表截取指定的长度,这个list已经被改变了,截断了
ltrim mylist 1 2
保留小标 1 2
将列表中指定下标的值替换为另外一个值,更新操作(不存在会报错,存在更新当前下标的值)
lset list 0 item
将下标为0,把值替换为item
将某个具体的value插入到列表中某个元素的后面
linsert mylist before world other
把other插入到world前面
将某个具体的value插入到列表中某个元素的后面
linsert mylist after world new
把new插入到world后面
小结
他实际上是一个链表,before Node after, left ,right都可以插入值
如果key不存在,创建新的链表
如果key存在,新增内容
如果移除了所有值,空链表,也代表不存在!
在两边插入活着改动值,效率最高! 中间元素,相对来说效率会低一点~
使用场景
消息排队
消息队列
Lpush Rpop
栈
Lpush Lpop
Set命令
set集合中添加元素
sadd myset hello
查看set的所有值
smembers myset
判断某一个元素是不是在set集合中
sismember myset helo
判断myset中有么有hello
存在:(integer)1
不存在:(integer)0
获取set集合中的元素个数
scard myset
移除set集合中的中元素
rem myset hello
移除myset中元素hello
随机抽选集合中的元素
srandmember myset
随机抽选myset集合的元素
随机抽选出指定个数的元素
srandmember myset 2
随机抽选2个myset集合中的元素
随机删除一个集合中的元素
spop myset
随机删除myset中的元素
将一个指定的值,移动到另外一个集合
smove myset myset2 wu
将myset中的wu元素移动到myset2里
差集(不同的)
sdiff key1 key2
以key1为基准,来找key2与key1中不同的
交集(相同的)(共同好友)
sinter key1 key2
以key1为基准,来找key2中与key1相同的元素
并集(存在的元素)
sunion key1 key2
以key1为基准,来找key2与key1存在的元素
小结
使用场景
粉丝
共同好友
共同关注
共同爱好
二度好友
Hash命令
基本了解
Map集合,key-map!时候这个值是一个map集合!本质和String类型没有太大的区别,还是一个简单你的key-value!
添加一个具体的key-value
hset myhash field1 kuangshen
在myhash键field1值中添加元素kuangshen
查询一个具体的key-value
hget myhash field1
get一个key-value
设置多个key-value
hmset myhash field1 hello field2 world
设置key field1 value 是hello,field2 value是 world
获取多个字段值
hmget myhash field1 field2
获取myhash中field和field2的值
获取全部的数据
hgetall myhash
删除hash指定的key字段!对应的value值也就消失了!
hdel myhash field1
查看有多少key-value对
hlen myhash
查看所有的key
hkeys myhash
查看所有的value
hvals myhash
value做加法
hincrby myhash field3 2
myhash中field3 key的 value field3 加2
value做减法
hincrby myhash field3 -2
myhash中field3 key的 value field3 减2
小结
hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!
hash更适合对象的存储,String更适合string字符串存储
Zset(有序集合)
添加用户
zadd salary 2500 xiaohong
添加salary(薪水)为2500 姓名为小红
从小到大依次排序
zrangebyscore salary -inf +inf
zrange salary 0 -1
从 大到小依次排序
zrevrange salary 0 -1
从最小到最大依次排序并打印出来
zrangebyscore salary -inf +inf withscores
从最小值到到指定的值
zrangebyscore salary -inf 2500 withscores
最小值到2500的排序
删除一个集合
zrem salary xiaohong
删除有序集合中的一个salary 中的xiaohong
获取有序集合中的个数
zcard salary
获取区间的成员数量
zcount myset 1 3
获取1~3之间的成员数量
小结
学习思路
其余的一些API,通过我们的学习,剩下的如如果共工作需要,这个时候你可以查看官方文档
使用场景
set排序
存储班级成绩标
工资表排序
普通消息设置1,重要消息设置2,带权重进行判断! 加权
排行榜
三种特殊数据类型
geospatial 地理位置
简述
朋友的定位,附近的人,打车距离计算?
Redis的Geo在Redis3.2版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,房源几里的人!
添加地理数据(两级步伐直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!):有效经度从-180到180度,有效纬度从-85.051度到85.05112878
geoadd china:city 116.40 39.90 beijing
添加国家为china 经度为116.40,维度为39.90 城市为北京
geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
添加国家为china 经度为120.16,维度为30.24 城市为杭州,经度为108.96,纬度为34.26城市为西安
获取地理经纬度
Geopos china:city beijin chongqing
获取china的beijin与chongqing地理位置(经纬度)
两个位置的距离长度
geodist china:city beijin shanghai km
国家中国,城市北京到上海的直线距离
m:表示米
km:表示千米
mi:表示英里
ft:表示英尺
km:表示千米
mi:表示英里
ft:表示英尺
查寻具体的长度半径,查询城市
georadius china:city 110 30 1000 km
查询经度110 纬度30 半径为1000km的所有城市名字
查询具体的长度半径,查询经纬
georadius china:city 110 30 1000 km withcoord
查询经度110 纬度30 半径为1000km的所有城市的经纬度
查询具体的长度半径,查询经纬,并且做出筛选
georadius china:city 110 30 1000 km withcoord count 1
查询经度110 纬度30 半径为1000km的所有城市的经纬度,但只查询一个出来
通过城市来查询具体的城市
georadiusbymember china:city beijin 1000 km
查询北京1000km以内的城市,返回城市名
hash,将该命令返回11个字符的Geohash字符串!
geohash china:city being chongin
127.0.0.1:6379> geohash china:city beijin chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
查看所有的元素
ZRANGE china:city 0 -1
子主题
移除指定元素
zrem china:city beijin
移除北京
小结
使用场景
附近找朋友
查距离长度
GEO
Hyperloglog基数统计
简介
什么是基数?
两组数做对比,基数(不重复的元素)
Redis2.8.9版本就更新了Hyperloglog数据结构
Redis Hperloglog基数统计的算法!
优点:占用的内存是固定,2的64次方不同的元素的技术,只需要12kb内存!
创建元素
pfadd mykey a b c d f i g e
创建元素a b c d f i g e
查看有多少元素
Pfcount mykey
查看元元素个数
合并元素
pfmerge mykey3 mykey mykey2
合并mykey 和mykey2成立新的键mykey(相同元素只出现一次)
小结
使用场景
网页的UV(一个人访问一个网站多次,但还是算作一个人!)
传统方法,set保存用户的id,然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id,就会比较麻烦! 我们的目的是为了计数,而不是保存用户id;
这个方式如果保存大量的用户id,就会比较麻烦! 我们的目的是为了计数,而不是保存用户id;
官方统计有0.81%的错误率!统计UV是任务,是可以忽略不记的
Bitmaps位图场景详解
位存储
Bitmaps位图
添加
setbit sign 0 1
如果是打卡,sign的第0天(1天),0是没打卡,1是打卡:第一天打卡了
查找
getbit sign 0
搜索sign 第一天有没有打卡
统计,1有多少
bitcount sign
统计sign打卡了多少天
小结
使用场景
统计用户信息,活跃,不活跃,登录,未登录,打卡,两个状态的,都可以使用Bitmaps
Redis事务
Redis事务本质:一组命令的集合!一个数据中的所有命令都会被序列化,在事务执行过程中,或按照顺序执行!,一次性,顺序性,排他性,执行一些列的命令~
Reds单挑命令是保存原子性的,但是事务不保证原子性~
Redis事务没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis的事务
基本逻辑
1.开启事务(multi)
2.入队命令(....)
3.执行事务(exec)
正常执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) "v2"
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) "v2"
从上到下依次执行
放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> get k1
"v1"
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> get k1
"v1"
取消事务后,事务列队中命令都不会被执行
编译型异常(代码有问题!命令有错),事务中所有的命令都不会被执行!
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 vi
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)
127.0.0.1:6379>
OK
127.0.0.1:6379(TX)> set k1 vi
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> getset k3
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)
127.0.0.1:6379>
先是命令错误,然后是执行事务报错
运行异常(1/0),如果事务中存在语法性,那么执行命令的时候,其他命令是可以正常执行的(比如有10个命令,其中有一个运行异常,系统会不管者一个异常,执行其他没有异常的语句)所以不存在原子型
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
"v1"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> get k3
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
Redis实现乐观锁
理论
悲观锁
很悲观,认为什么时候都会出现问题,无论做什么都会加锁!
乐观锁
很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候区判断一下,在此期间是否有人修改过这个数据,
获取version
更新的时候比较version
Redis监视测试
正常执行成功
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> get out
"0"
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
值测试多线程修改值,使用watch可以当做redis的乐观锁操作!
第一线程
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379>
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379>
第一吸纳线程执行失败
第二线程
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> get money
"1000"
"80"
127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> get money
"1000"
执行失败
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 990
2) (integer) 30
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 990
2) (integer) 30
1.如果发现事务执行失败,就先解锁
2.获取最新的值,再次监控,select version
3.对比监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果变量执行失败就继续执行1
Jedis
我们要使用Java来操作Redis
具体操作
1.导入对应的依赖
<dependencies>
<!--导入jedis的包-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.0.0-beta1</version>
</dependency>
<!--存入数据Fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
</dependencies>
<!--导入jedis的包-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.0.0-beta1</version>
</dependency>
<!--存入数据Fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
</dependencies>
2.编码测试:
1.链接数据库
2.操作命令
//Jedis 所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
System.out.println("清空数据"+jedis.flushDB());
System.out.println("判断某个键是否存在:"+ jedis.exists("username"));
System.out.println("新增<username,password>的键值对:"+jedis.set("username","password"));
System.out.println("获取username:"+jedis.get("username"));
//Jedis 所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
System.out.println("清空数据"+jedis.flushDB());
System.out.println("判断某个键是否存在:"+ jedis.exists("username"));
System.out.println("新增<username,password>的键值对:"+jedis.set("username","password"));
System.out.println("获取username:"+jedis.get("username"));
3.断开链接
public class TestPing {
public static void main(String[] args) {
//1.new Jedis 对象
Jedis jedis = new Jedis("192.168.238.128",6379);
//Jedis 所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
}
}
public static void main(String[] args) {
//1.new Jedis 对象
Jedis jedis = new Jedis("192.168.238.128",6379);
//Jedis 所有的命令就是我们之前学习的所有指令!
System.out.println(jedis.ping());
}
}
输出 pong
使用事务
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.238.128",6379);
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","kuangshen");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toString();
try {
//执行事务
multi.set("user1",result);
multi.set("user2",result);
// int i = 1/0;
multi.exec();
multi.exec();
}catch (Exception e){
//放弃事务
e.printStackTrace();
}finally {
//关闭链接
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();
}
}
}
public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.238.128",6379);
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","kuangshen");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toString();
try {
//执行事务
multi.set("user1",result);
multi.set("user2",result);
// int i = 1/0;
multi.exec();
multi.exec();
}catch (Exception e){
//放弃事务
e.printStackTrace();
}finally {
//关闭链接
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();
}
}
}
SpringBoot整合Redis
SpringBoot查欧总数据库:Spring-data jpa jdbc mogodb redis!
SpirngData也是和SpringBoot齐名的项目!
说用:在SpringBoot2.x之后,原来使用jedis被替为lettuce?
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!更像BIO模式
lettuce:采用netty,实例可以在多个多线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
SpirngData也是和SpringBoot齐名的项目!
说用:在SpringBoot2.x之后,原来使用jedis被替为lettuce?
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!更像BIO模式
lettuce:采用netty,实例可以在多个多线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
整合测试
1.导入依赖
<!--操作Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--操作Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置连接
# SpringBoot 所有的配置类,都有自动配置类 RedisAutoConfiguration
# 自动配置类都会绑定一个properties配置文件 RedisProperties
# 配置redis
spring.data.redis.host=192.168.238.128
spring.data.redis.port=6379
# 自动配置类都会绑定一个properties配置文件 RedisProperties
# 配置redis
spring.data.redis.host=192.168.238.128
spring.data.redis.port=6379
3.测试
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("wu","zhi");
System.out.println(redisTemplate.opsForValue().get("wu"));
}
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("wu","zhi");
System.out.println(redisTemplate.opsForValue().get("wu"));
}
jackson出错导入maven包(正确)
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version> 2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version> 2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version> 2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version> 2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version> 2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version> 2.9.9</version>
</dependency>
Redis Config详解
启动的时候,就是通过配置文件来启动的
查看狂神笔记
进入配置
docker exec -ti redis12 redis-cli -h localhost -p 6379
位置:/etc/redis
持久化
持久化之RDB(Redis DataBase)操作
Redis是数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失,所以Redis提供了持久化功能!
什么是RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何io操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常的敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点就是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况不需要修改这个配置!
RDB保存你的文件时dump.rdb文件
RDB文件地址:/data/redis/data
具体步骤
save 60 5
测试60s内修改5次key,就会触发rdb操作
出发机制
1.serve的规则慢足的情况下,会自动出发RDB规则
2.执行flushall命令,也会出发我们的rdb规则
3.退出redis,会产生出发我们rdb文件!
备份就自动生成一个dump.rdb
如何恢复RDB文件!
1.只需要将RDB文件放在我们redis启动目录就可以,redis启动的时候会自动检查dump.rdb恢复启动的数据!
2. 查看需要存在的位置
127.0.0.1:6379> config get dir
1)"dir"
2)"usr/local/bin"
1)"dir"
2)"usr/local/bin"
几乎就他默认的配置就够用了,但是我们还是需要去学习!
优点
1.适合大规模的数据恢复!
2.对数据完整性不高!
缺点
1.需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了!
2.fork进程的时候,会占用一定的内容空间!!
持久化之AOF(Append Only File)操作
什么是AOF
一日志的形式来记录每一个写操作,将redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就是根据日志文件的内容将指令从前到后执行一次一完成数据的恢复工作
AOF保存的时appendonly.aof文件
将所有命令都记录下来,history,恢复的时候就把这个文件全部再执行一遍!
具体操作
appendonly no
默认时不开起的,我们需要手动配置!我们只需要将appendonly改为yes启动aof
Redis发布订阅
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息,
使用场景
微博,微信,关注系统
具体命令
订阅频道
等待推送信息
等待推送信息
127.0.0.1:6379> SUBSCRIBE kuangshengsuo
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshengsuo"
3) (integer) 1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshengsuo"
3) (integer) 1
发布者腹部信息到频道
127.0.0.1:6379> PUBLISH kuangshengsuo "hell,kuangshen"
(integer) 1
(integer) 1
原理
Redis是使用C实现的,通过分析Redis源码里的pubsub.c文件,了解发布和订阅机制的底层实现,藉此加深对redis的理解。
Redis通过PUBLISH,SUBSCRIBE和PSUBSCRIBE等命令实现发布和订阅功能。
微信:
通过SUBSCRIBE命令订阅某频道后,redis-server里维护一个字典,字典的键就是一个个频道!,而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端,SUBSCRIBE命令的关键,就是将客户端添加到给定channel的订阅链表中。
通过PUBLSH命令订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub从字面上理解就是发布(publish)与订阅(subscribe),再Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行信息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
Redis集群环境搭建
主从复制之复制原理
主从复制,读写分离!80%的情况下都是进行读操作!减缓服务器的压力!架构中经常使用! 一主二从!
主从复制的作用主要包括:
1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据数据冗余方式
2.故障恢复:当主结点出现问题时,可以由从结点提供服务,实现快速的故障恢复;实际上是一种服务的冗余
3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从结点提供读服务(即Redis数据时引用连接主节点,读Redis数据时应用连接从结点),分担服务器负载;尤其是在写少读多的场景下,通过多个从结点分担读负载,可以大大提高redis服务器的并发量。
4.高可用(一般都是说集群)基石:除了上述作用外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制时Redis高可用的基础。
一般来说,要将Redis运用与工程项目中,只使用一台redis是万万不能的(宕机),原因如下:
1.从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有请求负载,压力较大;
2.从容量上,单个Redis服务器内存容量有限,就一台Redis服务i其内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该操过20G.
电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是”多读少写“。
对于这种场景我们可以使用如下这种架构
主从复制,读写分离!80%的情况都是进行读操作!减缓服务器的压力!架构中经常使用!一主二从!
只要在公司中,主从复制就必须要使用的,因为真实的项目不可能单机使用Redis!
环境配置
只配置从库,不用配置主库
info replication
查看当前库的信息
127.0.0.1:6379> info replication
# Replication
role:master #角色master
connected_slaves:0 #没有从机器
master_failover_state:no-failover
master_replid:c3caaca9f70450fad8adfe70f2ff6a68ce70e134
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# Replication
role:master #角色master
connected_slaves:0 #没有从机器
master_failover_state:no-failover
master_replid:c3caaca9f70450fad8adfe70f2ff6a68ce70e134
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
复制三个配置文件,然后修改对应的信息
1.端口
2.pid名字
log文件名字
dump.rdb 名字
修改完毕之后,启动三个redis服务,可以通过进程信息查看
默认情况下,每台redis都是主节点
一般情况配置从机就行
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
OK
OK
从机配置,认127.0.0.1 6379当主机
一主(79)二从(80,81)
使用命令配置完成
6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=196,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=196,lag=1
master_failover_state:no-failover
master_replid:c66d086d6b590783505eff47e620743f8b32d858
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:196
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=196,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=196,lag=1
master_failover_state:no-failover
master_replid:c66d086d6b590783505eff47e620743f8b32d858
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:196
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_read_repl_offset:280
slave_repl_offset:280
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c66d086d6b590783505eff47e620743f8b32d858
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:280
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:280
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_read_repl_offset:280
slave_repl_offset:280
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c66d086d6b590783505eff47e620743f8b32d858
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:280
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:280
6381
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_read_repl_offset:168
slave_repl_offset:168
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c66d086d6b590783505eff47e620743f8b32d858
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:168
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:155
repl_backlog_histlen:14
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_read_repl_offset:168
slave_repl_offset:168
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c66d086d6b590783505eff47e620743f8b32d858
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:168
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:155
repl_backlog_histlen:14
使用永久配置
在配置文件中修改
细节
从机里只读不写
127.0.0.1:6380> get k1
"v1"
127.0.0.1:6380> set k2 v2
(error) READONLY You can't write against a read only replica.
"v1"
127.0.0.1:6380> set k2 v2
(error) READONLY You can't write against a read only replica.
你不能对只读文件进行修改
主机断了没有配置哨兵,但配置还是从机
测试:主机断开连接,从机依旧连接到主机的,但是没有写操作,这个时候,主机如果回来了,从机依旧可以直接获取主机写的信息!
如果使用命令行,来配置的主从,这个时候如果重启了,就会变回主机!只要变为从机,立马就会从主机中获取值!
复制原理
Slave启动成功连接到Master后会发送一个sync同步命令
Master接收命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增值复制:Master继续将新的所有收集到的修改命令一次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将会被自动执行,我们的数据一定可以在从机中看到!
链路(毛毛虫)
上一个M连接下一个s
宕机后手动配置主机
自己当主机
SlaveofF no one
哨兵模式
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failove过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主管下线,当后面的哨兵也检车到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果有一个哨兵发起,进行failover【故障转移】操作。切换成功后,就会通过发布订阅模式,让各个哨兵吧自己监控的服务器是心啊切换主机,这个过程称为客观下线
测试
我们目前的状态就是一主二从
1.配置哨兵配置文件sentinel.conf
#sentinel monitor myredis(被监控的名称) 127.0.0.1(host) 6379(port) 1(选举人)
sentinel monitor myredis 127.0.0.1 6379 1
sentinel monitor myredis 127.0.0.1 6379 1
后面这个数字1,代表主机挂了,slave投票看让谁接替成为主机,票数最多的,就会成为主机!
2.启动哨兵
[root@localhost bin]# redis-sentinel /etc/redis/sentiel.conf
9678:X 06 Jun 2023 05:14:07.501 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9678:X 06 Jun 2023 05:14:07.501 # Redis version=6.2.7, bits=64, commit=00000000, modified=0, pid=9678, just started
9678:X 06 Jun 2023 05:14:07.501 # Configuration loaded
9678:X 06 Jun 2023 05:14:07.502 * Increased maximum number of open files to 10032 (it was originally set to 1024).
9678:X 06 Jun 2023 05:14:07.502 * monotonic clock: POSIX clock_gettime
9678:X 06 Jun 2023 05:14:07.502 # A key '__redis__compare_helper' was added to Lua globals which is not on the globals allow list nor listed on the deny list.
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.2.7 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 9678
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
9678:X 06 Jun 2023 05:14:07.504 # Sentinel ID is 8dee4b5228ca807e23fa2cbeb6dae863a1b7af8b
9678:X 06 Jun 2023 05:14:07.504 # +monitor master myredis 127.0.0.1 6379 quorum 1
9678:X 06 Jun 2023 05:14:07.504 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
9678:X 06 Jun 2023 05:14:07.501 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9678:X 06 Jun 2023 05:14:07.501 # Redis version=6.2.7, bits=64, commit=00000000, modified=0, pid=9678, just started
9678:X 06 Jun 2023 05:14:07.501 # Configuration loaded
9678:X 06 Jun 2023 05:14:07.502 * Increased maximum number of open files to 10032 (it was originally set to 1024).
9678:X 06 Jun 2023 05:14:07.502 * monotonic clock: POSIX clock_gettime
9678:X 06 Jun 2023 05:14:07.502 # A key '__redis__compare_helper' was added to Lua globals which is not on the globals allow list nor listed on the deny list.
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 6.2.7 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 9678
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | https://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
9678:X 06 Jun 2023 05:14:07.504 # Sentinel ID is 8dee4b5228ca807e23fa2cbeb6dae863a1b7af8b
9678:X 06 Jun 2023 05:14:07.504 # +monitor master myredis 127.0.0.1 6379 quorum 1
9678:X 06 Jun 2023 05:14:07.504 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
如果此时主机回来了,只能归并到新的主机下,当作从机,这就是哨兵模式的规则!
优点
哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
主从可以切换,故障可以转移,系统的可用性就会更好
哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点
Redis不好在线扩容,集群容量一旦到达上限,在线扩容就十分麻烦!
实现哨兵模式的配置其实是很麻烦的,里面有很多选择
哨兵模式的全部配置
# Example sentinel.conf
# 1、哨兵sentinel 实例运行的端口 默认26379
port 26379
# 2、 哨兵 sentinel 的工作目录
dir "/usr/local/bin"
# 3、哨兵sentinel监控的redis主节点 host port
# - master-name 可以自己对 主节点 明明
# - quorum 配置多少个sentinel 哨兵认为master 主节点失联,那么这个时候就客观的认为失恋了
# sentinel monitor master-name host port quorum
sentinel monitor myredis 127.0.0.1 7371 1
# 4、在Redis实例中开启了密码,这时,所有连接Redis的客户端都需要密码
# - 设置了哨兵sentinel 连接上主从的密码,注意必须设置一样的验证码
# sentinel auth-pass master-name password
sentinel auth-pass myredis 123456
# 5、指定多少毫秒后 主节点没有回答哨兵sentinel 此时 哨兵主观上认为主节点离线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds myredis 30000
# 6、这个配置指定了在发生failover 主备切换时最多可以由多少个slave同时对新的master进行同步
- 这个数字越小,完成failover 所需的时间越长
- 这个数字越大,就意味着越多的slave 因为repkication 而不可用
- 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态
# sentinel parallel-syncs <master-name> <numreplicas>
sentinel parallel-syncs mymaster 1
# 7、故障转移的时间 failover-timeout 可以用一下这些方面
同一个sentinel 对同一个master 两次failover 之间的间隔时间
当想要取消一个正在进行的fai1over所需要的时间,直到slave 被纠正为向正确的master那里同步数据时
当进行fai1over时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,s1aves依然会被正确配置为指向 master,但是就不按para11e1-syncs所配置的规则来了
# sentinel failover-timeout <master-name> <milliseconds> 默认三分钟
sentinel failover-timeout mymaster 180000
# 8、配置当某一个事件发生时需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时,发送
邮件通知相关人员
- 对于脚本的运行结果有以下规则:
1.若脚本执行后返回1,那么该脚本稍后会重新执行,重复次数默认为10
2.若脚本执行后返回2,或者是比2更高的返回值,脚本将不会执行
3.若脚本在执行过程中由于收到系统中断信号被终止了,则同返回值1的时候的相同
4.一个脚本执行的最大时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,重新执行
- 通知型脚本:当sentine1有任何警告级别的事件发生时(比如说re dis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentine1.conf配置文件中配置了这个脚本路路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则 sentine1无法正常启动成功。
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 9、客户端重新配置主节点参数脚本
- 当一个master 发生改变时,这个脚本就会被调用,通知相关的客户端关于 master 地址已经发生改变
- 一下参数将会在调用脚本的时候传给脚本
1. <master-name> <role> <state> <from-ip><from-port><to-ip><to-port>
2. 目前<state>总是"failover”
3. <ro1e>是"leader"或者"observer”中的一个。
4. 参数from-ip,from-port,to-ip,to-port是用来和旧的master和新的master(即旧的s1ave)通信的
5. 这个脚本应该是通用的,能被多次调用,不是针对性的
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
# 1、哨兵sentinel 实例运行的端口 默认26379
port 26379
# 2、 哨兵 sentinel 的工作目录
dir "/usr/local/bin"
# 3、哨兵sentinel监控的redis主节点 host port
# - master-name 可以自己对 主节点 明明
# - quorum 配置多少个sentinel 哨兵认为master 主节点失联,那么这个时候就客观的认为失恋了
# sentinel monitor master-name host port quorum
sentinel monitor myredis 127.0.0.1 7371 1
# 4、在Redis实例中开启了密码,这时,所有连接Redis的客户端都需要密码
# - 设置了哨兵sentinel 连接上主从的密码,注意必须设置一样的验证码
# sentinel auth-pass master-name password
sentinel auth-pass myredis 123456
# 5、指定多少毫秒后 主节点没有回答哨兵sentinel 此时 哨兵主观上认为主节点离线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds myredis 30000
# 6、这个配置指定了在发生failover 主备切换时最多可以由多少个slave同时对新的master进行同步
- 这个数字越小,完成failover 所需的时间越长
- 这个数字越大,就意味着越多的slave 因为repkication 而不可用
- 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态
# sentinel parallel-syncs <master-name> <numreplicas>
sentinel parallel-syncs mymaster 1
# 7、故障转移的时间 failover-timeout 可以用一下这些方面
同一个sentinel 对同一个master 两次failover 之间的间隔时间
当想要取消一个正在进行的fai1over所需要的时间,直到slave 被纠正为向正确的master那里同步数据时
当进行fai1over时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,s1aves依然会被正确配置为指向 master,但是就不按para11e1-syncs所配置的规则来了
# sentinel failover-timeout <master-name> <milliseconds> 默认三分钟
sentinel failover-timeout mymaster 180000
# 8、配置当某一个事件发生时需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时,发送
邮件通知相关人员
- 对于脚本的运行结果有以下规则:
1.若脚本执行后返回1,那么该脚本稍后会重新执行,重复次数默认为10
2.若脚本执行后返回2,或者是比2更高的返回值,脚本将不会执行
3.若脚本在执行过程中由于收到系统中断信号被终止了,则同返回值1的时候的相同
4.一个脚本执行的最大时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,重新执行
- 通知型脚本:当sentine1有任何警告级别的事件发生时(比如说re dis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentine1.conf配置文件中配置了这个脚本路路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则 sentine1无法正常启动成功。
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 9、客户端重新配置主节点参数脚本
- 当一个master 发生改变时,这个脚本就会被调用,通知相关的客户端关于 master 地址已经发生改变
- 一下参数将会在调用脚本的时候传给脚本
1. <master-name> <role> <state> <from-ip><from-port><to-ip><to-port>
2. 目前<state>总是"failover”
3. <ro1e>是"leader"或者"observer”中的一个。
4. 参数from-ip,from-port,to-ip,to-port是用来和旧的master和新的master(即旧的s1ave)通信的
5. 这个脚本应该是通用的,能被多次调用,不是针对性的
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
缓存穿透和雪崩
缓存穿透(查不到)
概念
缓存穿透的概念很简单,用户想要查询一个数据,发现Redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(具体场景:秒杀场景!),于是都去请求持久层数据库。这回给持久层数据库造成很大的压力,这时候就相当于出现缓存穿透。
解决方案
布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数一hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;
缓存击穿(量太大,缓存过期!)
概述
这里需要注意缓存击穿的区别,缓存击穿,是指一个key非常热点,在不听的扛着大并发,大并发集中对这个点进行访问,当这个key在失效的瞬间 ,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿了一个洞
当某个key在失效的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,回同时访问数据库来查询最新的数据,并且回写缓存,回导致数据库瞬间压力过大。
解决方案
设置设点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题
互加斥锁
使用分布式锁,保证对与每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此值需要等待即可。这种方式将高并发的压力转移到分布式锁,因此对分布式锁的考研很大
缓存雪崩
概念
缓存雪崩,是指在某个时间段,缓存集中过期失效。redis宕机
产生雪崩的原因之一,比如在写本文的时候,马上就要双十一零点,很快会迎来一波抢购,这波商品时间比较集中放入缓存,假设缓存一个小时,那么到了凌晨一点钟的时候,这批商品的缓存就过期了,而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用会暴增,造成存储层也会挂掉的情况
解决方式
redis高可用
这个思路的含义是,既然redis有可能挂掉,那我就多增设几台redis,这样一台挂掉之后其他还可以继续工作,其实就是搭建的集群。(异地多话)
限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读取数据库写缓存的线程数量。比如对某个key只允许一个线程查询喝写缓存,其他线程等待
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
Redis系能调优
RabbitMQ
详细队列RabbitMQ,ActiveMQ,Kafka各自优缺点
RabbitMQ
优点
高性能,高可靠性,分布式部署,支持事务,多语言开发,支持多种消息模式,支持SQL筛选,延迟消息等;
缺点
不具备海量数据处理的能力
ActiveMQ
优点
JMS兼容性,支持多种消息模式,支持多种协议,安全性较高,与Spring框架的集成比较好;
缺点
性能的吞吐量相对较低
Kafka
优点
高吞吐量,高性能,高可靠性,分布式部署,支持海量数据和高并发等特性;
缺点
功能相对简单,不适用于事务性强的场景
RabbitMQ简介
简介
1.RabbitMQ是一个开源的消息代理的队列服务器,用来通过普通协议在完全不同的应用之间共享数据。
2.RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AMQP协议的。
Rrlang语言在数据交互仿方面性能优秀,有着和原生Socket一样的延迟,这也是RabbitMQ高性能的原因所在。
AMQP协议:Adcanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,未面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品,开发语言等条件限制。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
特点
1.可靠性:RabbitMQ使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
2.灵活的路由:在消息进入队列之前通过Exchange来路由消息的。对于典型的路由功能,RabbitMQ已经提供了一些内置的Exchange来实现。针对更复杂的路由功能,可以将多个Exchange绑定在一起,也通过插件机制实现自己的Exchange。
3.消息集群:多个RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。
4.高可用:队列可以在集群中的机器上进行镜像,是的在部分结点出问题的情况下队列仍然可用。
5.多种协议:RabbitMQ支持多种消息队列协议,比如STOMP、MQTT等等。
6.多语言客户端:RibbitMQ几乎支持所有常用语言,比如Java、.NET、Ruby等等。
7.界面管理:RabbitMQ提供了一个易用的用户界面,是的用户可以监控和管理消息Broker的许多方面。
8.跟踪机制:如果消息异常,RabbitMQ提供跟踪机制,使用者可以找出发生了什么。
9.插件机制:RabbitMQ提供了许多插件,来从多方面进行扩展,也可以编写自己的插件
RabbitMQ环境搭建
RabbitMQ常用消息模式介绍
简单模式
简单模式是最简单的消息模式,它包含一个生产者、一个消费者和一个队列。生产者想队列里发送消息,消费者从队列中获取消息并消费。
工作模式
gognz 模式是指向多个互相竞争的消费者发送信息的模式,它包含一个生产者、两个消费者和一个队列。连个消费者同时绑定到一个队列上去,但消费者获取消息处理耗时任务时,空闲的消费者冲队列中获取并消费消息
发布/订阅模式
发布/订阅模式是指同时向多个消费者发送信息的模式(类似广播的形式),它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列绑定到交换机上去,生产者通过发送消息到交换机,所有消费者接收并消费消息
路由模式
路由模式时可以更具路由键选择性给多个消费者发送消息的模式,它包含一个生产者,两个消费者,两个队列和一个交换机,两个消费者同时绑定到交换机上去,生产者发送消息到交换机,交换机通过路由键转发到不同队列,队列绑定的消费者接收并消费消息。
通配符模式
通配符模式可以根据路由键匹配规则选择性给多个消费者发送消息的模式,它包含一个生产者,两个消费者,两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列通过路由键匹配规则绑定到交换机上去,生产者发送消息到交换机,交换机通过路由键匹配给这转发到不同队列,队列绑定的消费者接收并消费消息。
RabbitMQ消息发送原理
Spring集成RabbitMQ消息队列
SpringBoot集成RabbitMQ消息队列
RabbitMQ使用场景及面试刨析
解释:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的,高可靠的消息发布于订阅服务
分布式事务
分布式事务产生的原因
分布式事务理论模型
2PC模型
TCC模型
本地事务表
MQ消息事务
GTS集成和使用
TCC实现
在线支付
支付宝支付
微信支付
易宝支付
银联支付
Mycat
Mycat的简介和安装
Mycat架构模型
Mycat概念详解
Mycat主键自增
Mycat读写分离
Mycat分库分表
Mycat性能调优
Mycat权限控制
自定义邮件
实战项目
Dubbo+Zookeeper+SpringBoot+Spring+Mybatis+Mybatis-Plus+Nginx+Redis+RabbitMQ分布式项目实战
微服务
Spring Cloud Alibaba 简介
主要功能
服务限流降级
默认支持WebServlet,WebFlux,OpenFeign,RestTemplate,Spring Cloud Gateway,Zuul,Dubbo和RocketMQ限流降级功能的接入,可以运行时通过控制台实时修改限流降级规则,还支持查看限流降级Metrics监控
服务注册与发现
适配Spring Cloud服务注册与发现标准,默认集成了Ribbon的支持
分布式配置管理
支持分布式系统中的外部化配置,配置更该时自动刷新
消息驱动能力
基于Spring Cloud Stream为微服务应用构建消息驱动能力
分布式事务
使用@GlobalTransactional注解,高效并且对业务零侵入地解决分布式事务问题
阿里云对象存储
阿里云提供的海量,安全,低成本,高可靠的云存储服务,支持在任何应用,任何时间,任何地点存储和访问任意类型的数据
分布式任务调度
提供妙级,精准,高可靠,高可用的定时(基于Cron表达式)任务调度服务。同时提供分布式的任务
阿里云短信服务
覆盖全球的短信服务,友好,高效,智能的互联化通讯能力,帮助企业迅速搭建客户触达通道
服务注册中心
(不推荐)Eureka
Zookeeper
Zookeeper
Zookeeper简介和安装
Zookeeper概念解释
Zookeeper数据模型
Zookeper原生操作Zookeeper
ZClient操作Zookeeper
使用Zookeeper实现配置文件中心
Zookeeper节点类型
Zookeeper分布式锁
Consul
(推荐)Nacos
Nacos服务注册中心
可以实现服务注册中心
服务注册中心
Nacos Server
Nacos Server
它是一个Nacos Server,可以为服务提供者和服务xiao
服务消费者
Nacos Client
Nacos Client
服务提供者
Nacos Client
Nacos Client
具体学习
什么是Nacos
一个更易于构建云原生应用的动态服务发现(Nocos Discovery),配置管理(Nacos Config)和服务管理平台
具体
注册中心
配置中心
服务管理平台
Nacos的关键特性包括:
服务发现和服务健康检测
动态配置服务
动态DNS服务
服务及元数据管理
核心功能
服务注册
Nacos Client会通过发送REST请求的方式向Nacos Serve注册自己的服务,提供自身的元数据,比如ip地址,端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
权重
结合负载均衡器的权重机制,设置的权重(1~100)越大,给的流量就越多
服务心跳
在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除,默认5s发送一次心跳
服务同步
Nacos Server服务集之间会互相同步服务实例,用来保证服务信息的一致性。leader raft
服务发现
服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面注册额服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存
服务健康检查
Nacos Server会开启一个定时任务用来检测注册服务实例和健康情况,对于超过15s没有收到客户端心跳的实例会将它的healthy属性为false(客户端服务时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)
雪崩保护
保护阈值设置值必须在0-1之间
健康实例数 除以 总实例数 < 保护阈值(就会把不健康实例拿出来用)
图解
具体注解
@EnableDiscoveryClient
启动了Nacos的客户端,不加也能运行,方便看
具体搭建
单独部署
Nacos Client搭建
先确定端口,然后注册服务
server:
port: 8020
#应用名称(nacos会将该名称当作服务名称)
spring:
application:
name: order-service
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
name
port: 8020
#应用名称(nacos会将该名称当作服务名称)
spring:
application:
name: order-service
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
name
运行Nacos
D:\Student\SpringCloudAlibabadown\nacos\bin\startup.cmd
集群部署
下载Nacos,版本为1.4.1
https://ghproxy.com/https://github.com/alibaba/nacos/releases/download/1.4.1/nacos-server-1.4.1.tar.gz
应为要部署多个服务,所以要搭建多个服务文件
nacos8849 nacos8850 nacos8851
修改conf\application.properties的配置,使用外置数据源 要使用mysql5.7+(包括5.7)
#*************** Spring Boot Related Configurations ***************#
### Default web context path:
server.servlet.contextPath=/nacos
### Default web server port:
server.port=8849
### Default web context path:
server.servlet.contextPath=/nacos
### Default web server port:
server.port=8849
打开
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=nacos
db.password.0=nacos
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=nacos
db.password.0=nacos
修改或者复制修改conf\cluster.conf.example改为cluster.conf,添加结点配置
ip:port
#it is ip
#example
192.168.120.131:8849
192.168.120.131:8850
192.168.120.131:8851
#example
192.168.120.131:8849
192.168.120.131:8850
192.168.120.131:8851
创建mysql数据库,sql文件位置:conf\nacos-mysql.sql
添加mysql文件
如果出现内存不足:修改启动脚本(bin\startup.sh)的jvm参数
将2g改为512兆
JAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
运行
shutdown.cmd shutdown.sh startup.cmd startup.sh
[root@localhost bin]# ./startup.sh
[root@localhost bin]# ./startup.sh
官方推荐Nginx反向代理
具体代码
application.yaml
server:
port: 8021
#应用名称(nacos会将该名称当作服务名称)
spring:
application:
name: stock-servic-8021
cloud:
nacos:
server-add: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
# - `spring.application.name`代表当前应用的名称;
#- `spring.cloud.nacos.server-addr`代表Nacos服务端的地址;
#- `spring.cloud.nacos.discovery.username`
# `spring.cloud.nacos.discovery.password`代表Nacos服务端的登录用户名和密码。
# 相同的服务特征
port: 8021
#应用名称(nacos会将该名称当作服务名称)
spring:
application:
name: stock-servic-8021
cloud:
nacos:
server-add: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
# - `spring.application.name`代表当前应用的名称;
#- `spring.cloud.nacos.server-addr`代表Nacos服务端的地址;
#- `spring.cloud.nacos.discovery.username`
# `spring.cloud.nacos.discovery.password`代表Nacos服务端的登录用户名和密码。
# 相同的服务特征
服务调用
Ribbon(提供多种负载均衡)
修改默认负载均策略
使用RibbonClients
@RibbonClients(value = {
//这里的name给的是nacos服务名称
@RibbonClient(name = "stock-service",configuration = RibbonRandomRuleConfig.class )
})
//这里的name给的是nacos服务名称
@RibbonClient(name = "stock-service",configuration = RibbonRandomRuleConfig.class )
})
通过权重去设置负载均衡
直接在Nacos设置权重
权重取值范围在(1~10)权重越大,分配到的流量越多(访问人数也就越多)
自定义负载均衡策略
使用轮询
在Aplication中添加
@Bean
// 添加负载均衡的注解
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
RestTemplate restTemplate = builder.build();
return restTemplate;
}
// 添加负载均衡的注解
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
RestTemplate restTemplate = builder.build();
return restTemplate;
}
在实现类中使用
@Autowired
RestTemplate restTemplate;
@RequestMapping("/add")
public String add(){
System.out.println("下单成功");
String msg = restTemplate.getForObject("http://stock-service/stock/reduct",String.class);
return "Hello World"+msg;
}
RestTemplate restTemplate;
@RequestMapping("/add")
public String add(){
System.out.println("下单成功");
String msg = restTemplate.getForObject("http://stock-service/stock/reduct",String.class);
return "Hello World"+msg;
}
添加多服务
在运行中Runing
点击Copy Configration
添加(设置端口)
-Dserver.port=8022
使用权重实现负载均衡
1.在启动类Application外添加一个包
@Configuration
public class RibbonRandomRuleConfig {
//方法名一定要交iRule
@Bean
public IRule iRule(){
return new RandomRule();
}
}
public class RibbonRandomRuleConfig {
//方法名一定要交iRule
@Bean
public IRule iRule(){
return new RandomRule();
}
}
2.在Application.yaml中添加
stock-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
LoadBalancer(只提供轮询负载均衡)
服务调用2
(不推荐)Feign
OpenFeign
快速使用
1.创建一个接口
@FeignClient(name = "stock-service" ,path = "/stock")
public interface StockFeignService {
// 声明需要接用的rest接口对应的方法
@RequestMapping("/reduct")
public String reduct();
}
public interface StockFeignService {
// 声明需要接用的rest接口对应的方法
@RequestMapping("/reduct")
public String reduct();
}
2.在主启动类添加注解
@EnableFeignClients
OpenFeign的日志配置
1.全局配置
使用Configuration会配置作用所有的服务提供方
2.局部配置
使用只想针对一个服务进行配置,就不要加@Configuration
在接口添加 configuration = FeignConfig.class
@FeignClient(name = "stock-service" ,path = "/stock" ,configuration = FeignConfig.class)
public interface StockFeignService {
// 声明需要接用的rest接口对应的方法
@RequestMapping("/reduct")
public String reduct();
}
public interface StockFeignService {
// 声明需要接用的rest接口对应的方法
@RequestMapping("/reduct")
public String reduct();
}
使用配置文件
# springboot默认的日志级别是info,feign的debug日志级别就不会输入
# 只输出com.wu.feign里面的日志
logging:
level:
com.wu.feign: debug
# 只输出com.wu.feign里面的日志
logging:
level:
com.wu.feign: debug
# Feign日志局部配置
feign:
client:
config:
# 使用服务名
product-service:
loggerLevel: BASIC
feign:
client:
config:
# 使用服务名
product-service:
loggerLevel: BASIC
OpenFeign超时时间配置
通过Options可以配置连接时间和读取超时时间,Options的第一个参数是连接的超时间(ms),默认值是2s;第二个请求处理超时时间(ms),默认是5s
全局配置
@Bean
public Request.Options options(){
return new Request.Options(5000,10000);
}
public Request.Options options(){
return new Request.Options(5000,10000);
}
yml中配置
# 连接超时时间,默认2s
connectTimeout: 5000
# 请求处理超时时间,默认5s
readTimeout: 3000
connectTimeout: 5000
# 请求处理超时时间,默认5s
readTimeout: 3000
# Feign日志局部配置
feign:
client:
config:
# 使用服务名
product-service:
loggerLevel: BASIC
# 连接超时时间,默认2s
connectTimeout: 5000
# 请求处理超时时间,默认5s
readTimeout: 3000
feign:
client:
config:
# 使用服务名
product-service:
loggerLevel: BASIC
# 连接超时时间,默认2s
connectTimeout: 5000
# 请求处理超时时间,默认5s
readTimeout: 3000
OpenFeign自定义拦截器
实例
1.编写拦截包
在启动同目录下编写包
public class CustomFeignInterceptor implements RequestInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void apply(RequestTemplate requestTemplate) {
//Todo
requestTemplate.header("xxx","xxx");
requestTemplate.query("id","111");
requestTemplate.uri("/9");
logger.info("feign拦截器");
}
}
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void apply(RequestTemplate requestTemplate) {
//Todo
requestTemplate.header("xxx","xxx");
requestTemplate.query("id","111");
requestTemplate.uri("/9");
logger.info("feign拦截器");
}
}
2.有一个类来进行拦截
@RestController
@RequestMapping("/product")
public class ProductController {
// 端口注入
@Value("${server.port}")
String port;
@RequestMapping("/{id}")
public String get(@PathVariable Integer id) throws InterruptedException {
Thread.sleep(2000);
System.out.println("查询商品"+id);
return "查询商品"+id+":"+port;
}
}
@RequestMapping("/product")
public class ProductController {
// 端口注入
@Value("${server.port}")
String port;
@RequestMapping("/{id}")
public String get(@PathVariable Integer id) throws InterruptedException {
Thread.sleep(2000);
System.out.println("查询商品"+id);
return "查询商品"+id+":"+port;
}
}
3.在application.yaml添加
requestInterceptors[0]:
com.wu.intercptor.feign.CustomFeignInterceptor
com.wu.intercptor.feign.CustomFeignInterceptor
地址添加的是准备拦截的地址
子主题
服务降级
(不推荐)Hystrix
resilience4j
(推荐)sentinel
分布式系统需要的问题
负载不均
缺乏自我保护
缺乏容错机制
线程池满
耦合过重
Load飙高
缓存穿透
消息投递过快,导致消息处理挤压
....
解释:把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性
https://sentinelguard.io/zh-cn/docs/introduction.html
sentinel解决服务雪崩
@SentinelResource
控制台运行
启动jar包
java -jar sentinel-dashboard-1.8.0.jar
个人启动端口备占,换端口
java -Dserver.port=9010-Dcsp.sentinel.dashboard.server=localhost:9010-Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
java -Dserver.port=9010-jar sentinel-dashboard-1.8.0.jar
D:\Student\SpringCloudAlibabadown
配置启动参数
-Dcsp.sentinel.dashboard.server=consoleIp:port
控制台
实时监控
访问时间,访问量,响应时间
簇点链路
流量控制,服务降级,热点,权限
流控规则
使用场景
应对洪峰流浪:秒杀,大促,下单,订单回流处理
消息型场景:削峰填谷,冷热启动
付费系统:根据使用流量付费
API Gateway:精准控制API流量
任何应用:探索应用中运行的满程序块,进行限制
降级规则
热点规则
系统规则
权限规则
集群流控
机器列表
持久化
在Nacos中持久化
1.导入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2.nacos配置中心流控规则
[
{
"resource": "/testA",
"controIBhavior": 0,
"count": 10.0,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
{
"resource": "/testA",
"controIBhavior": 0,
"count": 10.0,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
resource
链接位置
controlBhavior
流控效果
count
阈值
grade
阈流阈值类型,QPS模式(1)或并发线程数模式(0)
limitApp
流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流
stategy
调用关系限流策略:直接,链路,关联
服务网关
(不推荐)Zuul
(可以用,不推荐)Zuul2
(推荐)gateway
直接查看SpringCloud中文文档
必须在同一个group(分组名称)里面
服务配置
(不推荐)Config
(推荐)Nacos
Nacos-config配置中心
服务总线
(不推荐)Bus
(推荐)Nacos
skywalking
分布式链路追踪
启动路径
D:\Student\SpringCloudAlibabadown\apache-skywalking-apm-bin\bin\statup.bat
文件解释
wepapp
UIQ前端(web监控页面)的jar包和配置文件
oap-libs
后台应用的jar包,以及它的依赖jar包,里边又一个server-starter-*.jar就是启动程序
config
启动后台应用程序的配置文件,是使用的各种配置
bin
各种启动脚本,一般使用脚本startup.*来启动web页面和对应的后台应用
oapService.*:默认使用的后台程序的启动脚本;(使用的是默认模式启动,还支持其他模式,个模式区别见,启动模式)
oapServiceinit.*:使用init模式启动;在此模式下,OAP服务启动亿执行初始化工作,然后退出
oapServiceNoInit.*:使用 no init模式启动;在此模式下,OAP服务器不进行初始化。
webappServic.*:UI前端的启动脚本;
statup.*:组合脚本,同时启动oapService.*:webappService.*脚本;
agent
skywalking-agent.jar:代理服务jar包
config:代理服务启动时使用的配置
plugins:包含多个插件,代理服务器时会加载改目录下的所有插件(实际是各种jar包)
optional-plugins:可选插件,当需要支持某种功能时,比如SpringCloud Gateway,则需要吧对应的jar包拷贝到plugins目录下;
启动成功后会启动两个服务,一个是skywalking-oap-server,一个时skywalking-web-ui
skywalking-oap-server服务启动后会暴露11800和12800两个端口,分别为收集监控数据的端口11800和接受前端请求的端口12800,修改端口可以修改config/application.yml
SkyWalking告警功能
告警规则
过去3分钟内服务平均响应时间超过1秒
过去2分钟服务成功率低于80%
过去3分钟内服务响应时间超过1s的百分比
服务实例在过去2分钟内平均响应时间超过1s,并且实例名称与正则表达式匹配
过去2分钟内端点平均响应时间超过1秒
过去2分钟内数据库访问平均响应时间超过1秒
过去2分钟内端点关系平均响应时间超过1秒
告警规则配置项的说明
Rule name:规则名称,也是在告警信息中显示的唯一名称,必须以rule结尾,前缀可自定义
Metrics name:度量名称,取值为oal脚本中的变量名,目前只支持long,double和int类型。
Include names:该规则租用与哪些实体名称,比如服务名,终端名(可选,默认为全部)
Exclude name:该规则不用于哪些实体名称,比如服务名,终端名(可选,默认全部)
Threshold:阈值
OP:操作符,目前支持>,<,=
Pariod:多久警告规则需要被核实一下,这是一个时间窗口,与后端部署环境时间相匹配
Count:在一个Period窗口中,如果values超过Threshold值(按op),打到Count值,需要发送警报
Silence period:在时间N中触发警报后,在TN->TN + period这个阶段不警告。默认情况下,它的Period一样,这意味着相同的警告()在同一个Metrics name拥有相同的id在
告警位置:
D:\Student\SpringCloudAlibabadown\apache-skywalking-apm-bin\config\alarm-settings.yml
优先
数据结构
什么是数据结构
可以理解为一组数据的存储结构
数据结构分类
逻辑结构
数据元素之间逻辑关系的数据结构,这里的逻辑关系是指数据元素的前后间关系,与数据在计算机中的存储的位置无关
线性结构
数据结构的元素之间存在一对一线性关系,所有结点都最多只有一个直接前趋结点和一个直接吼继结点。常见的有数组,队列,链表,栈
非线性结构
各个结点之间具有多个对应关系,一个结点可能有多个直接前趋结点和多个直接吼继结点。常见的有多维数组,广义表,树结构和图结构等
物理结构(存储结构)
指数据的逻辑结构在计算机存储空间中的存放形式称为数据的物理结构,也叫做存储结构
顺序存储
存储顺序是连续的,在内存中用一组地址连续的存储单元依次存储线性表的各个数据元素
链式存储
在内存中的存储元素不一定是连续的,用任意地址的存储单元存储元素,元素结点存放数据元素以及通过指针指向相邻元素的地址信息
索引存储
除建立存储结点信息外,还建立的附加的索引表来表示结点的地址。索引表由若干索引项组成
散列存储
又称Hash存储,由节点的关键码值决定节点的存储地址
常用数据结构
数列(Array)
数组可以说是最基本的数据结构。数组一般用来存储相同类型的数据,可通过数组名和下标进行数据的访问和更新。数组中元素的存储时安装先后顺序进行的,同时在内存中也是按照这个顺序进行连续存放。数组相邻元素之间的内存地址的间隔一般技术数据累心的大小。
队列(Queue)
队列就是栈的兄弟结构,与栈的后进先出对应,队列是一种先进先出相对应,队列是一种先进先出的数据结构。顾名思义,队列的数据存储是如同排队一般,先存入的数据先被压出。常与栈一同配合,可发挥最大的实力
栈(Stack)
栈是一种比较简单数据结构,常用一句话描述其特性,后进先出。栈本省是一个线性表,但是在这个表中是有一个口子允许数据的进出。
栈的常用操作包括入栈push和进栈pop,对应于数据的压入和压出。还有访问栈顶数据,判断栈是否为空和判断栈的大小等。由于栈后进先出的特性,常可以作为数据操作的临时容器,对数据的顺序进行调控,与其它数据结构结合可获得灵活的处理。
栈的常用操作包括入栈push和进栈pop,对应于数据的压入和压出。还有访问栈顶数据,判断栈是否为空和判断栈的大小等。由于栈后进先出的特性,常可以作为数据操作的临时容器,对数据的顺序进行调控,与其它数据结构结合可获得灵活的处理。
树(Tree)
数作为一种树状结构,其数据节点之间的关系也如大叔一样,将有限个节点根据不同层次关系进行排列,从而形成数据与数据之间的父子关系。常见的数的表示形成更接近“倒挂的数”,因为它根朝上,叶朝下。
树的数据存储在节点中,每个节点有零个或者多个子节点。没有父节点的节点在最顶端,成为根节点;没有非根节点有且只有一个父节点;每个非根节点有可以分为多个不相交的子数。
这意味着树具备层次关系的,父子关系清晰,家庭血缘关系明朗;这也是数树与图之间主要的区别。
树的数据存储在节点中,每个节点有零个或者多个子节点。没有父节点的节点在最顶端,成为根节点;没有非根节点有且只有一个父节点;每个非根节点有可以分为多个不相交的子数。
这意味着树具备层次关系的,父子关系清晰,家庭血缘关系明朗;这也是数树与图之间主要的区别。
树衍生了许多的结构,若将指针域设置为双指针,那么即可形成最常见的二叉树,即每个结点最多有凉饿子树的树结构。二叉树结构根据节点的排列和数量还可进一度划分为完全二叉树,满二叉树,平衡二叉树,红黑树等。
完全二叉树
出去最后一层结点,其它层的结点树都达到最大值;同时最后一层的结点都是按照从左到右依次排布
满二叉树
除了最后一层,其它层都结点都有两个子结点
平衡二叉树
平衡二叉树又称为AVL数,它是一棵二叉排序树,具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉排序树:是一棵空树,或者:若它的左子树不空,则左子树上所有结点值均小于它的根结点的值;若它的右子树不空,则右子树所有结点的值均大于它的根节点的值;它的左,右也分别为二叉排序树。
树的高度:结点层次的最大值
平衡因子:左子树高度-右子树高度
平衡二叉树的产生是为了解决二叉排序树在插入时发生线性排列的现象。由于二叉排序树本身为有序,当插入一个有序程度十分高的序列时,生成的二叉排序树会持续在某个方向的子树上插入数据,导致最终的二叉排序树会退化为链表,从而使得二叉树的查询和插入效率恶化。
红黑树
出现的原因:平衡二叉树(AVL)为了最最求高度平衡,需要通过 平衡处理是的左右子树的高度差必须小于等于1.高度平衡带来的好处时能够更高的搜索效率,其最坏的查找时间复杂度都是O(logN)。但是由于需要维持这份高度平衡,所付出的代价即使当对树种结点进行插入和删除时,需要经过多次旋转实现复衡。着导致AVL的插入和删除效率不高。
特点
每个结点要么时红要么时黑的
根节点时黑的
每个叶节点(叶结点即指树尾端NIL指针或NULL结点)都是黑的
如果一个几点是红的,那么它的两个儿子都是黑的
对于任意结点而言,其到叶节点树尾端NIL指针的每条路径都包含相同数目的黑结点
红黑树 VS 平衡二叉树
除了上面提及的树结构,还有许多应用在数据库,磁盘存储等场景下的树结构。比如B树,B++树等。
散列表(Hash)
散列表也叫哈希表,是一种通过键值对直接访问数据的机构。在初中,我们就徐国一种能够将一个x值通过一个函数获得对应的一个y值的操作,叫做映射。散列表的是心啊原理正是映射的原理,通过设定的一个关键字和一个映射函数,就可以直接获得访问数据的地址,是心啊0(1)的数据访问效率。在映射过程中,事先设定的函数就是一个映射表,也可以称作散列函数或者哈希函数
散列表的实现最关键的就是散列函数的定义和选择。一般常用的有一下几种散列函数:
直接寻址发
取关键字的某个线性函数值为散列地址。
数字分析发
通过对数据的分析,发现数据中冲突较少的部分,并构造散列地址。例如同学们的学号,通常同一届学生的学号,其中前面部分差别不太大,所以用后面的部分来构造散列地址。
平方取中法
当无法确定关键字里哪几位的分布相对比较均匀时,可以先求出关键字的平方值,然后按照需要取平方值的中间几位作散列地址。这时因为:计算平方之后的中间几位和关键字中的每一位都相关,所以不同的关键字会以比较高的概率生产不同的散列地址。
取随机数法
使用一个随机函数,取关键字的随机值作为散列地址,这种方式通常用于关键字长度不同的场合。
除留取余法
取关键字被某个不大于散列表的表长n的数m除后所得的余数p为散列地址。这种方式也可以用过其他方法后再使用。该函数对m的选着很重要,一般取素数或者直接用n。
确定确定好散列函数之后,通过某个key值的确会得到一个唯一的value地址。但是却会出现一些特殊情况。即通过不同的key值可能会访问到同一个地址,这个现象称之为冲突
冲突再发生之后,当再对不同的key值进行操作时会是的造成相同的数据发生覆盖或者丢失,时非常危险的,所以再设计散列表往往还需要采用冲突解决的办法
常用处理方式有很多,常用的包括一下几种
冲突再发生之后,当再对不同的key值进行操作时会是的造成相同的数据发生覆盖或者丢失,时非常危险的,所以再设计散列表往往还需要采用冲突解决的办法
常用处理方式有很多,常用的包括一下几种
开放地址法(也叫开放寻址法)
实际上就是当需要存储值时,对key哈希之后,发现指责个地址已经有值了,这时该怎么办?不能放在这个地址,不然之前的映射会被覆盖。这时对计算出来的地址进行一个探测再哈希,比如往后移动一个地址,如果没人占用,就用这个地址。如果超过最大长度,则可以对总长度取余。这里移动的地址时产生冲突时的增列序量
再哈希法
再产生冲突之后,使用关键字的其他部分继续计算地址,如果还有冲突,则继续使用其他部分再计算地址。这种方式的缺点时时间增加了
链地址法
链地址法其实就是对Key通过哈希之后落在同一个地址上的值,做一个链表。其实在很多高级语言的实现当中,也是使用这种方式处理冲突的
公共溢出区
这总方式时建立一个公共溢出区,当地址存在冲突时,把新的地址放在公共溢出区里。
堆(Heap)
堆通常是一个可以被看做一棵树的数组对象。堆的具体实现一般不通过指针域,而是通过构建一个一维数组与二叉树的父节点对应,因此堆总是一棵完全二叉树。
对于任意一个父节点的序号n来说(这里n从o算),它的子节点的序号一定是2n+1,2n+2,因此可以直接用数组类表示一个堆。
堆中某个结点的值总是不大于或小于其父节点的值。将根结点最大的堆叫做最大堆或打根堆,根结点最小的堆叫做最小堆或笑根堆
堆常用来实现优先队列,在面试中经常考的问题都是排序有关,比如堆排序有关,比如堆排序,topK问题等。由于堆的根节点是序列中最大或者最小值,因而可以在建堆以及重建堆的过程中,筛选除数据序列中的极值,从而达到排序或者挑选topK值的目的。
图(Graph)
图机构一般包括顶点和边,顶点通常用圆圈来表示,边就是这些圆圈之间的连线。边还可以根据顶点之间的关系设置不同的额权重,默认权重相同皆为1.此外根据边的方向性,还可以将图分为有向图和无向图。
还有更多如邻接矩阵,邻接表,逆邻接表,十字链表等
链表(Linked LIst)
链表相较于数组,除了数据域,还增加了指针域用于构建链式的存储数据。链表中每一个节点都包含此节点的数据和指向下一节点地址的指针。由于是通过指针进行下一个元素的查找和访问,使得链表的自由度更高。
这表现在对节点进行增加和删除是,只需要对上一节点的指针地址尽心修改,二无需变动其它的节点。不过十五皆有两级,指针带泪高自由度的同时,自然会牺牲数据查找的效率和多余空间的使用
一般常见的是有头有尾的单链表,对指针域进行反向链接,还可以形成双向链表或者循环链表。
这表现在对节点进行增加和删除是,只需要对上一节点的指针地址尽心修改,二无需变动其它的节点。不过十五皆有两级,指针带泪高自由度的同时,自然会牺牲数据查找的效率和多余空间的使用
一般常见的是有头有尾的单链表,对指针域进行反向链接,还可以形成双向链表或者循环链表。
链表和数组对比
跳表
从链表和数组对比可以看出,链表虽然通过增加指针域提升了自由度,但是却导致数据的查询效率恶化。特别是当链表长度很长的时候,对数据的查询还是得从头依次查询,这样的效率会更低。跳表的产生就是为了解决链表过长的问题,通过增加链表的多级索引来加快原始链表的查询效率。这样的方式可以让查询的时间复杂度从O(n)提升至O(logn)
J2EE
J2EE是什么
从整体上讲,J2EE 是使用 Java 技术开发企业级应用的工业标准,它是 Java 技术不断适应和促进企业级应用过程中的产物。
适用于企业级应用的 J2EE,提供一个平台独立的、可移植的、多用户的、安全的和基于标准的企业级平台,从而简化企业应用的
开发、管理和部署。J2EE 是一个标准,而不是一个现成的产品。
适用于企业级应用的 J2EE,提供一个平台独立的、可移植的、多用户的、安全的和基于标准的企业级平台,从而简化企业应用的
开发、管理和部署。J2EE 是一个标准,而不是一个现成的产品。
J2EE主要技术
基本
servlet
Servlet是java平台上的CGI技术。Servlet在服务器端运行,动态地生成Web页面。与传统的CGI和许多其他
类似CGI的 技术相比,Java Servlet具有更高的效率并更容易使用。
对于Servlet,重复的请求不会导致同一程序的多次转载,它是依靠线程的方式来支持并发访问的。
JSP(Java server Page)
JSP是一种实现普通静态HTML和动态页面输出混合编码的技术。从这一点来看,非常类似Microsoft ASP、
PHP等技术。借助形式上的内容和外观表现的分离,Web页面制作的任务可以比较方便地划分给页面设计人员,并
方便地通过JSP来合成。在运行时态,JSP将会被首先转换成Servlet,并以Servlet的形态编译运行,因此它的效
率和功能与Servlet相比没有差别,一样具有很高的效率。
JDBC
JDBC(java Database Connectivity,java数据库连接)API是一个标准SQL(Structured Query
Language, 结构化查询语言)数据库访问接口,它使数据库开发人员能够用标准java API编写数据库应用程序。
JDBC API主要用来连接数据库和直接调用SQL命令执行各种SQL语句。利用JDBC API可以执行一般的SQL语句、
动态SQL语句以及带IN和OUT参数的存储过程。Java中的JDBC相当于Microsoft平台中的ODBC(open Database Connectivity)
EJB
EJB定义了一组可重用的组件:Enterprise Beans。开发人员可以利用这些组件,像搭积木一样建立分布式应用
Servlet是java平台上的CGI技术。Servlet在服务器端运行,动态地生成Web页面。与传统的CGI和许多其他
类似CGI的 技术相比,Java Servlet具有更高的效率并更容易使用。
对于Servlet,重复的请求不会导致同一程序的多次转载,它是依靠线程的方式来支持并发访问的。
JSP(Java server Page)
JSP是一种实现普通静态HTML和动态页面输出混合编码的技术。从这一点来看,非常类似Microsoft ASP、
PHP等技术。借助形式上的内容和外观表现的分离,Web页面制作的任务可以比较方便地划分给页面设计人员,并
方便地通过JSP来合成。在运行时态,JSP将会被首先转换成Servlet,并以Servlet的形态编译运行,因此它的效
率和功能与Servlet相比没有差别,一样具有很高的效率。
JDBC
JDBC(java Database Connectivity,java数据库连接)API是一个标准SQL(Structured Query
Language, 结构化查询语言)数据库访问接口,它使数据库开发人员能够用标准java API编写数据库应用程序。
JDBC API主要用来连接数据库和直接调用SQL命令执行各种SQL语句。利用JDBC API可以执行一般的SQL语句、
动态SQL语句以及带IN和OUT参数的存储过程。Java中的JDBC相当于Microsoft平台中的ODBC(open Database Connectivity)
EJB
EJB定义了一组可重用的组件:Enterprise Beans。开发人员可以利用这些组件,像搭积木一样建立分布式应用
补充
1. javaEE应用的分层模型大致分为以下几层:
(1)Domain Object(领域对象)层:
此层由一系列的POJO(Plain Old Java Object,普通的、传统的java
对象)组成,这些对象是该系统的Domain Object,往往包含了各自所需实现的业务逻辑方法。
(2)DAO(Data Access Object,数据访问对象)层:此层由一系列的DAO组件组成,这些DAO实现了对数据库的创建、查询、更新和删除(CRUD)等原子操作。
(3)业务逻辑层:此层由一系列的业务逻辑对象组成,这些业务逻辑对象实现了系统所需要的业务逻辑方法。这些
业务逻辑方法可能仅仅用于暴露Domain Object对象所实现的业务逻辑方法,也可能是依赖DAO组件实现的业务
逻辑方法。
(4)控制器层:此层由一系列控制器组成,这些控制器用于拦截用户请求,并调用业务逻辑组件的业务逻辑方法,
处理用户请求,并根据处理结果转发到不同的表现层组件。
(5)表现层:此层由一系列的JSP页面,Velocity页面,PDF文档视图组件组成,负责收集用户请求,并显示处理结果。
(1)Domain Object(领域对象)层:
此层由一系列的POJO(Plain Old Java Object,普通的、传统的java
对象)组成,这些对象是该系统的Domain Object,往往包含了各自所需实现的业务逻辑方法。
(2)DAO(Data Access Object,数据访问对象)层:此层由一系列的DAO组件组成,这些DAO实现了对数据库的创建、查询、更新和删除(CRUD)等原子操作。
(3)业务逻辑层:此层由一系列的业务逻辑对象组成,这些业务逻辑对象实现了系统所需要的业务逻辑方法。这些
业务逻辑方法可能仅仅用于暴露Domain Object对象所实现的业务逻辑方法,也可能是依赖DAO组件实现的业务
逻辑方法。
(4)控制器层:此层由一系列控制器组成,这些控制器用于拦截用户请求,并调用业务逻辑组件的业务逻辑方法,
处理用户请求,并根据处理结果转发到不同的表现层组件。
(5)表现层:此层由一系列的JSP页面,Velocity页面,PDF文档视图组件组成,负责收集用户请求,并显示处理结果。
2. javaEE应用组件:
JavaEE构架提供了良好的分离,隔离了各组件之间的代码依赖,javaEE应用大致包括以下几类组件:
(1)表现层组件:主要负责收集用户输入数据,或者向客户显示系统状态。最常用的表现层技术是JSP,还可以是Velocity等技术。
(2)控制器组件:对于JavaEE的MVC框架而言,框架提供一个前端核心控制器,而核心控制器负责拦截用户请求,并将请求转发
给用户实现的控制器组件。而这些用户实现的控制器则负责处理调用业务逻辑方法,处理用户请求。
(3)业务逻辑组件:是系统的核心组件,实现系统的业务逻辑。通常一个业务逻辑方法对应一次用户操作。一个业务逻辑方法应该
是一个整体,因此要求对业务逻辑方法增加事务性。业务逻辑方法仅仅负责实现业务逻辑,不应该进行数据库访问。因此,业务逻辑
组件中不应该出现原始的Hibernate,JDBC等API。
(4)DAO组件:Data Access Object,也被称为数据访问对象。这个类型的对象比较缺乏变化,每个DAO组件都提供Domain
Object 对象基本的创建、查询、更新和删除等操作,这些操作对应于数据表的CURD等原子操作。当然,如果采用
JavaEE构架提供了良好的分离,隔离了各组件之间的代码依赖,javaEE应用大致包括以下几类组件:
(1)表现层组件:主要负责收集用户输入数据,或者向客户显示系统状态。最常用的表现层技术是JSP,还可以是Velocity等技术。
(2)控制器组件:对于JavaEE的MVC框架而言,框架提供一个前端核心控制器,而核心控制器负责拦截用户请求,并将请求转发
给用户实现的控制器组件。而这些用户实现的控制器则负责处理调用业务逻辑方法,处理用户请求。
(3)业务逻辑组件:是系统的核心组件,实现系统的业务逻辑。通常一个业务逻辑方法对应一次用户操作。一个业务逻辑方法应该
是一个整体,因此要求对业务逻辑方法增加事务性。业务逻辑方法仅仅负责实现业务逻辑,不应该进行数据库访问。因此,业务逻辑
组件中不应该出现原始的Hibernate,JDBC等API。
(4)DAO组件:Data Access Object,也被称为数据访问对象。这个类型的对象比较缺乏变化,每个DAO组件都提供Domain
Object 对象基本的创建、查询、更新和删除等操作,这些操作对应于数据表的CURD等原子操作。当然,如果采用
Docker
解释:Docker是一个用来装应用的容器,就像杯子可以装水,笔筒可以放笔,书包可以放书,可以把hello word放在Docker中,可以把网站放入Docker中,可以把任何想得到的程序放到Docker中
https://blog.csdn.net/q610376681/article/details/90483576?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166933825816800192297604%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166933825816800192297604&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-90483576-null-null.142^v66^js_top,201^v3^add_ask,213^v2^t3_control1&utm_term=docker&spm=1018.2226.3001.4187
https://blog.csdn.net/q610376681/article/details/90483576?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166933825816800192297604%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166933825816800192297604&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-90483576-null-null.142^v66^js_top,201^v3^add_ask,213^v2^t3_control1&utm_term=docker&spm=1018.2226.3001.4187
了解Doker
集装箱
没有集装箱之前运输货物,东西零散容易丢失,有了集装箱之后货物不容易丢失,我们可以把货物想象成程序,目前我们要把程序部署到一台新的机器上,可能会启动不起来,比如少一些配置文件什么的或者少了什么数据,有了docker的集装箱可以保证我们的程序不管运行在哪不会缺东西.
标准化
运输方式
docker运输东西有一个超级码头,任何地方需要货物都由鲸鱼先送到超级码头,然后再由鲸鱼从超级码头把货物送到目的地去.对应的技术来说,比如我们要把台式机的应用部署到笔记本上,我们可能选择用QQ发过去或者用U盘拷过去,docker就标准化了这个过程,我们只需在台式机上执行一个docker命令,把鲸鱼派过来,把程序送到超级码头去,再在笔记本上执行一个docker命令,然后由鲸鱼把程序从超级码头送到笔记本上去.
存储方式
当我们把程序存储到笔记本上时,我们需要一个目录,且我们要记住这个目录,因为下次我们可能还要修改,有了docker之后我们就不用记住了程序在哪里了,我们使用的时候只需要一条命令就行了.
API接口
docker提供了一系列rest api的接口,包含了对docker也就是对我们的应用的一个启动停止查看删除等等,如当我们要启动tomcat时我们要执行startup命令,当我们要停止时要执行shutdown命令,如果不是tomcat,我们可能还需要一些别的命令.有了docker我们记docker的命令就可以对其进行操作.
隔离
我们在使用虚拟机时有自己的cpu,硬盘,内存,完全感觉不到外面主机的存在,docker也差不多,不过它更轻量,我们创建虚拟机可能要几分钟,但是docker只需要一秒.最底层的技术时linux一种内核的限制机制,叫做LXC,LXC是一种轻量级的容器虚拟化技术.最大效率的隔离了进程和资源.通过cgroup,namespace等限制,隔离进程组所使用的物理资源,比如CPU,MEMORY等等,这个机制在7,8年前已经加入到linux内核了,直到2013年docker出世的时候才火起来,大家可能奇怪为什么这么好的技术埋没这么多年都没人发现呢?英雄造时势,时势造英雄,如果没有云计算,敏捷开发,高频度的弹性伸缩需求,没有IT行业这么多年长足的发展,也就没有docker.
Docker解决的问题
系统环境不一致
开发:我本地没问题.运维:服务器没问题. 这个问题就变成了皮球.
如果一个应用要正常的启动起来需要什么?比如java web应用.
需要一个操作系统,操作系统之上要jdk,tomcat,我们的代码,配置文件.
操作系统的改变可能会导致我们的应用开不起来,比如我们调用了某些系统命令.
jdk版本也可能导致程序的运行失败.比如class文件需要1.7编译,我们装了个1.6的jdk.
tomcat版本也能导致失败,比如旧的版本一些配置在新版本中不再支持.
代码的话就比如应用了C盘,D盘的一个文件,或者是用了系统的一些环境编码.
配置的话我们可能少了某个配置文件等等.
下面docker来了,它把操作系统,jdk,tomcat,代码,配置全部放到集装箱里.再打包放到鲸鱼上,由鲸鱼给我们送到服务器上,在我的机器上怎么运行,在别的机器上也怎么运行.不会有任何的问题.一句话就是docker解决了运行环境不一致所带来的问题.
如果一个应用要正常的启动起来需要什么?比如java web应用.
需要一个操作系统,操作系统之上要jdk,tomcat,我们的代码,配置文件.
操作系统的改变可能会导致我们的应用开不起来,比如我们调用了某些系统命令.
jdk版本也可能导致程序的运行失败.比如class文件需要1.7编译,我们装了个1.6的jdk.
tomcat版本也能导致失败,比如旧的版本一些配置在新版本中不再支持.
代码的话就比如应用了C盘,D盘的一个文件,或者是用了系统的一些环境编码.
配置的话我们可能少了某个配置文件等等.
下面docker来了,它把操作系统,jdk,tomcat,代码,配置全部放到集装箱里.再打包放到鲸鱼上,由鲸鱼给我们送到服务器上,在我的机器上怎么运行,在别的机器上也怎么运行.不会有任何的问题.一句话就是docker解决了运行环境不一致所带来的问题.
系统好卡,哪个哥们又写是死循环了
如果有根别人共用服务器的同学可能有这样的体会,莫名其妙发现自己的程序挂了,一查原因要不是内存不够了,要不是硬盘满了,还有就是发现某个服务变慢了,甚至敲终端都比较卡,但是linux本身就是一个多用户的操作系统本身就可以供多个用户使用,docker的隔离性可以解决这个问题,就算别人的程序还是死循环疯狂吃CPU,还是封装疯狂打日志把硬盘占满,还是内存泄漏,把内存占满,都不会导致我们的程序运行错误.因为docker在启动的时候就限定好了,它最大使用的CPU硬盘,如果超过了,就会杀掉对应进程.
双11来了,服务器撑不住
大部分系统业务量并不是每天都比较平均的,特别是一些电商系统,每天总有那么几天业务量是平时的几倍甚至几十倍,如果按双11的规模去准备服务器那么对于平时的规模来说又是极大的浪费,所以就在节日前临时扩展机器,过完节再把多余的节点下线,这就给运维带来了非常大的工作量,一到过节就在各个机器上部署各种各样的服务,我们启动程序需要java,tocmat等等,并且还可能起不来还要调试,这是非常恶心的工作,有了docker一切都变得美好了,只要点一下服务器就可以从10台变成100台甚至1000,1W台.都是分分钟的事情.
为什么会这么快呢?都是用标准的方式把我们的程序运过来,下载过来,再用标准的方式把它运行起来,就可以做到只要在每台机器上都执行一两条命令,就可以让程序正常跑起来,并且不用担心有问题.
为什么会这么快呢?都是用标准的方式把我们的程序运过来,下载过来,再用标准的方式把它运行起来,就可以做到只要在每台机器上都执行一两条命令,就可以让程序正常跑起来,并且不用担心有问题.
走进Docker
镜像就是上面说的集装箱,仓库就是超级码头,容器就是我们运行程序的地方.docker运行程序的过程就是去仓库把镜像拉到本地,然后用一条命令把镜像运行起来变成容器.
build:构建,就是构建镜像.
ship:运输,运输镜像,从仓库和主机运输.
run:运行的镜像就是一个容器.
build,ship,run和镜像,仓库,容器是一一对应的.
build:构建,就是构建镜像.
ship:运输,运输镜像,从仓库和主机运输.
run:运行的镜像就是一个容器.
build,ship,run和镜像,仓库,容器是一一对应的.
镜像
镜像的英文名交image.前面我们讲到了集装箱,鲸鱼拖着的所有集装箱就是一个镜像.
从本质上来说镜像就是一系列文件,可以包括我们应用程序的文件,也可以包括我们应用的运行环境的文件,既然是文件,那么是以什么样的格式在本地保存的呢?
说到存储格式,就要提到linux的一个存储技术,叫做联合文件系统,是一种分层的文件系统,可以将不同的目录挂到同一个虚拟文件系统下.
从本质上来说镜像就是一系列文件,可以包括我们应用程序的文件,也可以包括我们应用的运行环境的文件,既然是文件,那么是以什么样的格式在本地保存的呢?
说到存储格式,就要提到linux的一个存储技术,叫做联合文件系统,是一种分层的文件系统,可以将不同的目录挂到同一个虚拟文件系统下.
容器
为了便于理解,大家可以把容器想象成虚拟机,每个虚拟机都有自己的文件系统,可以把图1整个一部分看成是文件系统,与虚拟机系统的区别是这里面的文件系统是一层一层的,并且最下面的n层都是只读的,只有上面一层是可写的.为什么要有可写的这层呢?大家的程序运行起来,势必会要写一些日志,写一些文件,或者对系统的某一些文件做一些修改,所以容器在最上面一层创建了可读可写的文件系统.
在程序的运行过程中,如果要写镜像文件时,因为镜像的每一层都是只读的,它会把文件的每一层拷到文件的最上层,然后再对它进行修改,修改之后,当我们的应用读一个文件时会从顶层进行查找,如果没有才会找下一层.
由于容器的最上一层是可以修改的,镜像是不能修改的,这样就能保证镜像可以生成多个容器独立运行,没有任何干扰.
在程序的运行过程中,如果要写镜像文件时,因为镜像的每一层都是只读的,它会把文件的每一层拷到文件的最上层,然后再对它进行修改,修改之后,当我们的应用读一个文件时会从顶层进行查找,如果没有才会找下一层.
由于容器的最上一层是可以修改的,镜像是不能修改的,这样就能保证镜像可以生成多个容器独立运行,没有任何干扰.
仓库
我们的镜像是要在其它机器上运行,如何进行传输呢?
这就用到了docker仓库,我们要先把我们的镜像传到docker仓库中,再由目的地把docker仓库拉过去,这就完成了这样的一次传输过程.
谁提供了这样的仓库呢?docker自己提供了,hub.docker.com,但是非常慢,为了解决这个问题,国内很多公司也在做自己的仓库.比较知名的是由网易蜂巢提供的 https://c.163yun.com/hub#/m/home/
这就用到了docker仓库,我们要先把我们的镜像传到docker仓库中,再由目的地把docker仓库拉过去,这就完成了这样的一次传输过程.
谁提供了这样的仓库呢?docker自己提供了,hub.docker.com,但是非常慢,为了解决这个问题,国内很多公司也在做自己的仓库.比较知名的是由网易蜂巢提供的 https://c.163yun.com/hub#/m/home/
Docker具体学习
入门Docker
Docker安装和卸载
安装
官方文档特别详细
https://docs.docker.com/engine/install/centos/
卸载
官方也有
卸载目录
sudo yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
删除docker资源
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd
sudo rm -rf /var/lib/containerd
Docke启动关闭
systemctl start docker
启动Docker
docker version
显示docker的版本信息
docker info
查看docker的系统信息,包括镜像和容器的数量
docker 命令 --help
帮助命令
Docker具体命令
镜像基本命令
docker images -a
显示文件
docker images -aq
只显示id
docker search mysql
搜索镜像名字为mysql
docker rearch mysql --filter=STARS=3000
搜索STARS大于3000的
docker pull mysql
下载镜像mysql,默认最新版
doker pull mysql:5.7
下载指定版本5.7
docker rmi -f 4f06b49211c0
删除镜像id为“4f06b49211c0”
docker rmi -f 4f06b49211c0 3kljlkfj83334
删除多个镜像
docker rmi -f $(docker images -aq)
删除镜像容器
容器基本命令
有了镜像才能创建容器,下载centos镜像
新建容器并启动
docker run [可选参数] image
docker run -it -p 8080:8080 tomcat
启动并进入容器
docker run -it centos /bin/bash
启动容器centos进入到/bin/bash目录下
docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticse arch:7.6.2
下载并启动elasticsearch
elasticsearch十分耗内存
docker run -d --name nginx01 -p 3344:80
启动nginx,命名为nginx01,暴露nginx80端口映射到本机的3344端口
进入已启动的容器
docker exec -it nginx01 /bin/bash
进入名字为nginx01的容器,进入路径/bin/bash
用完就删除
nginx01也可以改为使用image id
列出当前正在运行的容器
docker ps
列出当前正在运行的容器+带出历史运行过的容器
docker ps -a
显示最近创建的容器
docker ps -n=1
显示最近一个创建的容器
只显示容器的编号
docker ps -aq
只显示历史运行过的容器编号
退出容器不停止运行
Ctrl + P + Q
退出容器
exit
通过id删除容器
docker rm acd92fc1e7e5
删除容器的id为“acd92fc1e7e5”(不能删除正在运行的容器,如要删除)
删除全部容器
docker rm -f $(docker ps -aq)
删除所有的容器
通过管道符删除所有容器
docker ps -a -q|xargs docker rm
暂停运行的容器
docker kill 容器id
启动和停止的操作
docker start 容器id
启动容器
docker restart 容器id
重启容器
docker stop 容器id
停止当前正在运行的容器
docker kill 容器id
强制停止当前容器
常用的其他命令
后台启动
docker run -d 镜像名
问题
docker ps,发现 centtos 停止了
docker 容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止
常见的坑 nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了
启动tomecat可以使用这个
查看日志
docker logs -f -t --tail 4c6ba3bbbe60
查询id为“4c6ba3bbbe60”日志
显示指定行数的日志
docker logs -tf -tail 10 2cf36e62ac43
查看id为“2cf36e62ac43”的容器显示最近的10跳
自己编写一段shell脚本
docker run -d centos /bin/sh -c "while true;do echo kuangshen;sleep 1;done"
查看容器中的进程信息
docker top 2cf36e62ac43
查看容器“2cf36e62ac43”的i进程
查看容器的全部信息
docker inspect 2cf36e62ac43
查看容器id为“2cf36e62ac43”的全部信息
进入当前正在运行的容器(进入容器后开启一个新的终端,可以在里面操作(常用))
docker exec -it 2cf36e62ac43 bashShell
docker exec -it 2cf36e62ac43 /bin/bash
进入当前正在执行命令的容器(进入容器正在执行的终端,不会启动新的进程)
docker attach 2cf36e62ac43
从容器内拷贝文件到主机上
docker cp 容器id:容器路径 目的路径
docker cp 58e290c363f3:/home/test.java /home
-d 后台运行
-p 端口映射
-v 卷挂载
-e 环境配置
--name 容器名字
-p 端口映射
-v 卷挂载
-e 环境配置
--name 容器名字
docker安装nginx
搜索nginx
docker search nginx
使用elasticsearch
docker run -d --name elasticsearch02 -p 9200:9200 -p 9200:9200 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticse arch:7.6.2
加上Java限制最小64M,最大512M,防止开发太大,服务器承载不懂
操作命令
docker --help
...
底层原理
Docker是一个Client-Servier结构的系统
Docker镜像
镜像是什么
镜像是一个轻量级,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某些如软件所需的所有内容,包括代码,运行时,库,环境变量和配置文件
所有的应用,直接打包docker镜像,就可以直接跑起来
如何得到镜像
从远程仓库下载
朋友拷贝给你
自己制作一个镜像DockerFile
Commit镜像
提交容器成为一个新的副本
提交容器成为一个新的副本
docker commit
提交用户名
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
docker commit -a="JavaWu" -m="add webapps app" f87bc6431e53 tomcat02:1.0
实战
启动一个默认的tomcat
启动一个默认的tomcat 是没有webapps应用,镜像的原因,官方的镜像默认webapps下面是没有文件的
自己拷贝进去了基本文件
将我们操作过的容器通过commit提交为一个镜像! 我们以后就使用我们修改过的镜像即可,这就是我们自己的一个修改的镜像
Docker可视化界面
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
内网用9000
外网用8088
容器数据卷
思考?
我们每次改动nginx配置文件,都需要进入容器内部?十分的麻烦,我要是可以在容器外部提供一个映射路径,达到在容器修改文件名,容器内部就可以自动修改? -v 数据卷?
具体使用
docker run -it -v /home/ceshi:/home centos /bin/bash
外部文件使用/home/ceshi内部文件/home 启动centos容器进入/bin/bash页面
查看是否挂载(在外部查看是否挂载)
docker inspect 614049b8739
docker inspect 614049b8739
"Mounts": [
{
"Type": "bind",
"Source": "/home/ceshi",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
{
"Type": "bind",
"Source": "/home/ceshi",
"Destination": "/home",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
Docker实战Mysql
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
获取镜像
docker pull mysql:7.5
运行容器,需要做数据挂载 #安装启动mysql,需要配置密码,这是要注意点!
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
启动mysql
docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
-d 后台运行
-p 端口映射
-v 卷挂载
-e 环境配置
--name 容器名字
-p 端口映射
-v 卷挂载
-e 环境配置
--name 容器名字
实现多个mysql实现数据共享
docker run -d -p 3310:3360 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-form mysql01 mysql:5.7
这个时候,可以实现两个容器数据同步!
docker run -d -p 3310:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-form mysql01 mysql:5.7
这个时候,可以实现两个容器数据同步!
结论
容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止
DockerFile
初识DockerFile
什么是DockerFile
Dockerfile就是用来构建docker镜像的构建文件!命令脚本!先体验一下!
通过这个脚本可以生成一个镜像,镜像十一层一层的,脚本一个个的命令,每个命令都是一层!
步骤
先写脚本
#创建一个dockerfile文件,名字可以随机,建议Dockerfile
#文件种的内容 指定(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "-----end----"
CMD /bin/bash
#这里的每个命令,就是镜像的一层!
#文件种的内容 指定(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "-----end----"
CMD /bin/bash
#这里的每个命令,就是镜像的一层!
自动挂载
构建镜像
docker build -f /home/docker-test-volume/dockerfile1 -t kuangshen/entos:1.0 .
这种方式我们未来使用的十分多,因为我们通常会构建自己的镜像!
假设构建镜像时候美誉挂载卷,要手动挂载, -v 卷名:容器路径!
假设构建镜像时候美誉挂载卷,要手动挂载, -v 卷名:容器路径!
Docker File构建过程
dockerfile是面向开发的,我们以后要发布项目,就做镜像,就需要编写Dockerfile文件,这个文件十分简单!
Docker镜像逐渐成为企业交付的标准,必要掌握!
DockerFile:构建文件,定义了一切的步骤,源代码
DockerImages:通过DockerFile构建生成的镜像,最终发布和运行的产品!
Docker容器:容器就是镜像运行起来提供服务器
DockerFile的指令
基础镜像,一切从这里开始构建
FROM
镜像是谁写的,姓名+邮箱
MAINTAINER
镜像构建的时候需要运行的命令
RUN
步骤,tomacat镜像,这个tomcat压缩包!添加内容
ADD
镜像的工作目录
WORKDIR
挂载的目录
VOLUME
保留端口配置
EXPOSE
指定这个容器启动的时候要运行的命令
CMD
指定这个容器启动的时候要运行的命令,可以追加命令
ENTRYPOINT
当构建一个被继承 DockerFile这个时候就会运行 ONBUILD 的指令,触发指令,
ONBUILD
类似ADD,将我们文件拷贝到镜像中
COPY
构建的时候设置环境变量
ENV
实战测试
Docker Hub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建
自己创建一个centos
第一步构建Dockerfile文件
FROM centos
MAINTAINER wuzhiguo<AndyJava@proton.me>
ENY MYPATH /usr/local
WORKDIR MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
MAINTAINER wuzhiguo<AndyJava@proton.me>
ENY MYPATH /usr/local
WORKDIR MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
第二步通过这个文件构建镜像
docker build -f mydockerfile -t mycentos:0.1 .
docker build -f dockerfile 文件路径 -t 镜像名:[tag]
构建成功
Successfully built id
Successsfully tagged mycentos:0.1
第三步测试运行
docker run id
实战:Tomcat镜像
Docker网络原理
IDEA整合Docker
Docker Compose
Docker Swarm
CI\CDjenkins
高并发
什么是高并发
响应时间
系统对请求做出响应时间。
吞吐量
单位时间内处理的请求数量
QPS
每秒请求数量
并发用户数
同一时间用户使用数量
如何解决高并发
分布式缓存
redis,memcached等
消息队列中间件
RabbitMQ,activeMQ等等,解决大量消息异步处理能力
应用拆分
一个工程被拆分多个工程部署,利用dubbo解决多工程之间的通信
数据库垂直拆分和水平拆分(分库分表)等。
还可以利用nosql例如mongoDB配合mysql 组合使用
还需要建立大数据访问情况下的服务降级以及限流等机制等。(sentinel进行限流)
ASP.NET MVC
第一部分(第1章~第5章):
第二部分(第6章~第10章):
第三部分(第11章~第12章):
第四部分(第13章):
第五部分(第14章~第16章):
第六部分(第17章):
多线程
消息队列
REST
JVM调优
Git
Ajax
Kibana
elasticsearch
portainer
是Docker图形化界面管理工具
青鸟(使用SSM框架开发企业级应用)
第一部分(第1~4章):
子主题
第二部分(第5~8章):
第三部分(第9~12章):
第四部分(第13章):
第五部分(第14章):
次要
算法
GC算法
递归算法
分治算法
贪心算法
动态规划
枚举算法
理解TCP/IP网络协议
外部文件OSI,了解
HSTS
作用
降低用户第一次访问请求被恶意拦截的风险
前提条件
执行该操作前,请确保已成功配置HTTPS证书
具体操作
https://help.aliyun.com/document_detail/95839.html
Typora
子主题
计算机网络
中科大 郑烇
计算机组成原理
华中科技大学 秦磊华
操作系统
哈尔冰工业大学 李治军
软件快捷键
IDEA
ctrl+shift+/
多行注释
ctrl+/
单行注释
时间线(从上到下,依次学习)
Typora
Docker
容器技术
基础
完成
进阶
已学到31章,后续进阶学习从SpringAlibaba学习在开始
Nginx学习
反向代理
Linux的命令
完成完成
SpringAlibaba Nacos的学习
SpringAlibaba 接下来课程的学习
学习nacos,会使用反向代理,使用方向代理,快捷部署,使用docker
Mybatis -plus
Redis
负载均衡算法
Git
JVM调优
理解TCP/IP网络协议
多线程
高并发
Ajax
spring Security
GeoServer
0 条评论
下一页