赞
踩
一个.java源文件中可以定义多个类
但只能有一个public修饰的类 并且此类的名字必须和源文件名字一致
每个类在编译时 都会生产一个自己的.class字节码文件
相同之处: 1:都可以作为逻辑运算符 表示逻辑与 and
2:作为逻辑运算符时结果一致:两边只要有一个false 结果就是false
不同之处: 1:&& 具有逻辑短路功能:a&&b 当a为false时 不再判断b 直接得到结果false
2: & 还可以作为位运算符,表示按位与操作
//当sum>100时结束循环
int sum=0;
a:for (int i = 0; true ; i++) {//给外层for循环定义一个标签:起个名字
for (int j = 0; j<=10 ; j++) {
sum+=j;
if(sum>100){
break a;//结束a标记的循环
}
}
}
System.out.println("sum="+sum);
//当sum>100时结束循环
int sum = 0;
for (int i = 0; true; i++) {
boolean b = true;
for (int j = 0; j < 10; j++) {
sum += i;
if (sum > 100) {
b = false;
break;//结束内层循环
}
}
if (!b) {
break;//结束外层循环
}
}
1:switch仅限于穷举法: 不适用于判断范围
2:switch后面表达式的值类型:仅限于 char, byte, short, int, Character, Byte, Short, Integer, String, or an enum
3: case 后面的值不能重复
4: 只有所有的case都与结果不一致时 才执行default
5:break表示结束switch
s1=s1+1;语法错误 需要强制类型转换:等号左边是short变量 等号右边是int值
s1+=1;语法正确 +=是赋值运算符:编译器会自动类型转换
相同之处:
equals方法和==都可以判断两个引用是否指向同一个对象
不同之处:
1:类型不同
==是比较运算符
equals是方法
2:结果是否可以更改
== 只能判断连个引用是否为同一个对象 不能被重写
equals方法可以被重写
3:是否可以操作基本数据类型
== 可以操作基本数据类型 用于判断基本数据类型的值是否相同
equals不能操作基本数据类型
相同之处:都表示整数
不同之处:
1:类型不同
int是基本数据类型
integer是引用数据类型
2:默认值不同
integer类型的成员变量默认值是null
int默认值是0
3:定义不同
Integer i;是引用 用于存储一个Integer对象的内存地址
int i;是基本类型变量 用一个存储一个int常量值
注意:Integer i1=n; Integer i2=n; 当n为[-128,127]之间 i1,i2为同一个对象
在[-128,127]之外 i1,i2为不同对象
关键字 名称 作用域
public: 公共的 整个项目
private: 私有的 当前类
protected:受保护的 本包和其他包的子类
不写: 默认的 当前包
概念: Overload是方法重载 Override的是方法重写/覆盖 重载:一个类中的几方法 方法名相同,参数列表不同的现象 重写:子类重新定义父类已有的方法 相同之处: 对应都是方法/并且都要求方法名相同 不同之处: 1要求不同: 重载要求:方法名必须相同 参数列表必须不同(参数个数/参数类型/参数顺序) 重写要求:子类重写父类的方法:方法名、参数列表、返回值类型必须相同 范围修饰符可以扩大,抛出的异常可以少/小 2位置不同 重载:对应的是同一个类中的几个同名的方法 重写:对应的是子类中的方法 3影响不同 重载:重载的方法互相之间没有影响:只是调用时 不能通过方法名区分 只能通过参数列表来区分 重写:子类重写父类的A方法: 子类从父类继承的A方法将会被隐藏
继承:子类继承父类 是类的复用 子类会继承父类中定义的所有实例成员
关键字extends
实现:子类重写父类的抽象方法/实现类"继承"接口
关键字implements
1:java只支持类与类的单继承:一个子类只能有一个直接的父类
2:Object类是整个类结构的超类:一个类如果不继承其他类 默认继承Object类
3:java支持接口之间的多继承
4:Java支持一个类可以实现多个接口 并同时继承一个直接父类
不同之处:
final关键字 是一个修饰符 用于修饰:类/方法/变量 表示最终的不可变的
finally用于在处理异常时候的一部分语句【不关闭虚拟机此线程不被打断】这段代码一定执行
finalize是Object类中的一个方法 用于系统回收垃圾
静态变量:被static修饰的成员变量
实例变量:没有被static修饰的成员变量
相同之处:都是成员变量 都可以被对象调用
不同之处:
1:修饰符不同
是否被static修饰符修饰
2:调用不同
静态变量 可以被对象调用 也可以被类直接调用
实例变量 只能被对象调用
3:加载的时间不同
静态变量在类加载时加载
实力变量在对象创建时才加载
4:作用范围不同
静态变量是共享数据 所有对象共用一个
实例变量是每个对象都拥有一个仅属于自己的实例变量
abstract class是抽象类 interface是接口 相同之处:都可以包含抽象方法/都不能实例化/都可以用于定义引用 不同之处: 1 有无构造方法: abstract class是类 有构造方法 interface是接口 没有构造方法 2 成员是否有默认修饰符 abstract class类的成员没有默认修饰符 interface的成员变量默认修饰符 public static final interface的成员方法默认修饰符 public abstract 3 是否可以包含非抽象方法 abstract class可以包含非抽象方法 interface的非抽象方法必须加修饰符default 4 使用不同 子类继承abstract class通过关键字extends 并且只能单继承 实现类实现interface 通过给关键字implements 并且可以多实现
封装: 封装是将数据和代码捆绑到一起,对象的某些数据和代码可以是私有的,不能被外界直接访问,以此实现对数据和代码不同级别的访问权限。有效实现了:对数据和行为的包装和信息隐藏 类是对描述同一类事物的数据和功能的封装 方法是对实现指定功能的代码块的封装 私有化是对数据的封装,实现数据访问权限的可控 封装作用:实现代码复用 和 数据安全 继承: 继承实现类的复用: A类继承B类 A类中无需定义 即可拥有B类中定义的所有成员 并且A类可根据需求修改和添加成员 继承作用:用于描述类之间的关系 实现类的复用 减少代码冗余 多态: 多态:父类类型的引用指向子类类型对象。让不同内部结构的子类对象共用相同类型的父类引用,达到运行时多样化的效果 多态性允许每个对象以适合自身的方式去响应共同的消息。 多态性增强了软件的灵活性和重用性。 抽象: 抽象:分析一类事物时 抽取共同的部分 忽略无关的内容 当类通过方法描述功能时:方法声明一样 但实现功能的方法体不一致时 可以定义为抽象方法(即只定义方法声明 添加abstract修饰符 方法体让子类根据自己的需求来实现)
参考:https://www.runoob.com/w3cnote/java-inner-class-intro.html 概念:A类定义在B类中 A类就是B类的内部类 内部类分类: 成员内部类:A类作为B类的实例成员 静态内部类:A类作为B类的static成员 局部内部类:A类定义在B类的方法体/构造代码块中 匿名内部类:在B类中定义的无名的内部类 class B{ class A1{}//成员内部类 static class A2{}//静态内部类 void hehe(){ class A3{}//局部内部类 C A4=new C(){//匿名内部类 public void hai(){} }; } } interface C{ void hai(); }
Math.floor(a) 取小于等于参数的最大整数
Math.ceil(a) 取大于等于参数的最小整数
Math.round(a) 四舍五入
Math.rint(a) 四舍六入 五取偶
Math.round(11.5) =12 Math.round(-11.5) =11
String类是final类 不能被继承
String对象是字符串常量:一旦创建 字符序列不能更改
String对象的创建:通过new调用构造方法 或者通过""
如果是通过""创建的字符串对象 会保存在字符串常量池中
字符串常量池特点:相同序列的字符串常量只创建一次
String s=new String("123");创建了两个字符串常量
先通过"123"在字符串常量池中创建一个123 在通过new在内存中创建一个"123"的副 本对象
String进行字符串连接运算时 会生成大量的字符串对象(每拼凑一次就创建一个新的对象)
如:String s="123"+"a"+"11"+"c"; 整个过程共创建7个对象
char charAt(int index) 获取指定位置的字符。 int compareTo(String anotherString) int compareToIgnoreCase(String str) 字符串比较 String concat(String str) 字符串拼凑 boolean contains(CharSequence s) 判断是否包含 boolean equals(Object anObject) boolean equalsIgnoreCase(String anotherString) 判断字符序列是否一致 byte[] getBytes(String charsetName) 获取字节数组 boolean isEmpty() 判断是否为空 int indexOf(x) int lastIndexOf(x) 获取子串/字符的位置 int length() 获取长度 boolean matches(String regex) 正则匹配。 String replace(char oldChar, char newChar) 替换 String[] split(String regex) 切割 boolean endsWith(String suffix) boolean startsWith(String prefix) 判断开头/结尾 String substring(int beginIndex) String substring(int beginIndex, int endIndex) 截取子串 char[] toCharArray() 转换为字符数组 String toLowerCase() 所有字母小写 String toUpperCase() 所有字母大写 String trim() 去除两边的空格
相同之处:都可以存储字符串/都是final类 不能被继承
不同之处:
String是字符串常量:字符序列不能更改
StringBuffer、StringBuilder是字符串缓冲区:字符序列可以更改
StringBuffer和StringBuilder的区别
相同之处: 都是字符串缓冲区
API兼容
不同之处: 版本不同: StringBuffer jdk1.0
StringBuilder jdk1.5
线程是否安全:StringBuffer 线程安全效率低
StringBuilder 线程不安全 效率高
Error类和Exception类都是继承Throwable类
Error(错误)是系统中的错误,是在程序编译时出现的严重错误,程序员只能通过修改代码才能
恢复正常。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方 法调用栈溢,语法错误等。
Exception(异常)是程序正常运行过程中可以预料到的意外情况,开发者可以通过代码逻辑来
实现异常的捕获和处理。
都是Exception的子类
运行时异常又叫未检查异常 编译时异常又叫已检查异常
不同之处:
1 是否继承RuntimeException
运行时异常直接或者间接继承了RuntimeException
编译时异常没有继承RuntimeException
2 编译器是否检查
编译器会检查出所有可能产生的编译时异常 此类异常如果没有捕获必须抛出
编译器不会检查运行时异常:运行时异常如果没有捕获 默认抛出
3 出现的频率不同
编译时异常一般时在特定情况下才会出现 出现概率比较低
运行时异常出现的概率比较高
java通过继承Throwable类的异常体系来封装和描述程序出现的异常情况
在方法体中通过try-catch实现对异常处理
在方法声明上通过throws实现对异常对象的传递
java的异常处理有两种机制:
抛出机制:在方法声明上添加(throws 异常类) 来 实现异常对象的传递 用于声明当前方法可
能产生的异常,以及把产生的异常对象抛给方法的调用者
捕获机制:在方法体中通过try{可能产生异常的代码块}catch(异常引用e){处理异常e的代码}
来实现对指定类型异常的捕获处理
throws 用在方法声明上,声明该方法有可能会抛出那些异常 以及把产生的异常对象抛给方
法的调用者
throw 用在方法体中,用于主动产生一个异常情况
try 用于指定要处理的可能产生异常的代码块
catch 用于对try代码块捕获的异常对象 并指定处理异常的代码块
finally 用于指定无论是否有异常都会执行的代码块
常见的运行时异常
ArrayIndexOutOfBoundsException 数组索引越界异常。
ArithmeticException 算术异常
NullPointerException 空指针异常
IllegalArgumentException 非法参数异常
NumberFormatException 数字格式异常
ClassCastException 类型转换异常
常见的编译时异常
EOFException: 达到文件末尾异常
IOException:io异常
SQLException:sql异常
ParseException:字符串解析异常
ClassNotFoundException: 找不到类异常
IllegalAccessException: 执行权限异常
集合和数组都是引用数据类型/都是可以装多个数据的容器
不同之处:
1 元素长度是否可变
数组对象一旦创建 元素个数不能更改
集合是动态扩容的 容量可以根据元素需求动态更改
2 元素类型是否可变
数组对象初始化时 必须指定类型 并且只能装此指定类型的元素
集合中元素类型是可以不同的(如果是泛型集合 就只能装指定类型的元素)
3 元素是否有默认值
数组对象创建时 元素如果不赋值 都要对应类型的默认值
集合中的元素没有默认值
4 是否可以装基本数据类型
数组可以定义为装引用数据类型的数组也可以装基本数据类型数组
集合只能装引用数据的元素
Collection:单列集合顶层接口
List:有序集合,允许相同元素和null
LinkedList:双向链表非同步,允许相同元素和null,遍历效率低插入和删除效
率高
ArrayList:非同步,允许相同元素和null,实现了动态大小的数组,遍历效率 高,用的多
Vector:同步,允许相同元素和null,效率低
Stack:继承自Vector,实现一个后进先出的堆栈
Set:不允许相同元素的集合,最多有一个null元素
HashSet:无序集合,不允许相同元素,最多有一个null元素
TreeSet:有序set,使用元素的自然顺序对元素进行排序
Map:双列(key-value)集合顶层接口 key不能重复,value可以重复
Hashtable:同步,不允许null作为key和value
HashMap: 非同步,允许null作为key和value
内部都是长度可变数组的实现/都是List有序列表的实现类
不同之处:
1:线程是否安全
ArrayList:线程不安全 效率高 Vector:线程安全 效率低
2:版本不同
Vector是JDK1.0 ArrayList是JDK1.2
3:扩容原则不同
当元素个数大于当前当前集合长度时:Vector容量会增加1倍 ArrayList会增加0.5
倍
都是List列表的实现类/都是线程不安全的
不同之处:
1.底层不同:
ArrayList 底层是数组,LinkedList 底层是双向链表
2.占内存不同:
LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,
还存储了相邻元素的两个引用
3.效率不同:
当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高,因为
LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
当对数据进行增加和删除的操作(add和remove操作)时,LinkedList比ArrayList的
效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有
数据的下标索引造成影响,需要进行数据的移动。
创建线程有四种方式
class Test{ public static void main(String[] args) { //3 :创建线程对象 MyThread01 t1=new MyThread01(); //4 :开启线程 t1.start();// 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 } } //1 :创建类继承Thread类 class MyThread01 extends Thread{ //2:重写run方法: 封装的线程任务 public void run() { for (int i = 0; i < 100; i++) { try {Thread.sleep(20);} catch (Exception e) {} System.out.println(Math.random()+"--"+i); } } }
class Test{ public static void main(String[] args) { // 3 : 创建实现类对象 MyThread02 mt=new MyThread02(); // 4 : 创建Thread对象 通过构造方法关联实现类对象 Thread t1=new Thread(mt); // 5 : 开启线程 t1.start(); } } // 1 : 创建类实现Runnable接口 class MyThread02 implements Runnable{ //2 :实现run方法 public void run() { for (int i = 0; i < 100000; i++) { try {Thread.sleep(20);} catch (Exception e) {} System.out.println(Math.random()+"--"+i); } } }
class Test{ public static void main(String[] args)throws Exception { //3 创建Callable的实现类对象 MyCall mc=new MyCall(); //4 创建FutureTask对象和实现类对象关联 FutureTask task1=new FutureTask(mc); //5 创建线程对象与futuretask关联 Thread t1=new Thread(task1, "线程111"); //6开启线程 t1.start(); //获取线程任务的返回值 System.out.println("sum1="+task1.get()); } } //1 创建Callable的实现类 class MyCall implements Callable<Integer>{ //2 实现call方法 public Integer call() throws Exception {// 随机100个int 求和 int sum=0; for (int i = 0; i <100; i++) { int n=(int)(Math.random()*10); Thread.sleep(20); sum+=n; System.out.println(i+" "+n); } return sum; } }
class Test{ public static void main(String[] args)throws Exception { //3 通过Executors的newFixedThreadPool方法创建线程池对象 ExecutorService pool=Executors.newFixedThreadPool(3); //4 创建runnable实现类对象 MyRun mr=new MyRun(); //5调用池对象execute方法执行任务:execute方法调用几次 就开启几个线程 pool.execute(mr); pool.execute(mr);pool.execute(mr); //6 关闭线程池 pool.shutdown(); } } //1 创建runnable接口的实现类 class MyRun implements Runnable{ //2 实现run方法 public void run(){// 随机100个int for (int i = 0; i <100; i++) { int n=(int)(Math.random()*10); try {Thread.sleep(20);} catch (Exception e) {} System.out.println(Thread.currentThread().getName()+" "+i+" "+n); } } }
有多种方式:
1 通过同步方法实现
2 通过同步代码块实现 synchronized
3 通过修饰符 volatile
4 通过ReentrantLock类
5 通过ThreadLocal类
代码参考:https://www.cnblogs.com/zeroingToOne/p/9554560.html
参考:https://blog.csdn.net/xingjing1226/article/details/81977129
都可以实现当前线程中止运行
不同之处:
1 sleep是Thread类的方法
wait是Object类的方法
2 sleep(long)方法调用 导致程序暂停执行指定的时间,让出cpu给其他线程,但其监
控状态依然保持着,当指定的时间到了又会自动恢复运行状态。
wait()方法调用 线程会放弃锁对象,进入此锁对象的等待队列,只有此锁对象调用了
notify/notifyAll方法后本线程才进入准备状态
3 sleep不会释放锁对象 wait会释放锁对象
4 sleep可以在任意位置调用 wait只能在同步代码块中调用
jdk:java development kits :java开发工具包
jre:java runtime environment: java运行环境
jvm:java Java Virtual Machine: java虚拟机
jdk=jre+java开发工具
jre=jvm(java.exe)+java基础类库+其他
1.1 接口新增默认方法与静态方法
1.2 新增Lambda表达式
1.3 新增方法引用
1.4 重复注解
1.5 扩展注解的支持
参考:https://blog.csdn.net/jun55xiu/article/details/93201032
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存
Java的栈(Stack)用于存储基本数据类型、引用名、方法
优势:存取速度快,仅次于寄存器,栈数据可以共享。
缺点:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
java的堆(Heap)用于存放由new创建的对象和数组
堆中分配的内存,由java虚拟机自动垃圾回收器来管理
虚拟机jvm会不定时的启动垃圾回收器对象,垃圾回收器会根据对象是否存在更改引用 来判断对象是否为垃圾,如果为垃圾 就调用对象的finalize()方法 销毁对象 释放内存
程序员可以通过System.gc()方法来手动启动垃圾回收器 来干涉垃圾回收机制
参考:https://www.cnblogs.com/Andya/p/7272462.html
https://blog.csdn.net/qq_38950316/article/details/81087809
按方向分: 输入流: xxxInPutStream/xxxReader 输出流: xxxOutPutStream/xxxWriter 按操作基本单位分类: 字符流:xxxWriter/xxxReader 字节流:xxxStream 按关联对象分类: 节点流:关联的是文件对象File 过滤流:关联的是流对象 字节流顶层接口:InputStream/OutputStream 常用的实现类:FileInputStream/FileOutputStream 字符流顶层接口:Reader/Writer FileReader/FileWriter 转换流: InputStreamReader/OutputStreamWriter 高效流: BufferedInputStream/BufferedOutputStream BufferedReader/BufferedWriter 序列化: ObjectInputStream(反序列化流)/ObjectOutputStream(序列化流)
设计模式分为三种类型,共23种:
创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、
代理模式
行为型模式:访问者模式、模板方法模式、责任链模式、命令模式、迭代器模式、
观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式
解释:参考https://www.cnblogs.com/pony1223/p/7608955.html
//饿汉模式
class Single{
private Single(){}
private static Single s=new Single();
public Single getInstance(){return s;}
}
//懒汉模式
class Single{
private Single(){}
private static Single s;
public synchronized Single getInstance(){
if(s==null){s=new Single();}
return s;
}
}
2 工厂模式:A类的对象通过B类的方法来创建
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
class A{}
class AFactory{
public static A getInstance(){//静态工厂方法
return new A();
}
public A getInstance(){//实例工厂方法
return new A();
}
}
interface A{
void haha();
}
class Target implements A{ //目标对象
public haha(){System.out.print(11);}
}
class Proxy implements A{
private A a;
public Proxy(A a){this.a=a;}
public haha(){
System.out.print("pre代码");
a.haha();
System.out.print("suf代码");
}
}
1:基本选择器: #id、.class、*、elementName、
2:层次选择器: > 、 + 、 ~
3:基本过滤选择器: :first、:last、:not、:even、:odd、 :eq、:gt、 :lt、
:header、 :animated
4:内容过滤选择器: :contains、 :empty、 :has、 :parent
5: 可见性过滤选择器: :hidden、 :visible
6: 属性过滤选择器: [attrName] [=、!=、^=、$=、*=]
7: 子元素过滤选择器: :nth/first/last/only-child
8: 表单过滤选择器: :input、:text ...
9: 表单对象属性过滤选择器: :enabled、:disabled :checked、:selected
attr():获取/设置属性
css():获取/设置css样式
html():获取/设置html代码
val():获取/设置表单组件的值
text():获取/设置文本
show()/hide():显示/隐藏
bind()/unbind():绑定/解除事件
append()/remove():添加/删除事件
ready():文档加载
相关的有五个:$.ajax()\load()\$.get()\$.getJSON()\$.getScript()\$.post();
常用1: $.ajax();参数是json对象
async:是否异步
cache:是否缓存
contentType:请求的内容编码类型
data:请求参数
dataType:期待响应数据类型
success:成功后的回调函数
type:请求方式
url:请求资源的路径
常用2:$.get(url, [data], [callback], [type]);
常用3:$.post(url, [data], [callback], [type]);
1、输入网址
2、DNS解析
3、建立tcp连接
4、客户端发送HTPP请求
5、服务器处理请求
6、服务器响应请求
7、浏览器展示HTML
8、浏览器发送请求获取其他在HTML中的资源(如js/css/img)。
js是基于对象和事件驱动的客户端脚本语言
java是面向对象的后台程序开发高级语言
不同之处:
1:js是基于对象 java是面向对象
2:js是弱类型语言 java是强类型语言
3:js是解释性语言 java是编译型语言
4:js用于实现html的动态效果 java用于实现后台程序的编程
servlet的方法有五个: void service(ServletRequest arg0, ServletResponse arg1) void destroy() ServletConfig getServletConfig() String getServletInfo() void init() 和servlet生命周期相关的方法: init、service、destroy servlet生命周期四个阶段: 1创建: 默认情况下tomcat在接收到对当前servlet的第一次请求时 会创建此servlet的对象 如果在servlet的配置信息中设置了load-on-startup标签 tomcat开启时就创建当前 servlet的对象(Servlet是单例 至始至终只有一个对象) 2初始化:--init方法 Tomcat创建完servlet对象后 就立刻调用对象的init方法对servlet进行初始化 3服务: --service方法 客户端每请求一次servlet tomcat就调用一次servlet对象的servcie方法: 来接受请求 处理数据 给予响应 4销毁: –destroy方法 Tomcat关闭之前会调用servlet对象的destroy方法 来销毁对象
方式1:实现接口:javax.servlet.Servlet接口
方式2:继承抽象类javax.servlet.GenericServlet 实现service方法
方式3:继承类javax.servlet.http.HttpServlet 重写doGet和doPost方法
J2EE平台由一整套服务(Services)、应用程序接口(APIs)和协议构成,它对开发基于Web的多层应用提供了功能支持,下面对J2EE中的 13种技术规范进行简单的描述 JDBC(Java Database Connectivity):统一访问数据库的API JDBC:访问不同的数据库提供了一种统一的途径,象ODBC一样,JDBC对开发者屏蔽了一些细节问题,另外,JDCB对数据库的访问也具有平台无关性。 JNDI(Java Name and Directory Interface):名字和目录命名服务 JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源如DNS和LDAP,本地文件系统,或应用服务器中的对象。 EJB(Enterprise JavaBean):基于分布式事务处理的企业级应用程序的组件 J2EE技术之所以赢得媒体广泛重视的原因之一就是 EJB。它们提供了一个框架来开发和实施分布式商务逻辑,由此很显著地简化了具有可伸缩性和高度复杂的企业级应用的开发。EJB规范定义了EJB组件在何时如何与它们的容器进行交互作用。容器负责提供公用的服务,例如目录服务、事务管理、安全性、资源缓冲池以及容错性。但这里值得注意的是,EJB并不是实现J2EE的唯一途径。正是由于J2EE的开放性,使得有的厂商能够以一种和EJB平行的方式来达到同样的目的。 RMI(Remote Method Invoke):远程对象方法的调用 正如其名字所表示的那样,RMI协议调用远程对象上方法。它使用了序列化方式在客户端和服务器端传递数据。 RMI是一种被EJB使用的更底层的协议。 Java IDL/CORBA:系统集成 在Java IDL的支持下,开发人员可以将Java和CORBA集成在一起。他们可以创建Java对象并使之可在CORBA ORB中展开, 或者他们还可以创建Java类并作为和其它ORB一起展开的CORBA对象的客户。后一种方法提供了另外一种途径,通过它Java可以被用于将你的新的应用和旧的系统相集成。 JSP(Java Server Pages):服务器端页面 JSP页面由HTML代码和嵌入其中的Java代码所组成。服务器在页面被客户端所请求以后对这些Java代码进行处理,然后将生成的HTML页面返回给客户端的浏览器。 Java Servlet:接受请求处理数据的小程序 Servlet是一种小型的Java程序,它扩展了Web服务器的功能。作为一种服务器端的应用,当被请求时开始执行,这和CGI Perl脚本很相似。Servlet提供的功能大多与JSP类似,不过实现的方式不同。JSP通常是大多数HTML代码中嵌入少量的Java代码,而 servlets全部由Java写成并且生成HTML。 XML(Extensible Markup Language):数据存储 XML是一种可以用来定义其它标记语言的语言。它被用来在不同的商务过程中共享数据。 XML的发展和Java是相互独立的,但是,它和Java具有的相同目标正是平台独立性。通过将Java和XML的组合,您可以得到一个完美的具有平台独立性的解决方案。 JMS(Java Message Service):消息通信 MS是用于和面向消息的中间件相互通信的应用程序接口(API)。它既支持点对点的域,有支持发布/订阅 (publish/subscribe)类型的域,并且提供对下列类型的支持:经认可的消息传递,事务型消息的传递,一致性消息和具有持久性的订阅者支持。JMS还提供了另 一种方式来对您的应用与旧的后台系统相集成。 JTA(Java Transaction Architecture):事务监控 JTA定义了一种标准的API,应用系统由此可以访问各种事务监控。 11. JTS(Java Transaction Service): JTS是CORBA OTS事务监控的基本的实现。JTS规定了事务管理器的实现方式。该事务管理器是在高层支持Java Transaction API (JTA)规范,并且在较底层实现OMG OTS specification的Java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信资源管理器提供了事务服务。 JavaMail:邮件发送 JavaMail是用于存取邮件服务器的API,它提供了一套邮件服务器的抽象类。不仅支持SMTP服务器,也支持IMAP服务器。 JAF(JavaBeans Activation Framework):邮件附件 JavaMail利用JAF来处理MIME编码的邮件附件。MIME的字节流可以被转换成Java对象,或者转换自 Java对象。大多数应用都可以不需要直接使用JAF。
必须说上来的:jdbc servlet jsp javamail jta jms xml ejb
HttpServletRequest作用:
1 获取请求参数
2 获取请求头
3 作为域对象
4 请求转发/请求包含
HttpServletResponse作用
1 设置响应头
2 设置响应体
3 重定向
cookie:是http规范的由服务器端创建,在客户端保存的,通过请求头和响应头传递的键值对文
本信息
session: 是服务器用于保存客户端信息而创建的回话对象,其依赖于名为JSESSIONID的
cookie
cookie和session都可以解决http协议的无状态的回话跟踪技术
不同之处:
1 cookie保存在客户端浏览器中 session保存在服务器端
2 cookie相对不安全,可以通过cookie分析就行cookie欺骗
3 session会占用服务器的性能
4 cookie保存数据有限4k 并且个数有限一个站点最多20个
HyperText Transfer Protocol,超文本传输协议
基于请求响应模式的无状态互联网通信协议 包括请求协议和协议协议
请求协议:请求首行 请求头 请求体
1 GET请求在URL中传送的参数是有长度限制的,而POST没有。
2 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
3 GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
4 GET请求参数保存在请求协议的请求首行,POST请求保存在请求体中
a请求转发到b a请求包含到b 相同之处 请求转发和请求包含 都是一个请求被两个资源处理 1 由于a和b处理的是同一个请求: a和b执行方法相同(get/post) 2由于a和b处理的是同一个请求: a和b都可以获取请求参数 3由于a和b处理的是同一个请求:可以通过request域对象实现数据共享 不同之处: 1: 代码不同:请求转发是调用调度器对象的forward方法 请求包含是include方法 请求转发:req.getRequestDispatcher(“/xxx”).forward(req,resp); 请求包含:req.getRequestDispatcher(“/xxx”).include(req,resp); 2: 对响应头的设置 请求转发:a和b都可以设置响应头 请求包含:a可以设置响应头 b不能 3:对响应体的设置 请求转发:b可以设置响应体 a不能 请求包含:a和b都可以设置响应体
重定向和请求转发的区别 1:方法不同 请求转发:req.getRequestDispatcher(“/xxx”).forward(req,resp); 重定向:resp.sendRedirect("/xxxx"); 2:请求次数不同 请求转发是一个请求被两个资源处理 可以使用request域 重定向是两个请求被两个资源处理 不能使用request域 3:地址栏显示不同: a请求转发到b 地址栏显示a a重定向到b 地址栏显示b 4:状态码不同: 请求转发:200 重定向是302 5:b处理请求的方式不同 请求转发: b处理请求的方式取决于请求的method 重定向: b 一定走的是doget方法 6:请求转发只能转发到当前项目的资源 重定向不仅仅限于当前项目
一、状态码大类
2xx 成功——表示请求已经被成功接收、理解、接受。
3xx 重定向——要完成请求必须进行更进一步的操作
4xx 客户端错误——请求有语法错误或请求无法实现
5xx 服务器端错误——服务器未能实现合法的请求。
二、常见状态码
200 OK //客户端请求成功,即处理成功,这是我们最希望看到的结果
302 Other //重定向
304 Not Modified //使用缓存
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在
500 Internal Server Error //服务器错误
jsp标签分两类:jsp指令标签和jsp动作标签
jsp指令标签格式:
<%@page %>:设置页面的属性
<%@include %>:静态包含
<%@taglib %>:引入标签库
jsp动作标签:
<jsp:include />:动态包含
<jsp:forward />:请求转发
<jsp:userBean/>:创建对象并设置为域属性
<jsp:setProperty/>:设置域属性
<jsp:getProperty/>:获取域属性
九大内置对象:是jsp对应的servlet的service方法体中可以直接使用的九个对象
request :请求对象 HttpServletRequest
response:响应对象 HttpServletReponse
session:回话 HttpSession
pageContext: page上下文对象 PageContext
application: 项目上下文对象 ServletContext
out:响应字符输出流 Writer
page:当前jsp页面 HttpServlet
config:配置信息 ServletConfig
exception:异常对象 Throwable (只存在isErrorPage=true的页面)
11个内置对象方便el表达式操作Jsp有关的所有数据
1 pageScope : 获取pageContext域属性
2 requestScope: 获取request域属性
3 sessionScope: 获取session域属性
4 applicationScope:获取application域属性
5 param: 获取请求单值请求参数
6 paramValues: 获取多值请求参数
7 header: 获取单值请求头
8 headerValues: 获取多值请求头
9 cookie :获取cookie
10 initParam :获取web,xml中配置的初始化参数
11 pageContext: 获取pageContext对象
参考:https://www.cnblogs.com/wenxiaofei/p/9853682.html
事务是对数据库中一系列操作进行统一的回滚或者提交的操作,主要用来保证数据的完整性和一致性。
事务是一个/多个dml语句组成一个执行单元
原子性(Atomicity):
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
一致性(Consistency):
事务无论成功还是失败 前后总数据必须一致:数据库的完整约束性不被破坏
隔离性(Isolation):
多个并发事务之间要相互隔离
持久性(Durability):
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的。
从理论上来说, 事务应该彼此完全隔离, 以避免并发事务所导致的问题,然而, 那样会对性能产生极大的影响, 因为事务必须按顺序运行, 在实际开发中, 为了提升性能, 事务会以较低的隔离级别运行, 事务的隔离级别可以通过隔离事务属性指定。
事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致。
3、幻读:幻读解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。
例如:事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作 这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。 而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有跟没有修改一样,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
解决事务并发问题 通过设置事务的隔离级别
事务隔离级别
读未提交Read Uncommitted:另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据脏读
不可重复读Read Committed:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致。
可重复读Repeated Read:在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象
串行化Serializable:最高的隔离级别,在这个隔离级别下,不会产生任何异常。并发的事务,就像事务是在一个个按照顺序执行一样
mysql默认是可重复读Repeated Read
sql:结构化查询语言(Structured Query Language) 针对于关系型数据库的编程语言
sql分类:
1.DDL(Data Definition Language)数据定义语言
关键字:create、alter、drop、truncate
2.DML(Data Manipulation Language) 数据操作语言
关键字:insert、update、delete
3.DDL(Data Query Language)数据查询语言
关键字:select
4.DCL(Data Control Language) 数据控制语言
关键字:grant、revoke
5 TCL(Transaction Control Language)事务控制语言
关键字:rollback、commit、savepoint
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建 立索引。 2.in 和 not in 也要慎用,否则会导致全表扫描,如: select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了: select id from t where num between 1 and 3 3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。 4.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。 select id from t where substring(name,1,3)='abc'--name以abc开头的id 应改为: select id from t where name like 'abc%' 5.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无 法正确使用索引。 6.很多时候用 exists 代替 in 是一个好的选择: select num from a where num in(select num from b) 用下面的语句替换: select num from a where exists(select 1 from b where num=a.num) 7.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行 全表扫描,如: select id from t where num is null 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0 8.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间, 其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。 9.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的 任何字段。 10.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑 改写。 11.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全 表扫描,如: select id from t where num=10 or num=20 可以这样查询: select id from t where num=10 union all select id from t where num=20
其他: 12.下面的查询也将导致全表扫描: select id from t where name like '%abc%' 13.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表 扫描。如: select id from t where num/2=100 应改为: select id from t where num=100*2 14.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段 作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺 序与索引顺序相一致。 15.不要写一些没有意义的查询,如需要生成一个空表结构: select col1,col2 into #t from t where 1=0 这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样: create table #t(...) 16.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据 重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半, 那么即使在sex上建了索引也对查询效率起不了作用。 17.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率, 因为 insert 或 update 时有可能会重建索引,所以怎样建索引 需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些 不常使用到的列上建的索引是否有必要。 18.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的 性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字 符,而对于数字型而言只需要比较一次就够了。 19.避免频繁创建和删除临时表,以减少系统表资源的消耗。 20.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型 表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。 21.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log , 以提高速度;如果数据量不大,为了缓和系统表的资源,应 先create table,然后insert。 22.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。 23.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方 法通常更有效。 24.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于 其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合 计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的 方法都可以尝试一下,看哪一种方法的效果更好。 25.尽量避免大事务操作,提高系统并发能力。 26.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
都是用于存储字符串的sql数据类型
不同之处:
char(n)是固定长度 固定n个单位(字符/字节 mysql是字符 oracle是字节)
varchar(n)/varchar2(n) 是可变长度 最多n个单位
char(n)最多4000个单位 varchar(n)/varchar2(n)最多8000个单位
char和varchar是sql标准 varchar2是oracle的方言
分页有两种:sql分页和内存分页
内存分页:把所有的记录查询出来 放入session中 按页数从session中取
sql分页:通过sql查询固定行 实现分页
sql分页之mysql: 通过limit 起始索引,每页记录数 实现:每页6行 查询低3页
select * from student limit 12,6
sql分页之oracle:通过伪列rownumber
select * from (select s.*,rownum r from student s ) ss
where ss.r>=13 and ss.r<=18
效率更高点:
select * from (select s.*,rownum r from student s where rownum<=18) ss where ss.r>=13
1 、查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断
select * from people where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1 )
2 、删除表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断,只留有rowid最小的记录
delete from people where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1 )
and rowid not in (select min(rowid) from people group by peopleId having count(peopleId )>1 )
3 、查找表中多余的重复记录(多个字段)
select * from vitae a where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1 )
相同之处: 都可以实现对表记录的删除
不同之处:
1、 truncate和delete只删除数据不删除表的结构(定义),
drop语句删除表结构和其约束(constrain)触发器(trigger)索引(index)
2、delete命令是DML,可以添加事务 实现事务回滚,可以触发trigger
truncate,drop命令是DDL,无法回滚。
3、速度:一般来说:drop > truncate > delete
4、delete可以添加条件 删除指定行
truncate和drop不能添加条件
存储过程(Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数来执行它
存储过程优点
1 执行速度更快---因为存储过程是预编译过的
2 模块化程序设计---类似方法的复用
3 提高系统的安全性---防止SQL注入
4 减少网络流量---只需传输存储过程的名称即可
触发器(trigger)是一种特殊类型的存储过程,不由用户直接调用,由指定事件触发而执行
触发器作用
1 触发器可通过数据库中的相关表实现级联更改
2 触发器可以实现复杂的约束
3 触发器还可以强制执行业务规则
4 触发器实现sql操作日志记录
触发器和存储过程相同之处:
都是指定功能的sql语句集
触发器和存储过程不同:
1 类型不同
存储过程类型是procedure 触发器类型是trigger
2 执行不同
存储过程由用户通过名字直接调用 触发器不能直接调用 而是由事件触发执行
3 功能不同
存储过程是对指定功能sql语句集的封装 实现代码复用
触发功能是:强化约束、跟踪变化、级联运行
mysql: int类型的主键 可以通过auto_increment实现主键自动增长
oracle:int类型的主键 通过序列 sequence实现自动增长
create sequence s_1;
create table student(sid int parmary key);
insert into student values(s_1.nextval);
第一范式:(确保每列保持原子性)所有字段值都是不可分解的原子值。
错误实例:用户编号 用户名字 用户地址
正确实例:用户编号 用户名字 用户省份 用户城市 用户小区 用户房间
第二范式:(确保表中的每列都和主键相关 消除部分依赖)在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中
错误实例:学号 课程 课程成绩 课程学分
此表 学号+课程是联合主键: 但课程学分只依赖课程---部分依赖
正确实例: 表1:学号 课程 课程成绩
表2:课程 课程学分
第三范式:(确保每列都和主键列直接相关,消除传递相关) 数据表中的每一列数据都和主键直接相关,而不能间接相关。
错误实例:订单编号 订单项目 负责人 客户编号 客户电话 客户地址
此表:客户电话+客户地址依赖于客户编号 而客户编号依赖于主键订单编号
正确实例: 表1: 订单编号 订单项目 负责人 客户编号
表2:客户编号 客户电话 客户地址
1、Oracle是大型的数据库 收费的
Mysql是中小型数据库 免费开源的
2、Oracle支持大并发,大访问量 性能优越 安全性高
3、Mysql启动和使用所在内存远远小于Oracle所占用的内存
4、事务隔离级别
MySQL是repeatable read的隔离级别,而Oracle是read commited的隔离级别,
同时二者都支持serializable串行化事务隔离级别,可以实现最高级别的
5、提交方式
Oracle默认不自动提交,需要手动提交。Mysql默认自动提交
6、sql语句的灵活性
mysql对sql语句有很多非常实用而方便的扩展,比如limit功能(分页),insert可以
一次插入多行数据,通过auto_increment实现主键自动增长
Oracle在这方面感觉更加稳重传统一些,Oracle的分页是通过伪列和子查询完成的,
插入数据只能一行行的插入数据,只能通过序列sequence实现主键自动增长
关系型数据库:Relational Database :以二维表的方式来存储数据
常见rdbms:mysql oracle sqlserver access db2
非关系型数据库:NoSql: 泛指所有不以二维表方式来存储数据的数据库
常见nosql:
Redis:键值存储数据库 适用于缓存/处理高访问负载
Hbase:列式存储数据库 适用于分布式文件系统
MongoDB:文档型数据库 适用于web应用结构化数据存储
Neo4J:图形数据库 适用社交网络
关系型数据库和非关系型数据库区别 关系型数据库优点 容易理解:二维表结构是非常贴近逻辑世界 使用方便:通用的SQL语言使得操作关系型数据库非常方便; 易于维护:丰富的完整性约束大大减低了数据冗余和数据不一致的概率; 支持SQL:易于实现复杂的查询。 支持事务 缺点 为了维护一致性所付出的巨大代价就是其读写性能比较差; 固定的表结构 灵活性差 不支持高并发读写需求; 不支持海量数据的高效率读写 非关系型数据库优点: 格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式 速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘; 高扩展性;很容易实现横向扩展(数据分片)和水平扩展 成本低:nosql数据库部署简单,基本都是开源软件 缺点 不提供sql支持 学习和使用成本高 不支持事务 不支持复杂查询
关系型数据库和非关系型数据库区别 1.存储上 Sql通常以数据库表的形式存储 NoSql采用key-value/列簇/图形/文档等多种形式存储 2.事务 SQL支持事务 nosql不支持事务 3.数据表 VS 数据集 关系型是表格型的,存储在数据表的行和列中。彼此关联,容易提取。 非关系型是大块存储在一起。 4.预定义结构 VS 动态结构 sql必须先定义表结构后 才能添加数据 Nosql数据可以随时添加 无需预先定义。 5.纵向拓展 VS 横向拓展 SQL数据采用纵向扩展,提高处理能力,通过提高计算机性能来提高处理能力。 NoSql通过横向拓展,非关系型数据库天然是分布式的,所以可以通过集群来实现负载均衡。 6.是否支持sql
Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求.
Spring Core:核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI命名与目录接口规范、定时任务等);
Spring AOP:AOP服务 面向切面编程;
Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;
Spring ORM:对现有的ORM框架的支持;
Spring Web:提供了基本的面向Web的综合特性,例如文件上传;
Spring MVC:提供面向Web应用的Model-View-Controller实现。
1.spring属于低侵入式设计,代码的污染极低;
2.spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
3.Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,实现业务复用。
4.spring对于主流的应用框架提供了集成支持。
5.spring简化异常处理:Spring 提供方便的 API 把具体技术相关的异常转化为一致的 unchecked异常。
IOC: Inversion of Control 控制反转,是指对象创建和操作的控制权的转移给spring容器
以前创建和使用对象的主动权有程序决定,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创
建实例和分配实例之间的关系,对象与对象之间松散耦合,易于复用。
DI: Dependency Injection依赖注入,应用程序在运行时通过spring容器把依赖的实例动态注入到项目中。
和控制反转是同一个概念的不同角度的描述
三种注入方式:Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
AOP:Aspect Oriented Programming面向切面,对面向对象的一种补充
通过预编译方式在运行时由动态代理实现程序功能的统一维护的一种技术
用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect)。
减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。
可用于权限认证、日志、事务处理等
AOP实现的关键在于代理模式
静态代理:代理对象和目标对象实现相同接口 代理对象对关联的目标对象方法进行扩展
缺点局限性差 只能对指定类型的对象扩展
动态代理:实现InvocationHandler接口 实现invoke方法
缺点只能对指定接口类型的实现类扩展
cglib代理:实现MethodInterceptor接口 实现intercept方法
可以代理所有对象
IoC让相互协作的组件保持松散的耦合,
AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
(1)实例化Bean: 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,会调用createBean进行实例化。 对于ApplicationContext容器,当容器启动后,通过获取BeanDefinition对象中的信息,实例化所有 的bean。 (2)设置对象属性(依赖注入): 实例化后的对象被封装在BeanWrapper对象中,Spring根据BeanDefinition中的信息以及通过 BeanWrapper提供的设置属性的接口完成依赖注入。 (3)处理Aware接口: Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean: ①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方 法,此处传递的就是Spring配置文件中Bean的id值; ②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的 是Spring工厂自身。 ③如果这个Bean已经实现了ApplicationContextAware接口,会调用 setApplicationContext(ApplicationContext)方法,传入Spring上下文; (4)BeanPostProcessor: 如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用 postProcessBeforeInitialization(Object obj, String s)方法。 (5)InitializingBean 与 init-method: 如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。 (6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术; 以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。 (7)DisposableBean: 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口, 会调用其实现的destroy()方法; (8)destroy-method: 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
自动装配:通过autowire配置由spring容器实现对象的自动关联
1)no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
2)byName - 它根据 bean 的名称注入对象依赖项
3)byType - 它根据 bean 的类型注入对象依赖项
4)constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配
5)autodetect - 首先容器尝试通过构造函数使用 autowire 装配,如果不能,则尝试通过 byType 自动装配。
<context:annotation-config/>
四个注解都用于spring容器创建bean对象
@Component : 适用于任意层
@Controller :使用与springmvc的控制层
@Service : 使用于业务层
@Repository :使用于持久层:使未经检查的异常有资格转换为 Spring DataAccessException。
(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
(2)prototype:每次对bean请求都提供一个新的实例。
(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean失效
(4)session:为每个session回话创建一个实例,在session过期后,bean会随之失效。
(5)global-session:全局作用域,一个项目只存在一个实例
(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
(2)单例模式:Bean默认为单例模式。
(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。
(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。
真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
Spring事务的种类:
spring支持编程式事务管理和声明式事务管理两种方式:
①编程式事务管理使用TransactionTemplate。 可以作用到代码块级别
②声明式事务管理建立在AOP之上 通过@Transactional注解实现。 只能作用到方法级别 非侵入式开发
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
① PROPAGATION_REQUIRED(常用):如果当前没事务,就创建一个新事务,如果当前有事务,就加入该事务
② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以 非事务执行。
③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛 出异常。
④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性 执行。
① ISOLATION_DEFAULT(默认):使用数据库默认的事务隔离级别。
② ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
③ ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看
到该事务对已有记录的更新。
④ ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不
能看到该事务对已有记录的更新。
⑤ ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。
springmvc:spring框架提供的构建web应用程序的全功能mvc模块
springmvc遵循mvc设计模式的思想 把视图渲染、请求处理、模型创建
SpringMVC的请求相应步骤如下
1、用户向服务器发送请求,请求被Spring中央控制器Servelt DispatcherServlet捕获
2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用
HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),
最后以HandlerExecutionChain对象的形式返回
3、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
4、提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。
5、Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
6、根据返回的ModelAndView,选择一个适合的ViewResolver返回给DispatcherServlet
7、ViewResolver 结合Model和View,来渲染视图
8、将渲染结果返回给客户端
1)DispatcherServlet:中央控制器:接受请求 、统一调度
降低组件之间的耦合度 托管组件的扩展性
2)HandleMappering:处理器映射器
指定url和action的映射关系:可以是配置方式、注解方式
3)HandleAdapter:处理器适配器
根据映射器找到的处理器 Handler 信息,按照特定的规则去执行相关的处理器 Handler。
4)ViewResolver:视图解析器
把视图逻辑路径 解析为视图真实路径
1.清晰的角色划分:控制器(controller)、验证器(validator)、命令对象(command obect)、表单对象(form object)、模型对象(model object)、Servlet分发器(DispatcherServlet)、处理器映射(handler mapping)、试图解析器(view resoler)等等。每一个角色都可以由一个专门的对象来实现。
2.强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在 web控制器中对业务对象和验证器validator)的引用。
3.可适配、非侵入:可以根据不同的应用场景,选择何事的控制器子类(simple型、command型、from型、wizard 型、multi-action型或者自定义),而不是一个单一控制器(比如Action/ActionForm)继承。
4.可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改变页面时,不需要重新编写业务逻辑。
Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。
View(视图) - 视图代表模型包含的数据的可视化。
Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。
它使视图与模型分离开
概念:
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。
通过反射获取注解时,返回的是Java运行时生成的动态代理对象。
通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。
常见:
@Override - 检查该方法是否是重写方法。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
@Test - 单元测试
springmvc常用的注解
1. @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。
2. @RequestBody:将HTTP请求正文转换为适合的HttpMessageConverter对象
3. @ResponseBody:将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格
式后,写入响应体中。
4. @Controller: 标记一个类是作为控制层
5. @Service: 标记一个类是作为业务层
6. @repository: 标记一个类是作为持久层
7. @RequestParam: 绑定单个请求参数
8、@PathVariable绑定URI模板变量值
9. @Autowired和@Resource:自动装配(实现对象)
@Autowired按类型装配:@Resource先按名字 再按类型
(1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4"
(2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com"
相同之处: 基本功能相似 ,都可以对指定的请求进行拦截 进行预处理、权限管理、日志记录等操作 都是aop编程思想的体现 不同之处:1 实现接口不同 过滤器Filter实现接口:Filter接口 拦截器Interceptor实现接口:HanderInteceptor接口 2 是否依赖web容器 Filter是servlet规范 需要web应用服务器解析 Interceptor由spring容器支持 不依赖web容器 3 实现原理不同 Filter是函数回调 Interceptor是反射和动态代理 4 适用的项目不同 Filter只使用于web项目 Interceptor使用于所有项目中 5 拦截资源不同 Filter可以拦截对所有资源的请求 Interceptor只能拦截action(方法)
参数类型:
1 参数是单值:接受同名的请求参数
2参数是javabean对象:通过对象的属性来接受同名的请求参数
3 参数是HttpServletRequest:请求对象
4 参数是HttpServletResponse:响应对象
5 参数是HttpSession:回话
6 参数是model:request域对象
返回值类型:
1 返回值类型是String::返回的是跳转资源的路径
2 返回值类型是ModelAndView::设置跳转资源的路径+request域属性
3 返回值类型是void
概念:
Hibernate 是一个开源框架,它是对象关联关系映射的框架,它对 JDBC 做了轻量级的封装,使 java
程序员可以使用面向对象的思想来操纵数据库。基本原理是ORM
相关接口:
sessionFactory:负责初始化 hibernate,创建 session 对象
session:负责被持久化对象 CRUD 操作
configuration:负责配置并启动 hibernate,创建 SessionFactory
Transaction:负责事物相关的操作
Query 和 Criteria 接口:负责执行各种数据库查询
orm:
对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互
不匹配现象的技术。简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,实现 java 程序中的对
象自动持久化为关系型数据库表的行,和 实现行自动解析为对象
相同之处:
get和load都是利用主键策略查询数据
不同之处:
1:get默认不使用懒加载机制,load默认要使用懒加载机制,
懒加载:数据如果不使用,hibernate就不发送SQL到数据库查询数据。
2:当查询数据库不存在的数据的时候,get方法返回null,load方法抛出ObjectNotFoundException
3:load 方法可返回没有加载实体数据的代理类实例,而 get 方法返回有实体数据的对象
瞬时态(Transient):创建Session后,由 new 操作符创建,且尚未与Hibernate Session 关联的对象。
瞬时对象不会被持久化到数据库中,也不会被赋予持久化标识。
如果瞬时对象在程序中没有被引用,它会被垃圾回收器销毁。
持久态(Persistent):持久的实例可能是刚被保存的,或刚被加载的,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久状态的对象的任何改动,在当前操作单元执行完毕时将对象数据(state)与
数据库同步,开发者不需要手动执行UPDATE。
脱管态(Detached):也叫游离态,与持久对象关联的Session被关闭后,对象就变为脱管的。
脱管态不能直接持久化,需要重新保存。
缓存: 是数据库数据在内存中的临时容器,包括数据库数据在内存中的临时拷贝,它位于数据库与数据库访问中间层,
ORM在查询数据时,首先会根据自身的缓存管理策略,在缓存中查找相关数据,如果发现所需的数据,则直接
将此数据作为结果加以使用,从而避免数据库调用性能的开销,而相对内存操作而言,数据库调用是一个代价高
昂的过程。
Hibernate缓存包括两大类:一级缓存和二级缓存
Hibernate一级缓存:又被称为“Session的缓存”。Session缓存是内置的,不能被卸载,是事务范围的缓存,
在一级缓存中,持久化类的每个实例都具有唯一的OID。
Hibernate二级缓存:又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整
个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问
题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别,第二级缓
存是可选的,默认是关闭的。
Hibernate的检索策略分为三种,立即检索、延迟检索、左外连接检索
立即检索:采用立即检索策略,会把被检索的对象,以及和这个对象关联的一对多对象都加载到缓存中。Session的get方法就使用的立即检索策略。
优点:频繁使用的对象会被加载到缓存中,程序调用很方便,也很及时。
缺点:占用的内存过多,而且数据库访问的次数也会很频繁,效率低下。
延迟检索:采用延迟检索策略,就不会立即加载关联对象的内容。直到第一次调用关联对象时,才去加载关联对象。在不涉及关联类操作时,延迟检索策略只适用于Session的load方法。涉及关联类操作时,延迟检索策略也能够适用于get,list等操作。
优点:由程序决定加载哪些类和内容,而不必全部都加载,避免了内存的大量占用和数据库的频繁访问。 缺点:在Session关闭后,就不能访问关联类对象了。
左外连接检索:采用左外连接检索,能够使用Sql的外连接查询,将需要加载的关联对象加载在缓存中。这种策略的优点在于,对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象。
优点:使用了外连接,select语句数目少。
缺点:可能会加载应用程序不需要访问的对象,白白浪费许多内存空间。影响检索性能。
5种:导航对象图检索方式、OID检索方式、HQL检索方式、QBC检索方式、本地SQL检索方式。
导航对象图检索方式:利用类与类之间的关系来检索对象
OID检索方式:由id获取对象,主要指用Session的get()和load()方法加载某条记录对应的对象。
HQL检索方式: HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似。
通过 Session类的Qurey接口实现
QBC检索方式: QBC(Query By Criteria)由Criteria接口实现,它支持在运行时动态生成查询语句。
本地SQL检索方式:由SQLQuery接口实现生成标准的SQL和本地SQL.
(1)sql优化方面 MyBatis: 手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量大。 Hibernate: 不需要编写大量的 SQL,就可以完全映射,提供了日志、缓存、级联等特性。会消耗性能 (2)开发方面 MyBatis: 是一个半自动映射的框架,因为 MyBatis 需要手写sql Hibernate: 是一个全表映射的框架,只需提供 POJO 和映射关系即可。 (3)适用场景 mybatis: 适合模型不成熟 需求频繁更改的项目。只需要更改sql即可 hibernate: 适合模型固定的项目。更改表关系的代价比较高 (4)hibernate优势 Hibernate 的 DAO 层开发比 MyBatis 简单,Mybatis需要维护 SQL 和结果映射。 Hibernate 对对象的维护和缓存要比 MyBatis 好,对增删改查的对象的维护要方便。 Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。 Hibernate 有更好的二级缓存机制,可以使用第三方缓存。MyBatis 本身提供的缓存机制不佳。 (5)Mybatis优势 MyBatis 可以进行更为细致的 SQL 优化,可以减少查询字段。 Mybatis学习门槛低,简单易学,而 Hibernate 门槛较高。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将java对象映射成表记录。
1.简单易学
2.灵活: sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
3.解除sql与程序代码的耦合:sql和代码的分离,提高了可维护性。
4.提供映射标签,支持对象与数据库的orm字段关系映射。
5.支持编写动态sql。
(1)含义不同
#{}是预编译处理,${}是字符串替换。
(2)在sql中的作用不同
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理${}时,就是把${}替换成变量的值。
(3)安全性不同
#{}可以有效的防止SQL注入,提高系统安全性。
(1)SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。
(2)SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。
Mybatis提供了9种动态sql标签:
trim | where | set | foreach | if | choose | when | otherwise | bind
Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。
1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,
当 Session flush/close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。
2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache的HashMap 存储,其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置<cache/> ;
3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新。
Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。
举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完
后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。
简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中
(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个
Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。
在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他 类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供 安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授 权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当
配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。
> 简单的身份认证, 支持多种数据源
> 对角色的简单的授权, 支持细粒度的授权(方法级)
> 支持一级缓存,以提升应用程序的性能;
> 内置的基于 POJO 企业会话管理, 适用于 Web 以及非 Web 的环境
> 非常简单的加密 API
> 不跟任何的框架或者容器捆绑, 可以独立运行
Session 所谓session,即用户访问应用时保持的连接关系,在多次交互中应用能够识别出当前访问的用户是谁,且可以在多次交互中保存一些数据。 Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); session.getId(); // 获取当前session的唯一标识 session.getHost(); // 获取当前Subject的主机地址 session.getTimeOut(); // 获取超时时间 session.setTimeOut(); // 设置超时时间(不设置默认是全局过期时间) session.touch(); // 更新最后访问时间 session.stop(); // 销毁session,当Subject.logout()时会自动调用stop方法来销毁会话。如果在web中,调用javax.servlet.http.HttpSession.invalidate()也会自动调用shiro session.top方法进行销毁shiro的会话 session.setAttribute(“key”,”123”); // 设置session属性 session.getAttribute(“key”); // 获取session属性 session.removeAttribute(“key”); // 删除属性 注:Shiro提供的会话可以用于javaSE/javaEE环境,不依赖于任何底层容器,可以独立使用,是完整的会话模块。 Session manager 会话管理器 会话管理器管理着应用中所有Subject的会话的创建、维护、删除、失效、验证等工作。是Shiro的核心组件,顶层组件SecurityManager直接继承了SessionManager,且提供了SessionSecurityManager实现直接把会话管理委托给相应的SessionManager、DefaultSecurityManager及DefaultWebSecurityManager 默认SecurityManager都继承了SessionSecurityManager。 Shiro提供了三个默认实现: DefaultSessionManager:DefaultSecurityManager使用的默认实现,用于JavaSE环境; ServletContainerSessionManager: DefaultWebSecurityManager使用的默认实现,用于Web环境,其直接使用Servlet容器的会话; DefaultWebSessionManager:用于Web环境的实现,可以替代ServletContainerSessionManager,自己维护着会话,直接废弃了Servlet容器的会话管理。
Shiro 在保持强大功能的同时, 使用简单性和灵活性
SpringSecurity: 即使是一个一个简单的请求,最少得经过它的 8 个Filter
SpringSecurity 必须在 Spring 的环境下使用
Spring Security学习成本高.
nginx是一款免费的、自由的、开源的、高性能HTTP服务器和反向代理服务器
高性能、稳定性、丰富的功能、简单的配置和低资源消耗
Nginx 解决了服务器的C10K(就是在一秒之内连接客户端的数目为10k即1万)问题
1.反向代理
为项目的服务器集群提供一个统一的url
2.负载均衡
nginx根据集群各个服务器的性能均衡分配处理请求个数
3.动静分离
动态资源(部署集群上)和静态资源(部署nginx上)部署在不同的服务器上
轻量级,同样起web 服务,比apache 占用更少的内存及资源;抗并发,nginx处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能;高度模块化的设计,编写模块相对简单;最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程。
1.跨平台、配置简单。
2.非阻塞、高并发连接
3.内存消耗小
4.成本低廉,且开源。
5.稳定性高,宕机的概率非常小。
反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和 Web 服务器之间的中间层。这对于安全方面来说是很好的,特别是当我们使用 Web 托管服务时
个主进程,多个工作进程,每个工作进程可以处理多个请求,每进来一个request,会有一个worker进程去处理。
Apache: 创建多个进程或线程,而每个进程或线程都会为其分配 cpu 和内存,并发过大会榨干服务器资源。
Nginx: 采用单线程来异步非阻塞处理请求,不会为每个请求分配 cpu 和内存资源,节省了大量资源,同时也减少了大量的 CPU 的上下文切换。所以才使得 Nginx 支持更高的并发。
在我们的软件开发中,有些请求是需要后台处理的,有些请求是不需要经过后台处理的,这些不需要经过后台处理的文件称为静态文件,否则动态文件。
负载均衡,即是代理服务器将接收的请求均衡的分发到各服务器中,负载均衡主要解决网络拥塞问题,提高服务器响应速度,服务就近提供,达到更好的访问质量,减少后台服务器大并发压力
1、轮询(默认)round_robin
2、IP 哈希 ip_hash
3、最少连接 least_conn
1)Tracker
Tracker Server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server提供文件上传服务。可以将tracker称为追踪服务器或调度服务器。
FastDFS集群中的Tracker server可以有多台,Tracker server之间是相互平等关系同时提供服务,Tracker server不存在单点故障。客户端请求Tracker server采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。2)Storage
Storage Server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己的文件系统而是使用操作系统的文件系统来管理文件。可以将storage称为存储服务器。
Storage集群采用了分组存储方式。storage集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容量之和。一个组由一台或多台存储服务器组成,组内的Storage server之间是平等关系,不同组的Storage server之间不会相互通信,同组内的Storage server之间会相互连接进行文件同步,从而保证同组内每个storage上的文件完全一致的。一个组的存储容量为该组内的存储服务器容量最小的那个,由此可见组内存储服务器的软硬件配置最好是一致的。
采用分组存储方式的好处是灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组也可以由tracker进行调度选择。一个分组的存储服务器访问压力较大时,可以在该组增加存储服务器来扩充服务能力(纵向扩容)。当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。3)Storage状态收集
Storage server会连接集群中所有的Tracker server,定时向他们报告自己的状态,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。
Fastdfs优点
1、主备Tracker服务,增强系统的可用性
2、系统不需要支持POSIX,这样的话就降低了系统的复杂度,使得处理的速度会更高
3、支持主从文件,支持自定义扩展名
4、支持在线扩容机制,增强了系统的可扩展性
5、实现了软RAID,增强了系统的并发处理能力和数据容错恢复能力
fastdfs缺点
1、通过API下载,存在单点的性能瓶颈
2、不支持断点续传,对大文件将是噩梦
3、同步机制不支持文件正确性校验,降低了系统的可用性
4、不支持POSIX通用接口访问,通用性比较的低
5、对跨公网的文件同步,存在着比较大的延迟,需要应用做相应的容错策略
Redis全称为:Remote Dictionary Server(远程数据服务),是一个基于内存的高性能key-value数据库。
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
我们实际项目中比较常用的是string,hash如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。
如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,Zset,hash等
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
(1) Memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
(2) Redis的速度比Memcached快很多
(3) Redis可以持久化其数据
(1)、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,这样能保证数据的持久性。
(2)、数据支持类型 Memcache对数据类型支持相对简单。 Redis有复杂的数据类型。
(3)、使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
Redis是单进程单线程的,redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。
Redis提供两种持久化机制RDB和AOF机制: 1)RDB(Redis DataBase)持久化方式: 是指用数据集快照的方式(半持久化模式)记录redis数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。 优点: 1.只有一个文件dump.rdb,方便持久化。 2.容灾性好,一个文件可以保存到安全的磁盘。 3.性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。(使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能) 4.相对于数据集大时,比AOF的启动效率更高。 缺点: 1.数据安全性低。(RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候) 2)AOF(Append-only file)持久化方式: 是指所有的命令行记录以redis命令请求协议的格式(完全持久化存储)保存为aof文件。 优点: 1.数据安全,aof持久化可以配置appendfsync属性,有always,每进行一次命令操作就记录到aof文件中一次。 2.通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。 3.AOF机制的rewrite模式。(AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)) 缺点: 1.AOF文件比RDB文件大,且恢复速度慢。 2.数据集大的时候,比rdb启动效率低。
(1)、定时删除:在设置键的过期时间的同时,创建一个定时器(timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
(2)、惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
(3)、定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 no-enviction(驱逐):禁止驱逐数据 注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。 使用策略规则: 1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru 2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random
Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
(1)、Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
(2)、Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
1)事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
2)事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面.
(1)、会话缓存(Session Cache) 最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。 (2)、全页缓存(FPC) 除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。 再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。 此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。 (3)、队列 Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。 如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。 (4),排行榜/计数器 Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。 (5)、发布/订阅 最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
set 指令可以把setnx和expire合成一条指令来用的!以防setnx执行expire执行前挂掉。
复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。
某一个非常热点的key,大量用户去请求这个缓存的key,这个缓存的key突然失效时,请求都会打到这个数据库上
解决:
使用分布式锁--互斥锁,大量用户去访问这个redis请求数据,有的话返回给用户,redis数据为空的话,会请求这个数据库去请求数据,请求数据库这一点上锁,这个时候只有一个线程能抢到这个锁,对数据库的压力非常小
使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法
缓存击穿,这个redis储存和这个数据库都没有这个数据,直接穿透redis到这个数据库
解决这个问题有如下办法。
1.ip拉黑
2.对参数的合法性检验,判断这个参数不合法的时候,直接return掉
3.使用布隆过滤器
bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小
存在同一时间内大量键过期(失效),接着来的一大波请求瞬间都落在了数据库中导致连接异常。
解决方案:
1.设置缓存失效时间,不在统一时间失效,随机初始化他的这个失效时间
2.不设置这个缓存失效时间
3.也是像解决缓存穿透一样加锁排队。
38.缓存并发问题
这里的并发指的是多个redis的client同时set key引起的并发问题。比较有效的解决方案就是把redis.set操作放在队列中使其串行化,必须的一个一个执行,具体的代码就不上了,当然加锁也是可以的,至于为什么不用redis中的事务,留给各位看官自己思考探究。
通过增加Slave DB的数量,读的性能可以线性增长。为了避免Master DB的单点故障,集群一般都会采用两台Master DB做双机热备,所以整个集群的读和写的可用性都非常高。读写分离架构的缺陷在于,不管是Master还是Slave,每个节点都必须保存完整的数据,如果在数据量很大的情况下,集群的扩展能力还是受限于单个节点的存储能力,而且对于Write-intensive类型的应用,读写分离架构并不适合。
为了解决读写分离模型的缺陷,可以将数据分片模型应用进来。
可以将每个节点看成都是独立的master,然后通过业务实现数据分片。
结合上面两种模型,可以将每个master设计成由一个master和多个slave组成的模型。
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。缺点:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。**能不能生产一次消费多次呢?**使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
String类型是 Redis 中最常使用的类型。
这是最简单的类型,就是普通的 set 和 get,做简单的 KV 缓存。
String的实际应用场景比较广泛的有:
- 缓存功能:String字符串是最常用的数据类型,不仅仅是Redis,各个语言都是最基本类型,因此,利用Redis作为缓存,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
- 计数器:许多系统都会使用Redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
- 共享用户Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存Cookie,但是可以利用Redis将用户的Session集中管理,在这种模式只需要保证Redis的高可用,每次用户Session的更新和获取都可以快速完成。大大提高效率。
这个是类似 Map 的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是这个对象没嵌套其他的对象)给缓存在 Redis 里,然后每次读写缓存的时候,可以就操作 Hash 里的某个字段。
List 是有序列表,
List本身就是我们在开发过程中比较常用的数据结构了,热点数据更不用说了。
- 消息队列:Redis的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过Lpush命令从左边插入数据,多个数据消费者,可以使用BRpop命令阻塞的“抢”列表尾部的数据。
- 文章列表或者数据分页展示的应用。
比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用Redis的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率。
Set 是无序集合,会自动去重的那种。
Sorted set 是排序的 Set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。
Kafka是分布式发布-订阅消息系统,它最初是由LinkedIn公司开发的,之后成为Apache项目的一部分,Kafka是一个分布式,可划分的,冗余备份的持久性的日志服务,它主要用于处理流式数据。
缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。
解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。
冗余:可以采用一对多的方式,一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。
健壮性:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。
异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
ISR:In-Sync Replicas 副本同步队列
AR:Assigned Replicas 所有副本
ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。
broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站。
zookeeper 是一个分布式的协调组件,早期版本的kafka用zk做meta信息存储,consumer的消费状态,group的管理以及 offset的值。考虑到zk本身的一些因素以及整个架构较大概率存在单点问题,新版本中逐渐弱化了zookeeper的作用。新的consumer使用了kafka内部的group coordination协议,也减少了对zookeeper的依赖,
但是broker依然依赖于ZK,zookeeper 在kafka中还用来选举controller 和 检测broker是否存活等等。
Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。完全同步复制要求All Alive Follower都复制完,这条消息才会被认为commit,这种复制方式极大的影响了吞吐率。而异步复制方式下,Follower异步的从Leader复制数据,数据只要被Leader写入log就被认为已经commit,这种情况下,如果leader挂掉,会丢失数据,kafka使用ISR的方式很好的均衡了确保数据不丢失以及吞吐率。Follower可以批量的从Leader复制数据,而且Leader充分利用磁盘顺序读以及send file(zero copy)机制,这样极大的提高复制性能,内部批量写磁盘,大幅减少了Follower与Leader的消息量差。
leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。
- Cache Filesystem Cache PageCache缓存
- 顺序写 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。
- Zero-copy 零拷技术减少拷贝次数
- Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。
- Pull 拉模式 使用拉模式进行消息的获取消费,与消费端处理能力相符。
- 增加线程
- 提高 batch.size
- 增加更多 producer 实例
- 增加 partition 数
- 设置 acks=-1 时,如果延迟增大:可以增大 num.replica.fetchers(follower 同步数据的线程数)来调解;
- 跨数据中心的传输:增加 socket 缓冲区设置以及 OS tcp 缓冲区设置。
- 1(默认) 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。
- 0 生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
- -1 producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。
unclean.leader.election.enable 为true的话,意味着非ISR集合的broker 也可以参与选举,这样有可能就会丢数据,spark streaming在消费过程中拿到的 end offset 会突然变小,导致 spark streaming job挂掉。如果unclean.leader.election.enable参数设置为true,就有可能发生数据丢失和数据不一致的情况,Kafka的可靠性就会降低;而如果unclean.leader.election.enable参数设置为false,Kafka的可用性就会降低。
kafka在Broker端提供了一个配置参数:unclean.leader.election,这个参数有两个值:
true(默认):允许不同步副本成为leader,由于不同步副本的消息较为滞后,此时成为leader,可能会出现消息不一致的情况。
false:不允许不同步副本成为leader,此时如果发生ISR列表为空,会一直等待旧leader恢复,降低了可用性。
一个Kafka的Message由一个固定长度的header和一个变长的消息体body组成
header部分由一个字节的magic(文件格式)和四个字节的CRC32(用于判断body消息体是否正常)构成。
当magic的值为1的时候,会在magic和crc32之间多一个字节的数据:attributes(保存一些相关属性,
比如是否压缩、压缩格式等等);如果magic的值为0,那么不存在attributes属性
body是由N个字节构成的一个消息体,包含了具体的key/value消息
同样是逻辑上的概念,是Kafka实现单播和广播两种消息模型的手段。同一个topic的数据,会广播给不同的group;同一个group中的worker,只有一个worker能拿到这个数据。换句话说,对于同一个topic,每个group都可以拿到同样的所有数据,但是数据进入group后只能被其中的一个worker消费。group内的worker可以使用多线程或多进程来实现,也可以将进程分散在多台机器上,worker的数量通常不超过partition的数量,且二者最好保持整数倍关系,因为Kafka在设计时假定了一个partition只能被一个worker消费(同一group内)。
要确定Kafka的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费。
1、消息发送
Kafka消息发送有两种方式:同步(sync)和异步(async),默认是同步方式,可通过producer.type属性进行配置。Kafka通过配置request.required.acks属性来确认消息的生产:
- 0—表示不进行消息接收是否成功的确认;
- 1—表示当Leader接收成功时确认;
- -1—表示Leader和Follower都接收成功时确认;
综上所述,有6种消息生产的情况,下面分情况来分析消息丢失的场景:
(1)acks=0,不和Kafka集群进行消息接收确认,则当网络异常、缓冲区满了等情况时,消息可能丢失;
(2)acks=1、同步模式下,只有Leader确认接收成功后但挂掉了,副本没有同步,数据可能丢失;
2、消息消费
Kafka消息消费有两个consumer接口,Low-level API和High-level API:
- Low-level API:消费者自己维护offset等值,可以实现对Kafka的完全控制;
- High-level API:封装了对parition和offset的管理,使用简单;
如果使用高级接口High-level API,可能存在一个问题就是当消息消费者从集群中把消息取出来、并提交了新的消息offset值后,还没来得及消费就挂掉了,那么下次再消费时之前没消费成功的消息就“诡异”的消失了;
解决办法:
针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入Leader和Follower之后再确认消息发送成功;异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态; 针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。
- 1
- 2
- 3
在 Kafka 中,生产者写入消息、消费者读取消息的操作都是与 leader 副本进行交互的,从 而实现的是一种主写主读的生产消费模型。
Kafka 并不支持主写从读,因为主写从读有 2 个很明 显的缺点:
- (1)数据一致性问题。数据从主节点转到从节点必然会有一个延时的时间窗口,这个时间 窗口会导致主从节点之间的数据不一致。某一时刻,在主节点和从节点中 A 数据的值都为 X, 之后将主节点中 A 的值修改为 Y,那么在这个变更通知到从节点之前,应用读取从节点中的 A 数据的值并不为最新的 Y,由此便产生了数据不一致的问题。
- (2)延时问题。类似 Redis 这种组件,数据从写入主节点到同步至从节点中的过程需要经 历网络→主节点内存→网络→从节点内存这几个阶段,整个过程会耗费一定的时间。而在 Kafka 中,主从同步会比 Redis 更加耗时,它需要经历网络→主节点内存→主节点磁盘→网络→从节 点内存→从节点磁盘这几个阶段。对延时敏感的应用而言,主写从读的功能并不太适用。
kafka每个partition中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的。
整个topic不保证有序。如果为了保证topic整个有序,那么将partition调整为1.
offset+1
Kafka并没有使用JDK自带的Timer或者DelayQueue来实现延迟的功能,而是基于时间轮自定义了一个用于实现延迟功能的定时器(SystemTimer)。JDK的Timer和DelayQueue插入和删除操作的平均时间复杂度为O(nlog(n)),并不能满足Kafka的高性能要求,而基于时间轮可以将插入和删除操作的时间复杂度都降为O(1)。时间轮的应用并非Kafka独有,其应用场景还有很多,在Netty、Akka、Quartz、Zookeeper等组件中都存在时间轮的踪影。
底层使用数组实现,数组中的每个元素可以存放一个TimerTaskList对象。TimerTaskList是一个环形双向链表,在其中的链表项TimerTaskEntry中封装了真正的定时任务TimerTask.
Kafka中到底是怎么推进时间的呢?Kafka中的定时器借助了JDK中的DelayQueue来协助推进时间轮。具体做法是对于每个使用到的TimerTaskList都会加入到DelayQueue中。Kafka中的TimingWheel专门用来执行插入和删除TimerTaskEntry的操作,而DelayQueue专门负责时间推进的任务。再试想一下,DelayQueue中的第一个超时任务列表的expiration为200ms,第二个超时任务为840ms,这里获取DelayQueue的队头只需要O(1)的时间复杂度。如果采用每秒定时推进,那么获取到第一个超时的任务列表时执行的200次推进中有199次属于“空推进”,而获取到第二个超时任务时有需要执行639次“空推进”,这样会无故空耗机器的性能资源,这里采用DelayQueue来辅助以少量空间换时间,从而做到了“精准推进”。Kafka中的定时器真可谓是“知人善用”,用TimingWheel做最擅长的任务添加和删除操作,而用DelayQueue做最擅长的时间推进工作,相辅相成。
采用AMQP高级消息队列协议的一种消息队列技术,最大的特点就是消费并不需要确保提供方存在,实现了服务之间的高度解耦
将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。
一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。
如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。
这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性;
下面罗列几种特殊情况:
如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。
由于TCP连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。
若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。
通过路由可实现多消费的功能
消息提供方->路由->一至多个队列
消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。
通过队列路由键,可以把队列绑定到交换器上。
消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);
常用的交换器主要分为一下三种:
fanout:如果交换器收到消息,将会广播到所有绑定的队列上
direct:如果路由键完全匹配,消息就被投递到相应的队列
topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符
消息持久化,当然前提是队列必须持久化
RabbitMQ确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit会在消息提交到日志文件后才发送响应。
一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前RabbitMQ重启,那么Rabbit会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。
服务间高度解耦,
异步通信性能高,
流量削峰
…
镜像集群模式
你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。
好处在于,你任何一个机器宕机了,没事儿,别的机器都可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很重!第二,这么玩儿,就没有扩展性可言了,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue
mysql主从复制是master将所有的事务操作写入到binlog,slave获取binlog读入自己的中继区,然后再进行执行。
垂直拆分:是将单表,或者是有关联的表放在一个数据库,把原有的一个数据库拆分成若干个数据库。
水平拆分:是将一个很大的表,通过取模,按照日期范围等等拆分成若干个小表
sql解析 ->数据源分配 -> 请求响应 -> 结果整合
- 使用好ER表
- 善用全局表
- 在sql上添加注解
- 不存在热点数据时,则使用连续分片
- 存在热点数据时,使用离散分片或者是综合分片
- 离散分片暂时迁移比较麻烦(但是mycat给出了数据迁移的脚本,虽然现在还是不是很完美),综合分片占用总机器数量多
10、 Mycat中全局ID方案有哪些?程序自定义全局ID的方案有哪些?
一、mycat的全局id方案- 本地文件方式 sequnceHandlerType = 0
配置sequence_conf.properties
使用next value for MYCATSEQ_XXX- 数据库方式
sequnceHandlerType = 1
配置sequence_db_conf.properties
使用next value for MYCATSEQ_XXX或者指定autoIncrement- 本地时间戳方式
ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加)
sequnceHandlerType = 2
配置sequence_time_conf.properties
指定autoIncrement
搭建高可用的Eureka集群,只需要在注册中心的配置文件中配置其他注册中心的地址,
注册中心收到注册信息后会判断是否是其他注册中心同步的信息还是客户端注册的信息,如果是客户端注册的信息,那么他将会将该客户端信息同步到其他注册中心去;否则收到信息后不作任何操作。通过此机制避免集群中信息同步的死循环。
心跳机制:
- 客户端启动后,就会启动一个定时任务,定时向服务端发送心跳数据,告知服务端自己还活着,默认的心跳时间间隔是30秒。
服务剔除机制:
- 如果开启了自我保护机制,那么所有的服务,包括长时间没有收到心跳的服务(即已过期的服务)都不会被剔除;
- 如果未开启自我保护机制,那么将判断最后一分钟收到的心跳数与一分钟收到心跳数临界值(计算方法参考5.1节)比较,如果前者大于后者,且后者大于0的话,则启用服务剔除机制;
- 一旦服务剔除机制开启,则Eureka服务端并不会直接剔除所有已过期的服务,而是通过随机数的方式进行剔除,避免自我保护开启之前将所有的服务(包括正常的服务)给剔除。
在分布式系统的CAP理论中,Eureka采用的AP,也就是Eureak保证了服务的可用性(A),而舍弃了数据的一致性(C)。当网络发生分区时,客户端和服务端的通讯将会终止,那么服务端在一定的时间内将收不到大部分的客户端的一个心跳,如果这个时候将这些收不到心跳的服务剔除,那可能会将可用的客户端剔除了,这就不符合AP理论。
传统的关系型数据库是ACID(原子性,一致性,独立性,持久性),
nosql数据库是CAP(强一致性,可用性,分区容错性),分布式系统只能3进2,三个选两个
*eureka遵守AP原则,zookeeper遵守CP原则*
*CA-单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大*
*CP-满足一致性,分区容忍必的系统,通常性能不是特别高。*
*AP-满足可用性,分区容忍性的系统,通常可能对一致性要求低一些**分布式系统中,分区容错性是必须遵守的.*
1 RoundRobinRule: 轮询策略,Ribbon以轮询的方式选择服务器,这个是默认值。所以示例中所启动的两个服务会被循环访问;
2 RandomRule: 随机策略,也就是说Ribbon会随机从服务器列表中选择一个进行访问;
3 BestAvailableRule: 最大可用策略,即先过滤出故障服务器后,选择一个当前并发请求数最小的;
4 WeightedResponseTimeRule: 带有加权的轮询策略,对各个服务器响应时间进行加权处理,然后在采用轮询的方式来获取相应的服务器;
5 AvailabilityFilteringRule: 可用过滤策略,先过滤出故障的或并发请求大于阈值的一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个;
6 ZoneAvoidanceRule: 区域感知策略,先使用主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤,判断最小过滤数(默认1)和最小过滤百分比(默认0),最后对满足条件的服务器则使用RoundRobinRule(轮询方式)选择一个服务器实例
feign
?因为我们想像
dubbo
调用远程服务一样,节省构建请求body
并发送http
请求,还要手动反序列化响应结果的步骤。使用feign
能够让我们像同进程的接口方法调用一样调用远程进程的接口。
feign
是spring cloud
组件中的一个轻量级restful
的http
服务客户端,内置了ribbon
(因此使用feign
也需要引入ribbon
的依赖)。openfeign
是spring cloud
在feign
的基础上支持了spring mvc
的注解,如@RequesMapping
、@GetMapping
、@PostMapping
等。
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。
Zuul包含了对请求的路由和过滤两个最主要的功能:
- 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础.
- Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得.
- 防止不需要频繁请求服务的请求恶意频繁请求服务,造成服务器资源浪费。
- 防止不法分子恶意攻击系统,击穿系统盗取数据,防止数据安全隐患。
- 防止系统高峰时期,对系统对频繁访问,给服务器带来巨大压力。
限流策略
Zuul可以通过加载动态过滤机制,从而实现以下各项功能:
- 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
- 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
- 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
- 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
- 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
- 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
- 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。
相同点:Zuul和Nginx都可以实现负载均衡、反向代理(隐藏真实ip地址),过滤请求,实现网关的效果
不同点:
- Nginx–c语言开发
- Zuul–java语言开发
Zuul负载均衡实现:采用ribbon+eureka实现本地负载均衡
Nginx负载均衡实现:采用服务器实现负载均衡
Nginx相比zuul功能会更加强大,因为Nginx整合一些脚本语言(Nginx+lua)
Nginx适合于服务器端负载均衡
Zuul适合微服务中实现网关
Docker不是虚拟化方法。它依赖于实际实现基于容器的虚拟化或操作系统级虚拟化的其他工具。为此,Docker最初使用LXC驱动程序,然后移动到libcontainer现在重命名为runc。Docker主要专注于在应用程序容器内自动部署应用程序。应用程序容器旨在打包和运行单个服务,而系统容器则设计为运行多个进程,如虚拟机。因此,Docker被视为容器化系统上的容器管理或应用程序部署工具。
A 容器不需要引导操作系统内核,因此可以在不到一秒的时间内创建容器。此功能使基于容器的虚拟化比其他虚拟化方法更加独特和可取。
B 由于基于容器的虚拟化为主机增加了很少或没有开销,因此基于容器的虚拟化具有接近本机的性能。
C 对于基于容器的虚拟化,与其他虚拟化不同,不需要其他软件。
D 主机上的所有容器共享主机的调度程序,从而节省了额外资源的需求。
E 与虚拟机映像相比,容器状态(Docker或LXC映像)的大小很小,因此容器映像很容易分发。
F 容器中的资源管理是通过cgroup实现的。Cgroups不允许容器消耗比分配给它们更多的资源。虽然主机的所有资源都在虚拟机中可见,但无法使用。这可以通过在容器和主机上同时运行top或htop来实现。所有环境的输出看起来都很相似。
docker pull 拉取或者更新指定镜像
docker push 将镜像推送至远程仓库
docker rm 删除容器
docker rmi 删除镜像
docker images 列出所有镜像
docker ps 列出所有容器
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。