历时小半年总结之JAVA

2019年11月09日 阅读数:328
这篇文章主要向大家介绍历时小半年总结之JAVA,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

1、JavaSE

1线程

1)、进程与线程的区别?

:进程是全部线程的集合,每个线程是进程中的一条执行路径,线程只是一条执行路径。javascript

2)、为何要用多线程?

 :提升程序效率css

3)、多线程建立方式?

  :继承ThreadRunnable 接口。html

4)、是继承Thread类好仍是实现Runnable接口好?

:Runnable接口好,由于实现了接口还能够继续继承。继承Thread类不能再继承。前端

5)、你在哪里用到了多线程?

:主要能体现到多线程提升程序效率。java

举例:分批发送短信、迅雷多线程下载等。node

6)、什么是多线程安全?

:当多个线程同时共享,同一个全局变量或静态变量,作写的操做时,可能会发生数据冲突问题,也就是线程安全问题。作读操做是不会发生数据冲突问题。python

7)、如何解决多线程之间线程安全问题?

:使用多线程之间同步或使用锁(lock)mysql

8)、为何使用线程同步或使用锁能解决线程安全问题呢?

:将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。被包裹的代码执行完成后释放锁,让后才能让其余线程进行执行。这样的话就能够解决线程不安全问题。linux

9)、什么是多线程之间同步?

:当多个线程共享同一个资源,不会受到其余线程的干扰。nginx

10)、什么是同步代码块?

:就是将可能会发生线程安全问题的代码,给包括起来。只能让当前一个线程进行执行,被包裹的代码执行完成以后才能释放所,让后才能让其余线程进行执行。

11)、多线程同步的分类?

1.使用同步代码块?

synchronized(同一个数据){

 可能会发生线程冲突问题

}

   private Object mutex = new Object();// 自定义多线程同步锁

public void sale() {

synchronized (mutex) {

if (trainCount > 0) {

try {

Thread.sleep(10);

} catch (Exception e) {

}

System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");

trainCount--;}

}

}

 

2.使用同步函数

在方法上修饰synchronized 称为同步函数

public synchronized void sale() {

if (trainCount > 0) { 

try {

Thread.sleep(40);

} catch (Exception e) {

}

System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");

trainCount--;

}

}

 

3.静态同步函数

方法上加上static关键字,使用synchronized 关键字修饰 为静态同步函数

静态的同步函数使用的锁是  该函数所属字节码文件对象

12)、同步代码块与同步函数区别?

:

同步代码使用自定锁(明锁)

同步函数使用this

13)、同步函数与静态同步函数区别?

注意:有些面试会这样问:例如如今一个静态方法和一个非静态静态怎么实现同步?

:

同步函数使用this

静态同步函数使用字节码文件,也就是类.class

14)、什么是多线程死锁?

答:

同步中嵌套同步,没法释放锁的资源。

解决办法:同步中尽可能不要嵌套同步

15)、Wait()Notify ()区别?

Wait让当前线程有运行状态变为等待状态,和同步一块儿使用

Notify 唤醒如今正在等待的状态,和同步一块儿使用

16)、Wait()sleep()区别?

对于sleep()方法,咱们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法致使了程序暂停执行指定的时间,让出cpu该其余线程,可是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程当中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

获取对象锁进入运行状态。

17)、LockSynchronized区别

Lock 接口能够尝试非阻塞地获取锁 当前线程尝试获取锁。若是这一时刻锁没有被其余线程获取到,则成功获取并持有锁。
*Lock 接口能被中断地获取锁 与 synchronized 不一样,获取到锁的线程可以响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。

Lock 接口在指定的截止时间以前获取锁,若是截止时间到了依旧没法获取锁,则返回。

18)、Condition用法

 Condition的功能相似于在传统的线程技术中的,Object.wait()和Object.notify()的功能,

代码:

Condition condition = lock.newCondition();

res. condition.await();  相似wait

res. Condition. Signal() 相似notify

Signalall notifyALL

 

19)、如何中止线程?

  1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

    2.  使用stop方法强行终止线程(这个方法不推荐使用,由于stop和suspend、resume同样,也可能发生不可预料的结果)。

3.  使用interrupt方法中断线程。 线程阻塞状态

20)、什么是守护线程

Java有两种线程,一种是用户线程,另外一种是守护线程。

 进程不存在或主线程中止,守护线程也中止

 使用setDaemon(true)方法设置为守护线程

21)、join()方法做用

join做用是让其余线程变为等待只有当前线程执行完毕后,等待的线程才会被释放。

22)、线程三特性

多线程三大特性,原子性、可见性有序

原子性:保证数据一致性线程安全。

见性:对另外一个线程是否课件

有序:线程之间执行有顺序

23)、Java内存模型

共享内存模型指的就是Java内存模型(简称JMM)JMM决定一个线程对共享变量的写入时,对另外一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每一个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其余的硬件和编译器优化。

 

 

24)、什么Volatile做用

Volatile 关键字的做用是变量在多个线程之间可见。

25)、什么是AtomicInteger

AtomicInteger原子

26)、什么ThreadLocal

ThreadLocal提升一个线程的局部变量,访问某个线程拥有本身局部变量。

 当使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程提供独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其它线程所对应的副本。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,咱们先来了解一下:

void set(Object value)设置当前线程的线程局部变量的值。

public Object get()该方法返回当前线程所对应的线程局部变量。

public void remove()将当前线程局部变量的值删除,目的是为了减小内存的占用,该方法是JDK 5.0新增的方法。须要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,因此显式调用该方法清除线程的局部变量并非必须的操做,但它能够加快内存回收的速度。

protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,而且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

 

27)、什么是线程池?

 线程池是指在初始化一个多线程应用程序过程当中建立一个线程集合,而后在须要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量一般彻底取决于可用内存数量和应用程序的需求。然而,增长可用线程数量是可能的。线程池中的每一个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。

28)、线程池做用

基于如下几个缘由在多线程应用程序中使用线程是必须的:

  1. 线程池改进了一个应用程序的响应时间。因为线程池中的线程已经准备好且等待被分配任务,应用程序能够直接拿来使用而不用新建一个线程。

  2. 线程池节省了CLR 为每一个短生存周期任务建立一个完整的线程的开销并能够在任务完成后回收资源。

  3. 线程池根据当前在系统中运行的进程来优化线程时间片。

  4. 线程池容许咱们开启多个任务而不用为每一个线程设置属性。

  5. 线程池容许咱们为正在执行的任务的程序参数传递一个包含状态信息的对象引用。

  6. 线程池能够用来解决处理一个特定请求最大线程数量限制问题。

 

29)、线程池四种建立方式

Java经过Executorsjdk1.5并发包)提供四种线程池,分别为:
newCachedThreadPool建立一个可缓存线程池,若是线程池长度超过处理须要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 建立一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。

 

30)、JDK1.5并发包

名称

做用

Lock

Executors

线程

ReentrantLock

一个可重入的互斥锁定 Lock,功能相似synchronized,但要强大的多。

Condition

 Condition的功能相似于在传统的线程技术中的,Object.wait()和Object.notify()的功能,

 

ConcurrentHashMap

分段HasMap

AtomicInteger

原子

BlockingQueue

 

BlockingQueue 一般用于一个线程生产对象,而另一个线程消费这些对象的场景

ExecutorService

 

执行器服务

31)、锁的种类

1.自旋锁

自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其余线程改变时 才能进入临界区。以下

public class SpinLock {

 

private AtomicReference<Thread> sign = new AtomicReference<>();

 

public void lock() {

Thread current = Thread.currentThread();

while (!sign.compareAndSet(null, current)) {

}

}

 

public void unlock() {

Thread current = Thread.currentThread();

sign.compareAndSet(current, null);

}

}

 

2.互斥锁

所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. jdk1.5以前, 咱们一般使用synchronized机制控制多个线程对共享资源Lock接口及其实现类ReentrantLock

3.可重入锁

可重入锁,也叫作递归锁,指的是同一线程 外层函数得到锁以后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
JAVA环境下 ReentrantLock synchronized 都是 可重入锁

4.悲观

.悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其余事务,以及来自外部系统的事务处理)修改持保守态度,所以,在整个数据处理过程当中,将数据处于锁定状态。悲观锁的实现,每每依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,不然,即便在本系统中实现了加锁机制,也没法保证外部系 统不会修改数据)。

5.乐观锁

相对悲观锁而言,乐观锁机制采起了更加宽松的加锁机制。悲观锁大多数状况下依靠数据库的锁机制实现,以保证操做最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,这样的开销每每没法承受。 而乐观锁机制在必定程度上解决了这个问题。乐观锁,大可能是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增长一个版本标识,在基于数据库表的版本解决方案中,通常是经过为数据库表增长一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,以后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如 果提交的数据版本号大于数据库表当前版本号,则予以更新,不然认为是过时数据。

6.信号量

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们可以正确、合理的使用公共资源。 

二、集合

1)、List的三个子类的特色

ArrayList:

底层数据结构是数组,查询快,增删慢。线程不安全,效率高。

Vector:

底层数据结构是数组,查询快,增删慢。线程安全,效率低。

LinkedList:

底层数据结构是链表,查询慢,增删快。线程不安全,效率高。

2)、HashMapHashtable的区别

* HashtableJDK1.0版本出现的,是线程安全的,效率低,HashMapJDK1.2版本出现的,是线程不安全的,效率高

* Hashtable不能够存储null键和null,HashMap能够存储null键和null

3)、Set的线程不安全

 

3网络编程

1)、什么是Socket

Socket就是为网络服务提供的一种机制。

通信的两端都有Sokcet

网络通信其实就是Sokcet间的通信

数据在两个Sokcet间经过IO传输。

2)、TCPUDP在概念上的区别

udp: a、是面向无链接, 将数据及源的封装成数据包中,不须要创建创建链接

    b、每一个数据报的大小在限制64k内

    c、因无链接,是不可靠协议

    d、不须要创建链接,速度快

tcp: a、建议链接,造成传输数据的通道.

    b、在链接中进行大数据量传输,以字节流方式

    c 经过三次握手完成链接,是可靠协议

d 必须创建链接m效率会稍低

4设计模式

1)、什么是设计模式?

设计模式(Design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石同样。项目中合理的运用设计模式能够完美的解决不少问题,每种模式在如今中都有相应的原理来与之对应,每个模式描述了一个在咱们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被普遍应用的缘由。本章系Java之美[从菜鸟到高手演变]系列之设计模式,咱们会以理论与实践相结合的方式来进行本章的学习,但愿广大程序爱好者,学好设计模式,作一个优秀的软件工程师!

2)、设计模式的分类?

整体来讲设计模式分为三大类:

建立型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。用一个图片来总体描述一下:

 

 

3)、设计模式的六大原则

一、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序须要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。因此一句话归纳就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,咱们须要使用接口和抽象类,后面的具体设计中咱们会提到这点。

二、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类能够出现的地方,子类必定能够出现。 LSP是继承复用的基石,只有当衍生类能够替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也可以在基类的基础上增长新的行为。里氏代换原则是对-原则的补充。实现-原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,因此里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

三、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

四、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。仍是一个下降类之间的耦合度的意思,从这儿咱们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。因此上文中屡次出现:下降依赖,下降耦合。

五、迪米特法则(最少知道原则)(Demeter Principle)

为何叫最少知道原则,就是说:一个实体应当尽可能少的与其余实体之间发生相互做用,使得系统功能模块相对独立。

六、合成复用原则(Composite Reuse Principle)

原则是尽可能使用合成/聚合的方式,而不是使用继承。

4)、模式

1.什么是单例模式?

 单例保证一个对象JVM中只能有一个实例,常见单例 懒汉式饿汉式

 什么是懒汉式,就是须要的才会去实例化,线程不安全。

 什么饿汉,就是当class文件被加载的时候,初始化,天生线程安全。

2.单例写法

 

 懒汉式代码

 

class SingletonTest {

public static void main(String[] args) {

Singleton sl1 = Singleton.getSingleton();

Singleton sl2 = Singleton.getSingleton();

System.out.println(sl1 == sl2);

}

}

 

public class Singleton {

// 当须要的才会被实例化

private static Singleton singleton;

 

private Singleton() {

 

}

 

synchronized public static Singleton getSingleton() {

if (singleton == null) {

singleton = new Singleton();

}

return singleton;

}

 

}

 

饿汉代码

class SingletonTest1 {

public static void main(String[] args) {

Singleton1 sl1 = Singleton1.getSingleton();

Singleton1 sl2 = Singleton1.getSingleton();

System.out.println((sl1 == sl2)+"-");

}

}

 

public class Singleton1 {

//当class 文件被加载初始化

private static Singleton1 singleton = new Singleton1();

 

private Singleton1() {

 

}

 

public static Singleton1 getSingleton() {

return singleton;

}

 

}

 

 

5)、工厂模式

 1.什么工厂模式?

实现建立者和调用者分离

 2.简单工厂代码

public interface Car {

public void run();

}

public class AoDi implements Car {

@Override

public void run() {

     System.out.println("奥迪....");

}

}

public interface Car {

public void run();

}

 

 

public class CarFactory {

static public Car createCar(String carName) {

Car car = null;

if (carName.equals("奥迪")) {

car = new AoDi();

} else if (carName.equals("奔驰")) {

car = new BenChi();

}

return car;

 

}

public static void main(String[] args) {

Car car1 = CarFactory.createCar("奥迪");

Car car2 = CarFactory.createCar("奔驰");

car1.run();

car2.run();

}

}

 

 3.工厂方法

public interface Car {

 

public void run();

 

}

 

public class AoDi implements Car {

 

@Override

public void run() {

System.out.println("奥迪....");

}

 

}

 

 

 

public class BenChi implements Car {

 

@Override

public void run() {

System.out.println("奔驰....");

}

 

}

 

 

 

 

public class AoDiChiFactory {

static public Car createCar() {

return new AoDi();

}

}

 

public interface BenChiFactory  {

static public Car createCar() {

return new BenChi();

}

}

public class Main {

 

public static void main(String[] args) {

Car c1 = AoDiChiFactory.createCar();

Car c2 = BenChiFactory.createCar();

c1.run();

c2.run();

}

 

}

 

 

 

6)、代理模式

1.什么是代理?

经过代理控制对象的访问,能够详细访问某个对象的方法,这个方法调用处理,或调用处理。(AOP微实现)  ,AOP核心技术面向切面编程。

 

 

2.代理应用场景

安全代理 能够屏蔽真实角色

远程代理 远程调用代理类RMI

延迟加载 加载轻量级代理类,真正须要加载真实

3.代理分类

静态代理(静态定义代理类)

动态代理(动态生成代理类)

Jdk自带动态代理

Cglib javaassist(字节码操做库

4.静态代理

静态代理须要本身生成代理类

public class XiaoMing implements Hose {

@Override

public void mai() {

System.out.println("我是小明,我要买房啦!!!!haha ");

}

}

class Proxy  implements Hose {

private XiaoMing xiaoMing;

public Proxy(XiaoMing xiaoMing) {

this.xiaoMing = xiaoMing;

}

public void mai() {

System.out.println("我是中介 看你买房开始啦!");

xiaoMing.mai();

System.out.println("我是中介 看你买房结束啦!");

}

public static void main(String[] args) {

Hose proxy = new Proxy(new XiaoMing());

proxy.mai();

}

}

 

5.JDK动态代理(不须要生成代理类)

实现InvocationHandler 就能够了。

public interface Hose {

 

/**

 *

 * @methodDesc: 功能描述:(买房代理)

 * @author: 余胜军

 * @param:

 * @createTime:2017827日 上午2:54:34

 * @returnType: void

 * @copyright:上海每特教育科技有限公司

 */

public void mai();

 

}

 

 

public class XiaoMing implements Hose {

 

@Override

public void mai() {

System.out.println("我是小明,我要买房啦!!!!haha ");

}

 

}

 

public class JDKProxy implements InvocationHandler {

private Object tarjet;

 

public JDKProxy(Object tarjet) {

this.tarjet = tarjet;

}

 

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("我是房产中介.....开始监听你买房啦!");

Object oj = method.invoke(tarjet, args);

System.out.println("我是房产中介.....结束监听你买房啦!");

return oj;

 

}

 

}

 

class Test222 {

public static void main(String[] args) {

XiaoMing xiaoMing = new XiaoMing();

JDKProxy jdkProxy = new JDKProxy(xiaoMing);

Hose hose=(Hose) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(), xiaoMing.getClass().getInterfaces(), jdkProxy);

hose.mai();

}

 

}

 

6.CGLIB动态代理

实现

 

import java.lang.reflect.Method;

 

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

 

public class Cglib implements MethodInterceptor {

 

@Override

public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

System.out.println("我是买房中介 , 开始监听你买房了....");

Object invokeSuper = methodProxy.invokeSuper(o, args);

System.out.println("我是买房中介 , 开结束你买房了....");

return invokeSuper;

 

}

 

}

 

class Test22222 {

public static void main(String[] args) {

Cglib cglib = new Cglib();

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(XiaoMing.class);

enhancer.setCallback(cglib);

Hose hose = (Hose) enhancer.create();

hose.mai();

}

}

 

 

7.CGLIBJDK动态代理区别

jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来讲,反射机制在生成类的过程当中比较高效,而asm在生成类以后的相关执行过程当中比较高效(能够经过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。若是没有上述前提,jdk动态代理不能应用。

:asm其实就是java字节码控制.

 

 

5其余

1)、什么是注解?

Jdk1.5新增新技术,注解。不少框架为了简化代码,都会提供有些注解。能够理解为插件,是代码级别的插件,在类的方法上写:@XXX,就是在代码上插入了一个插件。

注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的做用

注解分类:内置注解(也成为元注解 jdk 自带注解)自定义注解(Spring框架

 

2)、如何定义一个注解?

代码:

使用@interface 定义注解

@Target(value = { ElementType.METHOD, ElementType.TYPE })

@Retention(RetentionPolicy.RUNTIME)

public @interface OneAnnotation {

int beanId() default 0;

String className() default "";

String[]arrays();

}

 

 

3)、什么数据交换格式

客户端与服务器经常使用数据交换格式xml、json、html

4)、数据交换格式用场景

移动(安卓IOS)通信方式采用http协议+JSON格式 走restful风格

不少互联网项目都采用Http协议+JSON

由于xml比较重WebService服务采用http+xml格式 银行项目使用比较多

同窗们能够思考下?移动PC服务器是接口是怎么设计的?

 

5)、JSON解析框架有哪些

fastjson(阿里)gson(谷歌)、jackson(SpringMVC自带)

6)、XML解析方式?

Dom4j、SaxPull

7)、Dom4jSax区别

 dom4j不适合大文件的解析,由于它是一会儿将文件加载到内存中,因此有可能出现内存溢出,sax是基于事件来对xml进行解析的,因此他能够解析大文件的xml,也正是由于如此,因此dom4j能够对xml进行灵活的增删改查和导航,而sax没有这么强的灵活性,因此sax常常是用来解析大型xml文件,而要对xml文件进行一些灵活(crud)操做就用dom4j。

 

8)、XMLJSON区别

Xml是重量级数据交换格式,占宽带比较大。

JSON是轻量级交换格式,xml占宽带小。

全部不少互联网公司都会使用json做为数据交换格式

不少银行项目,大多数仍是在使用xml。

9)、反射

1.什么是Java反射

就是正在运行,动态获取这个类的全部信息。

2.反射机制的做用

  -1,反编译:.class-->.java

   -2.经过反射机制访问java对象的属性,方法,构造方法等;

3.反射机制的应用场景

Jdbc 加载驱动-----

Spring ioc

框架

4.反射机制获取类有三种方法

//第一种方式:  

Classc1 = Class.forName("Employee");  

//第二种方式:  

//java中每一个类型都有class 属性.  

Classc2 = Employee.class;  

   

//第三种方式:  

//java语言中任何一个java对象都有getClass 方法  

Employeee = new Employee();  

Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)  

 

5.反射建立对象的方式

Class<?> forName = Class.forName("com.itmayiedu.entity.User");

// 建立此Class 对象所表示的类的一个新实例 调用了User的无参数构造方法.

Object newInstance = forName.newInstance();

实例化有参构造函数

Class<?> forName = Class.forName("com.itmayiedu.entity.User");

Constructor<?> constructor = forName.getConstructor(String.class, String.class);

User newInstance = (User) constructor.newInstance("123", "123");

 

6.反射建立api

方法名称

做用

getDeclaredMethods []

获取该类的全部方法

getReturnType()

获取该类的返回值

getParameterTypes()

获取传入参数

getDeclaredFields()

获取该类的全部字段

setAccessible

容许访问私有成员

7.使用反射为类私有属性赋值

// 获取当前类class地址

Class<?> forName = Class.forName("com.itmayiedu.entity.User");

// 使用反射实例化对象 无参数构造函数

Object newInstance = forName.newInstance();

// 获取当前类的 userId字段

Field declaredField = forName.getDeclaredField("userId");

// 容许操做私有成员

declaredField.setAccessible(true);

// 设置值

declaredField.set(newInstance, "123");

User user = (User) newInstance;

System.out.println(user.getUserId());

 

 

 

 

 

 

 

 

 

 

6JVM参数调优

1)、Java虚拟机原理 

所谓虚拟机,就是一台虚拟的机器。他是一款软件,用来执行一系列虚拟计算指令,大致上虚拟机能够分为

系统虚拟机和程序虚拟机, 大名鼎鼎的Visual BoxVmare就属于系统虚拟机,他们彻底是对物理计算的仿真,

提供了一个能够运行完整操做系统的软件平台。

 程序虚拟机典型代码就是Java虚拟机,它专门为执行单个计算程序而计算,在Java虚拟机中执行的指令咱们成为Java

本身码指令。不管是系统虚拟机仍是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。

 Java发展至今,出现过不少虚拟机,作初Sun使用的一款叫ClassIcJava虚拟机,到如今引用最普遍的是HotSpot虚拟

机,除了Sum意外,还有BEAJrockit,目前JrockitHostSopt都被oralce收入旗下,大有整合的趋势。

 

2)、Java内存结构

 

 

一、 类加载子系统:负责从文件系统或者网络加载Class信息,加载的信息存放在一块称之方法区的内存空间。

二、 方法区:就是存放类的信息、常量信息、常量池信息、包括字符串字面量和数字常量等。

三、 Java堆:在Java虚拟机启动的时候创建Java堆,它是Java程序最主要的内存工做区域,几乎全部的对象实例都存放到

Java堆中,堆空间是全部线程共享。

四、 直接内存:JavaNio库容许Java程序直接内存,从而提升性能,一般直接内存速度会优于Java堆。读写频繁的场合可能会考虑使用。

五、 每一个虚拟机线程都有一个私有栈,一个线程的Java栈在线程建立的时候被建立,Java栈保存着局部变量、方法参数、同事Java的方法调用、

返回值等。

六、 本地方法栈,最大不一样为本地方法栈用于本地方法调用。Java虚拟机容许Java直接调用本地方法(经过使用C语言写)

七、 垃圾收集系统是Java的核心,也是不可少的,Java有一套本身进行垃圾清理的机制,开发人员无需手工清理,下一节课详细讲。

八、 PCProgram Couneter)寄存器也是每一个线程私有的空间, Java虚拟机会为每一个线程建立PC寄存器,在任意时刻,

一个Java线程老是在执行一个方法,这个方法称为当前方法,若是当前方法不是本地方法,PC寄存器总会执行当前正在被执行的指令,

若是是本地方法,则PC寄存器值为Underfined,寄存器存放若是当前执行环境指针、程序技术器、操做栈指针、计算的变量指针等信息。

九、 虚拟机核心的组件就是执行引擎,它负责执行虚拟机的字节码,通常户先进行编译成机器码后执行。

3)、、栈、方法区概念区别

1.Java

堆内存用于存放由new建立的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还能够在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,之后就能够在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量至关于为数组或者对象起的一个别名,或者代号。

根据垃圾回收机制的不一样,Java堆有可能拥有不一样的结构,最为常见的就是将整个Java堆分为

新生代和老年代。其中新声带存放新生的对象或者年龄不大的对象,老年代则存放老年对象。

新生代分为den区、s0区、s1区,s0s1也被称为fromto区域,他们是两块大小相等而且能够互相角色的空间。

绝大多数状况下,对象首先分配在eden区,在新生代回收后,若是对象还存活,则进入s0s1区,以后每通过一次

新生代回收,若是对象存活则它的年龄就加1,对象达到必定的年龄后,则进入老年代。

 

 

2.Java

Java栈是一块线程私有的空间,一个栈,通常由三部分组成:局部变量表、操做数据栈和帧数据区

局部变量表:用于报错函数的参数及局部变量

操做数栈:主要保存计算过程的中间结果,同时做为计算过程当中的变量临时的存储空间。

帧数据区:除了局部变量表和操做数据栈之外,栈还须要一些数据来支持常量池的解析,这里帧数据区保存着

访问常量池的指针,方便计程序访问常量池,另外当函数返回或出现异常时卖虚拟机子必须有一个异常处理表,方便发送异常

的时候找到异常的代码,所以异常处理表也是帧数据区的一部分。

 

3.Java方法

Java方法区和堆同样,方法区是一块全部线程共享的内存区域,他保存系统的类信息。

好比类的字段、方法、常量池等。方法区的大小决定系统能够保存多少个类。若是系统

定义太多的类,致使方法区溢出。虚拟机一样会抛出内存溢出的错误。方法区能够理解

为永久区。

 

4)、虚拟机参数配置

1.什么虚拟机参数配置

在虚拟机运行的过程当中,若是能够跟踪系统的运行状态,那么对于问题的故障

排查会有必定的帮助,为此,在虚拟机提供了一些跟踪系统状态的参数,使用

给定的参数执行Java虚拟机,就能够在系统运行时打印相关日志,用于分析实际

问题。咱们进行虚拟机参数配置,其实就是围绕着堆、栈、方法区、进行配置。

说下 熟悉那些jvm参数调优

2.的参数配置

-XX:+PrintGC      每次触发GC的时候打印相关日志

-XX:+UseSerialGC      串行回收

-XX:+PrintGCDetails  更详细的GC日志

-Xms               堆初始值

-Xmx               堆最大可用值

-Xmn               新生代堆最大可用值

-XX:SurvivorRatio     来设置新代中eden空间from/to空间的比例.

-XX:SurvivorRatio=eden/from=den/to

总结:在实际工做中,咱们能够直接将初始的堆大小与最大堆大小相等,

这样的好处是能够减小程序运行时垃圾回收次数,从而提升效率。

 

-XX:SurvivorRatio     来设置新代中eden空间from/to空间的比例.

5)、设置最大堆内存

参数: -Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags

/**

 * jvm参数设置

 *

 * @author Administrator

 *

 */

public class JvmDemo01 {

 

public static void main(String[] args) throws InterruptedException {

byte[] b1 = new byte[1 * 1024 * 1024];

System.out.println("分配了1m");

jvmInfo();

Thread.sleep(3000);

byte[] b2 = new byte[4 * 1024 * 1024];

System.out.println("分配了4m");

Thread.sleep(3000);

jvmInfo();

 

}

 

/**

 * 转换为m

 *

 * @param maxMemory

 * @return

 */

static private String toM(long maxMemory) {

float num = (float) maxMemory / (1024 * 1024);

DecimalFormat df = new DecimalFormat("0.00");// 格式化小数

String s = df.format(num);// 返回的是String类型

return s;

}

 

static private void jvmInfo() {

// 最大内存

long maxMemory = Runtime.getRuntime().maxMemory();

System.out.println("maxMemory:" + maxMemory + ",转换为M:" + toM(maxMemory));

// 当前空闲内存

long freeMemory = Runtime.getRuntime().freeMemory();

System.out.println("freeMemory:" +freeMemory+",转换为M:"+toM(freeMemory));

// 已经使用内存

long totalMemory = Runtime.getRuntime().totalMemory();

System.out.println("totalMemory:" +totalMemory+",转换为M"+toM(totalMemory));

}

 

}

 

6)、设置新与老年代优化参数

-Xmn    新生代大小,通常设为整个堆的1/3到1/4左右

-XX:SurvivorRatio    设置新生代中eden区和from/to空间的比例关系n/1

1.设置新生代比例参数

参数: -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

public class JvmDemo02 {

 

 public static void main(String[] args) {

//-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

 byte [] b = null;

 for (int i = 0; i < 10; i++) {

b =new byte[1*1024*1024];

}

 

}

 

}

 

 

2.设置新老年代代参数

-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC

-XX:NewRatio=2

总结:不一样的堆分布状况,对系统执行会产生必定的影响,在实际工做中,

应该根据系统的特色作出合理的配置,基本策略:尽量将对象预留在新生代,

减小老年代的GC次数。

除了能够设置新生代的绝对大小(-Xmn),能够使用(-XX:NewRatio)设置新生代和老年

代的比例:-XX:NewRatio=老年代/新生代

 

3.内存溢出解决办法

-1.设置内存大小

错误缘由: java.lang.OutOfMemoryError: Java heap space

解决办法:设置堆内存大小 -Xms1m -Xmx70m -XX:+HeapDumpOnOutOfMemoryError

 

public static void main(String[] args) throws InterruptedException {

List<Object> list = new ArrayList<>();

Thread.sleep(3000);

jvmInfo();

for (int i = 0; i < 10; i++) {

System.out.println("i:"+i);

Byte [] bytes=new Byte[1*1024*1024];

list.add(bytes);

jvmInfo();

}

System.out.println("添加成功...");

}

 

-2.设置栈内存大小

错误缘由: java.lang.StackOverflowError

栈溢出 产生于递归调用,循环遍历是不会的,可是循环方法里面产生递归调用, 也会发生栈溢出。

解决办法:设置线程最大调用深度

-Xss5m 设置最大调用深度

public class JvmDemo04 {

 private static int count;

 public static void count(){

try {

 count++;

 count();

} catch (Throwable e) {

System.out.println("最大深度:"+count);

e.printStackTrace();

}

 }

 public static void main(String[] args) {

 count();

}

}

 

4.Tomcat内存溢出catalina.sh 修改JVM内存大小
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"

7)、内存溢出与内存泄露的区别

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;好比申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,没法释放已申请的内存空间,一次内存泄露危害能够忽略,但内存泄露堆积后果很严重,不管多少内存,早晚会被占光。

memory leak会最终会致使out of memory

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能知足需求,因而产生溢出。 

    内存泄漏是指你向系统申请分配内存进行使用(new),但是使用完了之后却不归还(delete),结果你申请到的那块内存你本身也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给须要的程序。一个盘子用尽各类方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再作进栈一定产生空间溢出,叫上溢,栈空时再作退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出

   以发生的方式来分类,内存泄漏能够分为4类: 

1. 常发性内存泄漏。发生内存泄漏的代码会被屡次执行到,每次被执行的时候都会致使一块内存泄漏。 
2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操做过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。因此测试环境和测试方法对检测内存泄漏相当重要。 
3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者因为算法上的缺陷,致使总会有一块仅且一块内存发生泄漏。好比,在类的构造函数中分配内存,在析构函数中却没有释放该内存,因此内存泄漏只会发生一次。 
4. 隐式内存泄漏。程序在运行过程当中不停的分配内存,可是直到结束的时候才释放内存。严格的说这里并无发生内存泄漏,由于最终程序释放了全部申请的内存。可是对于一个服务器程序,须要运行几天,几周甚至几个月,不及时释放内存也可能致使最终耗尽系统的全部内存。因此,咱们称这类内存泄漏为隐式内存泄漏。 

从用户使用程序的角度来看,内存泄漏自己不会产生什么危害,做为通常的用户,根本感受不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统全部的内存。从这个角度来讲,一次性内存泄漏并无什么危害,由于它不会堆积,而隐式内存泄漏危害性则很是大,由于较之于常发性和偶发性内存泄漏它更难被检测到 

 

 

8)、JVM参数调优总结

    JVM启动参数中,能够设置跟内存、垃圾回收相关的一些参数设置,默认状况不作任何设置JVM会工做的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能得到最佳性能。经过设置咱们但愿达到一些目标:

  • GC的时间足够的小
  • GC的次数足够的少
  • 发生Full GC的周期足够的长

  前两个目前是相悖的,要想GC时间小必需要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,咱们只能取其平衡。

   1)针对JVM堆的设置,通常能够经过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,咱们一般把最大、最小设置为相同的值
   2)年轻代和年老代将根据默认的比例(12)分配堆内存,能够经过调整两者之间的比率NewRadio来调整两者之间的大小,也能够针对回收代,好比年轻代,经过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。一样,为了防止年轻代的堆收缩,咱们一般会把-XX:newSize -XX:MaxNewSize设置为一样大小

   3)年轻代和年老代设置多大才算合理?这个我问题毫无疑问是没有答案的,不然也就不会有调优。咱们观察一下两者大小变化有哪些影响

  • 更大的年轻代必然致使更小的年老代,大的年轻代会延长普通GC的周期,但会增长每次GC的时间;小的年老代会致使更频繁的Full GC
  • 更小的年轻代必然致使更大年老代,小的年轻代会致使普通GC很频繁,但每次的GC时间会更短;大的年老代会减小Full GC的频率
  • 如何选择应该依赖应用程序对象生命周期的分布状况:若是应用存在大量的临时对象,应该选择更大的年轻代;若是存在相对较多的持久对象,年老代应该适当增大。但不少应用都没有这样明显的特性,在抉择时应该根据如下两点:(A)本着Full GC尽可能少的原则,让年老代尽可能缓存经常使用对象,JVM的默认比例12也是这个道理 (B)经过观察应用一段时间,看其余在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际状况加大年轻代,好比能够把比例控制在11。但应该给年老代至少预留1/3的增加空间

 

 

 

 

7垃圾回收机制

1)、垃圾回收机制概述

 Java语言中一个显著的特色就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候再也不须要考虑内存管理。因为有个垃圾回收机制,Java中的对象再也不有“做用域”的概念,只有对象的引用才有“做用域”。垃圾回收能够有效的防止内存泄露,有效的使用空闲的内存。

  ps:内存泄露是指该内存空间使用完毕以后未回收,在不涉及复杂数据结构的通常状况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序须要它的时间长度,咱们有时也将其称为“对象游离”。

2)、垃圾回收简要过程

  这里必须点出一个很重要的误区:不可达的对象并不会立刻就会被直接回收,而是至少要通过两次标记的过程。 
        第一次被标记过的对象,会检查该对象是否重写了finalize()方法。若是重写了该方法,则将其放入一个F-Query队列中,不然,直接将对象加入“即将回收”集合。在第二次标记以前,F-Query队列中的全部对象会逐个执行finalize()方法,可是不保证该队列中全部对象的finalize()方法都能被执行,这是由于JVM建立一个低优先级的线程去运行此队列中的方法,极可能在没有遍历完以前,就已经被剥夺了运行的权利。那么运行finalize()方法的意义何在呢?这是对象避免本身被清理的最后手段:若是在执行finalize()方法的过程当中,使得此对象从新与GC Roots引用链相连,则会在第二次标记过程当中将此对象从F-Query队列中清除,避免在此次回收中被清除,恢复成了一个“正常”的对象。但显然这种好事不能无限的发生,对于曾经执行过一次finalize()的对象来讲,以后若是再被标记,则不会再执行finalize()方法,只能等待被清除的命运。 
        以后,GC将对F-Queue中的对象进行第二次小规模的标记,将队列中从新与GC Roots引用链恢复链接的对象清除出“即将回收”集合。全部此集合中的内容将被回收。

3)、手动GC回收

public class JVMDemo05 {

public static void main(String[] args) {

JVMDemo05 jvmDemo05 = new JVMDemo05();

//jvmDemo05 = null;

System.gc();

}

protected void finalize() throws Throwable {

       System.out.println("gc在回收对象...");

}

}

 

4)、finalize做用

Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,作必要的清理工做。这个方法是由垃圾收集器在肯定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,所以全部的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其余清理工做。finalize()方法是在垃圾收集器删除对象以前对这个对象调用的。

 

5)、垃圾回收机制算法

1.引用计数法

a.概述

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任什么时候刻计数器都为0的对象就是再也不被使用的,垃圾收集器将回收该对象使用的内存。

b.优缺点

优势:

引用计数收集器能够很快的执行,交织在程序运行中。对程序须要不被长时间打断的实时环境比较有利。

缺点:

没法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0.并且每次加减很是浪费内存。

2.标记清除算法

标记-清除(Mark-Sweep)算法顾名思义,主要就是两个动做,一个是标记,另外一个就是清除。

标记就是根据特定的算法(如:引用计数算法,可达性分析算法等)标出内存中哪些对象能够回收,哪些对象还要继续用。

标记指示回收,那就直接收掉;标记指示对象还能用,那就原地不动留下。

 

缺点

  1. 标记与清除效率低;
  2. 清除以后内存会产生大量碎片;

因此碎片这个问题还得处理,怎么处理,看标记-整理算法。

3.复制算法

S0和s1将可用内存按容量分红大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另外一块内存上去,而后把使用过的内存空间一次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂状况,只须要移动堆顶指针,按顺序分配内存便可,实现简单,运行高效。

复制算法的缺点显而易见,可以使用的内存降为原来一半。

复制算法用于在新生代垃圾回收

4.标记-压缩算法

标记压缩法在标记清除基础之上作了优化,把存活的对象压缩到内存一端,然后进行垃圾清理。(java中老年代使用的就是标记压缩法)

5.分代收集算法

根据内存中对象的存活周期不一样,将内存划分为几块,java的虚拟机中通常把内存划分为新生代和年老代,当新建立对象时通常在新生代中分配内存空间,当新生代垃圾收集器回收几回以后仍然存活的对象会被移动到年老代内存中,当大对象在新生代中没法找到足够的连续内存时也直接在年老代中建立。

于新生代和老年代来讲,生代回收频率很高,可是每次回收耗时很短,老年代回收频率较低,可是耗时会相对较长,因此应该尽可能减小老年代的GC.

 

6.为何老年代使用标记压缩、生代使用复制算法。

7.垃圾回收时的停顿现象

 

垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器能够更高效的执行,大部分状况下,会要求系统进如一个停顿的状态。停顿的目的是为了终止全部的应用线程,只有这样的系统才不会有新垃圾的产生。同时停顿保证了系统状态在某一个瞬间的一致性,也有利于更好的标记垃圾对象。所以在垃圾回收时,都会产生应用程序的停顿。

6)、垃圾收集器

1.什么Java垃圾回收器

Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的自动分配(Memory Allocation)、自动回收(Garbage Collect)功能,这两个操做都发生在Java堆上(一段内存快)。某一个时点,一个对象若是有一个以上的引用(Rreference)指向它,那么该对象就为活着的(Live),不然死亡(Dead),视为垃圾,可被垃圾回收器回收再利用。垃圾回收操做须要消耗CPU、线程、时间等资源,因此容易理解的是垃圾回收操做不是实时的发生(对象死亡立刻释放),当内存消耗完或者是达到某一个指标(Threshold,使用内存占总内存的比列,好比0.75)时,触发垃圾回收操做。有一个对象死亡的例外,java.lang.Thread类型的对象即便没有引用,只要线程还在运行,就不会被回收。

2.串行回收器(Serial Collector)

单线程执行回收操做,回收期间暂停全部应用线程的执行,client模式下的默认回收器,经过-XX:+UseSerialGC命令行可选项强制指定。参数能够设置使用新生代老年代串行回收器

年轻代的回收算法(Minor Collection)
Eden区的存活对象移到To区,To区装不下直接移到年老代,把From区的移到To区,To区装不下直接移到年老代,From区里面年龄很大的升级到年老代。 回收结束以后,EdenFrom区都为空,此时把FromTo的功能互换,FromToToFrom,每一轮回收以前To都是空的。设计的选型为复制。

年老代的回收算法(Full Collection)
年老代的回收分为三个步骤,标记(Mark)、清除(Sweep)、合并(Compact)。标记阶段把全部存活的对象标记出来,清除阶段释放全部死亡的对象,合并阶段 把全部活着的对象合并到年老代的前部分,把空闲的片断都留到后面。设计的选型为合并,减小内存的碎片。

3.并行回收

a并行回收器(ParNew回收器)


并行回收器在串行回收器基础上作了改进,他能够使用多个线程同时进行垃
圾回收,对于计算能力强的计算机而言,能够有效的缩短垃圾回收所需的尖
际时间。
ParNew回收器是一个工做在新生代的垃圾收集器,他只是简单的将串行回收
器多线程快他的回收策略和算法和串行回收器同样。
使用XX:+UseParNewGC 新生代ParNew回收器,老年代则使用市行回收器
ParNew回收器工做时的线程数量能够使用XX:ParaleiGCThreads参数指
定,通常最好和计算机的CPU至关,避免过多的栽程影响性能。

b并行回收集器(ParallelGC)

老年代ParallelOldGC回收器也是一种多线程的回收器,和新生代的
ParallelGC回收器同样,也是一种关往吞吐量的回收器,他使用了标记压缩
算法进行实现。
-XX:+UseParallelOldGC 进行设置
-XX:+ParallelCThread也能够设置垃圾收集时的线程教量。

 

4.CMS(并发GC)收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是基于“标记-清除”算法实现的,整个收集过程大体分为4个步骤:

.初始标记(CMS initial mark)

.并发标记(CMS concurrenr mark)

.从新标记(CMS remark)

.并发清除(CMS concurrent sweep)

     其中初始标记、从新标记这两个步骤任然须要停顿其余用户线程。初始标记仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快,并发标记阶段是进行GC ROOTS 根搜索算法阶段,会断定对象是否存活。而从新标记阶段则是为了修正并发标记期间,因用户程序继续运行而致使标记产生变更的那一部分对象的标记记录,这个阶段的停顿时间会被初始标记阶段稍长,但比并发标记阶段要短。

     因为整个过程当中耗时最长的并发标记和并发清除过程当中,收集器线程均可以与用户线程一块儿工做,因此总体来讲,CMS收集器的内存回收过程是与用户线程一块儿并发执行的。

CMS收集器的优势:并发收集、低停顿,可是CMS还远远达不到完美,器主要有三个显著缺点:

CMS收集器对CPU资源很是敏感。在并发阶段,虽然不会致使用户线程停顿,可是会占用CPU资源而致使引用程序变慢,总吞吐量降低。CMS默认启动的回收线程数是:(CPU数量+3) / 4

CMS收集器没法处理浮动垃圾,可能出现“Concurrent Mode Failure“,失败后而致使另外一次Full  GC的产生。因为CMS并发清理阶段用户线程还在运行,伴随程序的运行自热会有新的垃圾不断产生,这一部分垃圾出如今标记过程以后,CMS没法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为“浮动垃圾”。也是因为在垃圾收集阶段用户线程还须要运行,
即须要预留足够的内存空间给用户线程使用,所以CMS收集器不能像其余收集器那样等到老年代几乎彻底被填满了再进行收集,须要预留一部份内存空间提供并发收集时的程序运做使用。在默认设置下,CMS收集器在老年代使用了68%的空间时就会被激活,也能够经过参数-XX:CMSInitiatingOccupancyFraction的值来提供触发百分比,以下降内存回收次数提升性能。要是CMS运行期间预留的内存没法知足程序其余线程须要,就会出现“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来从新进行老年代的垃圾收集,这样停顿时间就很长了。因此说参数-XX:CMSInitiatingOccupancyFraction设置的太高将会很容易致使“Concurrent Mode Failure”失败,性能反而下降。

最后一个缺点,CMS是基于“标记-清除”算法实现的收集器,使用“标记-清除”算法收集后,会产生大量碎片。空间碎片太多时,将会给对象分配带来不少麻烦,好比说大对象,内存空间找不到连续的空间来分配不得不提早触发一次Full  GC。为了解决这个问题,CMS收集器提供了一个-XX:UseCMSCompactAtFullCollection开关参数,用于在Full  GC以后增长一个碎片整理过程,还可经过-XX:CMSFullGCBeforeCompaction参数设置执行多少次不压缩的Full  GC以后,跟着来一次碎片整理过程。

5.G1回收器

G1回收器(Garbage-First)实在]dk1.7中提出的垃圾回收器,从长期目标来看是为了取代CMS回收器,G1回收器拥有独特的垃圾回收策略,G1属于分代垃圾回收器,区分新生代和老年代,依然有eden和from/to区,它并不要求整个eden区或者新生代、老年代的空间都连续,它使用了分区算法。
并行性: G1回收期间可多线程同时工做。
井发性G1拥有与应用程序交替执行能力,部分工做可与应用程序同时执行,在整个GC期间不会彻底阻塞应用程序。
分代GC:G1依然是一个分代的收集器,可是它是非两新生代和老年代一杯政的杂尊。
空间基理,G1在国收过程当中,不会微CMS那样在若千tacAy 要进行碎片整理。
G1
来用了有效复制对象的方式,减小空间碎片。
利得程,用于分区的缘由,G能够贝造取都分区城进行回收,帽小了国收的格想,
提高了性能。
使用.XXX:+UseG1GC 应用G1收集器,
Mills指定最大停顿时间
使用-XX:MaxGCPausel
设置并行回收的线程数量
使用-XX:ParallelGCThreads

 

7)、调优总结

初始堆值和最大内存内存越大,吞吐量就越高。

最好使用并行收集器,由于并行手机器速度比串行吞吐量高,速度快。

设置堆内存新生的比例和老年代的比例最好1:2或者1:3

减小GC老年代的回收。

 

8MySQL优化

1)、MySQL如何优化

表的设计合理化(符合3NF)

添加适当索引(index) [四种: 普通索引、主键索引、惟一索引unique、全文索引]

SQL语句优化

分表技术(水平分割、垂直分割)

读写[写: update/delete/add]分离

存储过程 [模块化编程,能够提升速度]

mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ]

mysql服务器硬件升级

定时的去清除不须要的数据,定时进行碎片整理(MyISAM)

2)、数据库设计

3)、什么是数据库范式

为了创建冗余较小、结构合理的数据库,设计数据库时必须遵循必定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须知足必定的范式。

4)、数据库三大范式

第一范式:1NF是对属性的原子性约束,要求属性(列)具备原子性,不可再分解;(只要是关系型数据库都知足1NF)

第二范式:2NF是对记录的唯一性约束,表中的记录是惟一的, 就知足2NF, 一般咱们设计一个主键来实现,主键不能包含业务逻辑。

第三范式:3NF是对字段冗余性的约束,它要求字段没有冗余。 没有冗余的数据库设计能够作到。

可是,没有冗余的数据库未必是最好的数据库,有时为了提升运行效率,就必须下降范式标准,适当保留冗余数据。具体作法是: 在概念数据模型设计时遵照第三范式,下降范式标准的工做放到物理数据模型设计时考虑。下降范式就是增长字段,容许冗余。

5)、慢查询

1.什么是慢查询

  MySQL默认10秒内没有响应SQL结果,为慢查询

能够去修改MySQL慢查询默认时间

2.如何修改慢查询

--查询慢查询时间

show variables like 'long_query_time';

--修改慢查询时间

set long_query_time=1; ---可是重启mysql以后,long_query_time依然是my.ini中的值

 

 

6)、如何将慢查询定位到日志中

在默认状况下,咱们的mysql不会记录慢查询,须要在启动mysql时候,指定记录慢查询才能够

bin\mysqld.exe --safe-mode  --slow-query-log [mysql5.5 能够在my.ini指定](安全模式启动,数据库将操做写入日志,以备恢复)

bin\mysqld.exe log-slow-queries=d:/abc.log [低版本mysql5.0能够在my.ini指定]

先关闭mysql,再启动, 若是启用了慢查询日志,默认把这个文件放在

my.ini 文件中记录的位置

#Path to the database root

datadir=" C:/ProgramData/MySQL/MySQL Server 5.5/Data/"

 

7)、索引

1.什么是索引

索引用来快速地寻找那些具备特定值的记录,全部MySQL索引都以B-树的形式保存。若是没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的全部记录,直至找到符合要求的记录。表里面的记录数量越多,这个操做的代价就越高。若是做为搜索条件的列上已经建立了索引,MySQL无需扫描任何记录便可迅速获得目标记录所在的位置。若是表有1000个记录,经过索引查找记录至少要比顺序扫描记录快100倍。 

2.索引的分类

a主键索引

主键是一种惟一性索引,但它必须指定为“PRIMARY KEY”。若是你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键通常在建立表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。可是,咱们也能够经过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每一个表只能有一个主键。 

b建立主键索引

主键是一种惟一性索引,但它必须指定为“PRIMARY KEY”。若是你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键通常在建立表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。可是,咱们也能够经过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每一个表只能有一个主键。 

当一张表,把某个列设为主键的时候,则该列就是主键索引

create table aaa

(id int unsigned primary key auto_increment ,

name varchar(32) not null default '');

这是id 列就是主键索引.

create table bbb (id int , name varchar(32) not null default '');

若是你建立表时,没有指定主键索引,也能够在建立表后,在添加, 指令:

实例:

alter table 表名 add primary key (列名);

删除主键索引

alter table articles drop primary key;

c查询索引

desc  表名;   不能显示索引名称

show index from 表

show keys from 表

d全文索引

错误用法:

select * from articles where body like '%mysql%'; 错误用法 索引不会生效

正确用法:

select * from articles where match(title,body) against ( 'database')

说明:

  1. mysqlfulltext 索引只针对 myisam生效
  2. mysql本身提供的fulltext针对英文生效->sphinx (coreseek) 技术处理中文
  3. 使用方法是 match(字段名..) against(‘关键字’)
  4. 全文索引:中止词,  由于在一个文本中,建立索引是一个无穷大的数,所以,对一些经常使用词和字符,就不会建立,这些词,称为中止词.好比(abmysqlthe

mysql> select match(title,body) against ('database') from articles;(输出的是每行和database的匹配度)

e惟一索引

这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的全部值都只能出现一次,即必须惟一。惟一性索引能够用如下几种方式建立: 

建立索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表); 

修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表); 

建立表的时候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) ); 

 

f普通索引

 普通索引(由关键字KEY或INDEX定义的索引)的惟一任务是加快对数据的访问速度。所以,应该只为那些最常常出如今查询条件(WHEREcolumn=)或排序条件(ORDERBYcolumn)中的数据列建立索引。只要有可能,就应该选择一个数据最整齐、最紧凑的数据列(如一个整数类型的数据列)来建立索引。

 

create table ccc(

id int unsigned,

name varchar(32)

)

create index 索引名 on 表 (列1,列名2);

 

 

g索引实现原理

 

 

数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现一般使用 B 树及其变种 B+ 树

在数据以外,数据库系统还维护着知足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就能够在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

为表设置索引要付出代价的:一是增长了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(由于索引也要随之变更)。

 

上图展现了一种可能的索引方式。左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并非必定物理相邻的)。为了加快 Col2 的查找,能够维护一个右边所示的二叉查找树,每一个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就能够运用二叉查找在 O(log2n)的复杂度内获取到相应数据。

建立索引能够大大提升系统的性能。

第一,经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。

第二,能够大大加快数据的检索速度,这也是建立索引的最主要的缘由。

第三,能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别有意义。

第四,在使用分组和排序子句进行数据检索时,一样能够显著减小查询中分组和排序的时间。

第五,经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。

也许会有人要问:增长索引有如此多的优势,为何不对表中的每个列建立一个索引呢?由于,增长索引也有许多不利的方面。

第一,建立索引和维护索引要耗费时间,这种时间随着数据量的增长而增长。

第二,索引须要占物理空间,除了数据表占数据空间以外,每个索引还要占必定的物理空间,若是要创建聚簇索引,那么须要的空间就会更大。

第三,当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这样就下降了数据的维护速度。

索引是创建在数据库表中的某些列的上面。在建立索引的时候,应该考虑在哪些列上能够建立索引,在哪些列上不能建立索引。通常来讲,应该在这些列上建立索引:在常常须要搜索的列上,能够加快搜索的速度;在做为主键的列上,强制该列的惟一性和组织表中数据的排列结构;在常常用在链接的列上,这些列主要是一些外键,能够加快链接的速度;在常常须要根据范围进行搜索的列上建立索引,由于索引已经排序,其指定的范围是连续的;在常常须要排序的列上建立索引,由于索引已经排序,这样查询能够利用索引的排序,加快排序查询时间;在常用在 WHERE 子句中的列上面建立索引,加快条件的判断速度。

一样,对于有些列不该该建立索引。通常来讲,不该该建立索引的的这些列具备下列特色:

第一,对于那些在查询中不多使用或者参考的列不该该建立索引。这是由于,既然这些列不多使用到,所以有索引或者无索引,并不能提升查询速度。相反,因为增长了索引,反而下降了系统的维护速度和增大了空间需求。

第二,对于那些只有不多数据值的列也不该该增长索引。这是由于,因为这些列的取值不多,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即须要在表中搜索的数据行的比例很大。增长索引,并不能明显加快检索速度。

第三,对于那些定义为 text, image 和 bit 数据类型的列不该该增长索引。这是由于,这些列的数据量要么至关大,要么取值不多。

第四,当修改性能远远大于检索性能时,不该该建立索引。这是由于,修改性能和检索性能是互相矛盾的。当增长索引时,会提升检索性能,可是会下降修改性能。当减小索引时,会提升修改性能,下降检索性能。所以,当修改性能远远大于检索性能时,不该该建立索引。

根据数据库的功能,能够在数据库设计器中建立三种索引:惟一索引、主键索引和汇集索引

惟一索引

惟一索引是不容许其中任何两行具备相同索引值的索引。

当现有数据中存在重复的键值时,大多数数据库不容许将新建立的惟一索引与表一块儿保存。数据库还可能防止添加将在表中建立重复键值的新数据。例如,若是在 employee 表中职员的姓(lname)上建立了惟一索引,则任何两个员工都不能同姓。主键索引数据库表常常有一列或列组合,其值惟一标识表中的每一行。该列称为表的主键。在数据库关系图中为表定义主键将自动建立主键索引,主键索引是惟一索引的特定类型。该索引要求主键中的每一个值都惟一。当在查询中使用主键索引时,它还容许对数据的快速访问。汇集索引在汇集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个汇集索引。

若是某索引不是汇集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。与非汇集索引相比,汇集索引一般提供更快的数据访问速度。

局部性原理与磁盘预读

因为存储介质的特性,磁盘自己存取就比主存慢不少,再加上机械运动耗费,磁盘的存取速度每每是主存的几百分分之一,所以为了提升效率,要尽可能减小磁盘 I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这样作的理论依据是计算机科学中著名的局部性原理当一个数据被用到时,其附近的数据也一般会立刻被使用。程序运行期间所须要的数据一般比较集中。

因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升 I/O 效率。

预读的长度通常为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为 4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。

B-/+Tree 索引的性能分析

到这里终于能够分析 B-/+Tree 索引的性能了。

上文说过通常使用磁盘 I/O 次数评价索引结构的优劣。先从 B-Tree 分析,根据 B-Tree 的定义,可知检索一次最多须要访问 h 个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每一个节点只须要一次 I/O 就能够彻底载入。为了达到这个目的,在实际实现 B-Tree 还须要使用以下技巧:

每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个 node 只需一次 I/O。

B-Tree 中一次检索最多须要 h-1 次 I/O(根节点常驻内存),渐进复杂度为 O(h)=O(logdN)。通常实际应用中,出度 d 是很是大的数字,一般超过 100,所以 h 很是小(一般不超过 3)。

而红黑树这种结构,h 明显要深的多。因为逻辑上很近的节点(父子)物理上可能很远,没法利用局部性,因此红黑树的 I/O 渐进复杂度也为 O(h),效率明显比 B-Tree 差不少。

综上所述,用 B-Tree 做为索引结构效率是很是高的。

应该花时间学习 B-树和 B+ 树数据结构

=============================================================================================================

1)B 树

B 树中每一个节点包含了键值和键值对于的数据对象存放地址指针,因此成功搜索一个对象能够不用到达树的叶节点。

成功搜索包括节点内搜索和沿某一路径的搜索,成功搜索时间取决于关键码所在的层次以及节点内关键码的数量。

B 树中查找给定关键字的方法是:首先把根结点取来,在根结点所包含的关键字 K1,…,kj 查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;不然,必定能够肯定要查的关键字在某个 Ki 或 Ki+1 之间,因而取 Pi 所指的下一层索引节点块继续查找,直到找到,或指针 Pi 为空时查找失败。

2)B+ 树

B+ 树非叶节点中存放的关键码并不指示数据对象的地址指针,非也节点只是索引部分。全部的叶节点在同一层上,包含了所有关键码和相应数据对象的存放地址指针,且叶节点按关键码从小到大顺序连接。若是实际数据对象按加入的顺序存储而不是按关键码次数存储的话,叶节点的索引必须是稠密索引,若实际数据存储按关键码次序存放的话,叶节点索引时稀疏索引。

B+ 树有 2 个头指针,一个是树的根节点,一个是最小关键码的叶节点。

因此 B+ 树有两种搜索方法:

一种是按叶节点本身拉起的链表顺序搜索。

一种是从根节点开始搜索,和 B 树相似,不过若是非叶节点的关键码等于给定值,搜索并不中止,而是继续沿右指针,一直查到叶节点上的关键码。因此不管搜索是否成功,都将走完树的全部层。

B+ 树中,数据对象的插入和删除仅在叶节点上进行。

这两种处理索引的数据结构的不一样之处:
a,B 树中同一键值不会出现屡次,而且它有可能出如今叶结点,也有可能出如今非叶结点中。而 B+ 树的键必定会出如今叶结点中,而且有可能在非叶结点中也有可能重复出现,以维持 B+ 树的平衡。
b,由于 B 树键位置不定,且在整个树结构中只出现一次,虽然能够节省存储空间,但使得在插入、删除操做复杂度明显增长。B+ 树相比来讲是一种较好的折中。
c,B 树的查询效率与键在树中的位置有关,最大时间复杂度与 B+ 树相同(在叶结点的时候),最小时间复杂度为 1(在根结点的时候)。而 B+ 树的时候复杂度对某建成的树是固定的。能够扫描2的次方。

 

 

h索引的代价

占用磁盘空间

DML(update、delete、insert)语句的效率影响

增删改会对索引影响,由于索引要从新整理。

 

存储引擎

容许的索引类型

myisam

btree

innodb

btree

memory/yeap

Hash,btree

 

 

t那些列上适合添加索引

① 查询做为查询条件字段应该建立索引

② 惟一性太差的字段不适合单首创建索引,即便频繁 

Select * from emp where sex=’男’

③ 频繁更新字段,也不要定义索引

④ 不会出如今where语句字段不要建立索引

 

总结:满处一下条件的字段,才应该建立索引

① 确定在where条件常用

② 该字段的内容不是惟一的几个值

③ 字段内容不是频繁变化

l索引注意事项

 

建立主键索引

alter table 表名 add primary key (列名);

建立一个联合索引

alter table dept add index my_ind (dname,loc); //  dname 左边的列,loc就是右边的列

注意:

1.对于建立的多列索引,若是不是使用第一部分,则不会建立索引。

explain select * from dept where loc='aaa'\G 

就不会使用到索引

2.模糊查询在like前面有百分号开头会失效。

3. 若是条件中有or,即便其中有条件带索引也不会使用。换言之,就是要求使用的全部字段,都必须创建索引, 咱们建议你们尽可能避免使用or 关键字

4.若是列类型是字符串,那必定要在条件中将数据使用引号引用起来。不然不使用索引。(添加时,字符串必须’’), 也就是,若是列是字符串类型,就必定要用 ‘’ 把他包括起来.

5.若是mysql估计使用全表扫描要比使用索引快,则不使用索引。

 

 

m查询所用使用率

show status like ‘handler_read%’;

 

你们能够注意:
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。

handler_read_rnd_next:这个值越高,说明查询低效。

 

8)、SQL调优

① 使用group by 分组查询是,默认分组后,还会排序,可能会下降速度,

group by 后面增长 order by null 就能够防止排序.

explain select * from emp  group by deptno order by null;

② 有些状况下,能够使用链接来替代子查询。由于使用join,MySQL不须要在内存中建立临时表。

select * from dept, emp where dept.deptno=emp.deptno; [简单处理方式]

select * from dept left join emp on dept.deptno=emp.deptno;  [左外链接,更ok!]

 

 

③ 对查询进行优化,要尽可能避免全表扫描,首先应考虑在 where 及 order by 涉及的列上创建索引

应尽可能避免在 where 子句中对字段进行 null 值判断,不然将致使引擎放弃使用索引而进行全表扫描,如:

select id from t where num is null

最好不要给数据库留 NULL,尽量的使用 NOT NULL 填充数据库.

备注、描述、评论之类的能够设置为 NULL,其余的,最好不要使用 NULL。

不要觉得 NULL 不须要空间,好比:char(100) 型,在字段创建时,空间就固定了, 无论是否插入值(NULL 也包含在内),都是占用 100 个字符的空间的,若是是 varchar 这样的变长字段, null 不占用空间。

能够在 num 上设置默认值 0,确保表中 num 列没有 null 值,而后这样查询:

select id from t where num = 0

更多mysql sql语句调优查看http://bbs.itmayiedu.com/article/1511164574773

9)、MySQL数据引擎

使用的存储引擎 myisam / innodb/ memory

myisam 存储: 若是表对事务要求不高,同时是以查询和添加为主的,咱们考虑使用myisam存储引擎. ,好比 bbs 中的 发帖表,回复表.

INNODB 存储: 对事务要求高,保存的数据都是重要数据,咱们建议使用INNODB,好比订单表,帐号表.

MyISAM 和 INNODB的区别

1. 事务安全(MyISAM不支持事务,INNODB支持事务)

2. 查询和添加速度(MyISAM批量插入速度快)

3. 支持全文索引(MyISAM支持全文索引,INNODB不支持全文索引)

4. 锁机制(MyISAM时表锁,innodb是行锁)

5. 外键 MyISAM 不支持外键, INNODB支持外键. (在PHP开发中,一般不设置外键,一般是在程序中保证数据的一致)

Memory 存储,好比咱们数据变化频繁,不须要入库,同时又频繁的查询和修改,咱们考虑使用memory, 速度极快. (若是mysql重启的话,数据就不存在了)

 

 

 

1.Myisam注意事项

若是你的数据库的存储引擎是myisam,请必定记住要定时进行碎片整理

举例说明:

create table test100(id int unsigned ,name varchar(32))engine=myisam;

insert into test100 values(1,’aaaaa’);

insert into test100 values(2,’bbbb’);

insert into test100 values(3,’ccccc’);

咱们应该定义对myisam进行整理

optimize table test100;

 

10)、数据库数据备份

1.手动方式

cmd控制台:

在环境变量中配置mysql环境变量

mysqldump –u -帐号密码 数据库 [表名1 表名2..]  > 文件路径

案例: mysqldump -u -root root test > d:\temp.sql

 

好比: 把temp数据库备份到 d:\temp.bak

mysqldump -u root -proot test > f:\temp.bak

若是你但愿备份是,数据库的某几张表

mysqldump -u root -proot test dept > f:\temp.dept.sql

 

如何使用备份文件恢复咱们的数据.

mysql控制台

source d:\temp.dept.bak

2.自动方式

把备份数据库的指令,写入到 bat文件, 而后经过任务管理器去定时调用 bat文件.

mytask.bat 内容是:

@echo off

F:\path\mysqlanzhuang\bin\mysqldump -u root -proot test dept > f:\temp.dept.sql

建立执行计划任务执行脚本。

 

 

11)、分表分库

1.垂直拆分

垂直拆分就是要把表按模块划分到不一样数据库表中(固然原则仍是不破坏第三范式),这种拆分在大型网站的演变过程当中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一块儿,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分红多个弱耦合的服务,经过服务间的调用来知足业务需求看,所以表拆出来后要经过服务的形式暴露出去,而不是直接调用不一样模块的表,淘宝在架构不断演变过程,最重要的一环就是服务化改造,把用户、交易、店铺、宝贝这些核心的概念抽取成独立的服务,也很是有利于进行局部的优化和治理,保障核心模块的稳定性

垂直拆分用于分布式场景。

2.水平拆分

上面谈到垂直切分只是把表按模块划分到不一样数据库,但没有解决单表大数据量的问题,而水平切分就是要把一个表按照某种规则把数据划分到不一样表或数据库里。例如像计费系统,经过按时间来划分表就比较合适,由于系统都是处理某一时间段的数据。而像SaaS应用,经过按用户维度来划分数据比较合适,由于用户与用户之间的隔离的,通常不存在处理多个用户数据的状况,简单的按user_id范围来水平切分

通俗理解:水平拆分行,行数据拆分到不一样表中, 垂直拆分列,表数据拆分到不一样表中

3.水平分割案例

思路:在大型电商系统中,天天的会员人数不断的增长。达到必定瓶颈后如何优化查询。

可能你们会想到索引,万一用户量达到上亿级别,如何进行优化呢?

使用水平分割拆分数据库表。

4.如何使用水平拆分数据库

使用水平分割拆分表,具体根据业务需求,有的按照注册时间、取摸、帐号规则、年份等。

 

5.使用取摸方式分表

首先我建立三张表 user0 / user1 /user2 , 而后我再建立 uuid表,该表的做用就是提供自增的id。

 

create table user0(

id int unsigned primary key ,

name varchar(32) not null default '',

pwd  varchar(32) not null default '')

engine=myisam charset utf8;

 

create table user1(

id int unsigned primary key ,

name varchar(32) not null default '',

pwd  varchar(32) not null default '')

engine=myisam charset utf8;

 

create table user2(

id int unsigned primary key ,

name varchar(32) not null default '',

pwd  varchar(32) not null default '')

engine=myisam charset utf8;

 

 

create table uuid(

id int unsigned primary key auto_increment)engine=myisam charset utf8;

 

--建立一个demo项目
A-POM文件

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.3.3.RELEASE</version>

</parent>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-jdbc</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

</dependencies>

B-Service代码

@Service

public class UserService {

 

@Autowired

private JdbcTemplate jdbcTemplate;

 

public String regit(String name, String pwd) {

// 1.先获取到 自定增加ID

String idInsertSQL = "INSERT INTO uuid VALUES (NULL);";

jdbcTemplate.update(idInsertSQL);

Long insertId = jdbcTemplate.queryForObject("select last_insert_id()", Long.class);

// 2.判断存储表名称

String tableName = "user" + insertId % 3;

// 3.注册数据

String insertUserSql = "INSERT INTO " + tableName + " VALUES ('" + insertId + "','" + name + "','" + pwd

+ "');";

System.out.println("insertUserSql:" + insertUserSql);

jdbcTemplate.update(insertUserSql);

return "success";

}

 

public String get(Long id) {

String tableName = "user" + id % 3;

String sql = "select name from " + tableName + "  where id="+id;

System.out.println("SQL:" + sql);

String name = jdbcTemplate.queryForObject(sql, String.class);

return name;

}

 

}

 

C-Controller

@RestController

public class UserController {

@Autowired

private UserService userService;

 

@RequestMapping("/regit")

public String regit(String name, String pwd) {

return userService.regit(name, pwd);

}

 

@RequestMapping("/get")

public String get(Long id) {

String name = userService.get(id);

return name;

}

 

}

 

 

12)、什么是读写分离

在数据库集群架构中,让主库负责处理事务性查询,而从库只负责处理select查询,让二者分工明确达到提升数据库总体读写性能。固然,主数据库另一个功能就是负责将事务性查询致使的数据变动同步到从库中,也就是写操做。

1.读写分离的好处

 1)分摊服务器压力,提升机器的系统处理效率

    读写分离适用于读远比写的场景,若是有一台服务器,当select不少时,update和delete会被这些select访问中的数据堵塞,等待select结束,并发性能并不高,而主从只负责各自的写和读,极大程度的缓解X锁和S锁争用;

   假如咱们有1主3从,不考虑上述1中提到的从库单方面设置,假设如今1分钟内有10条写入,150条读取。那么,1主3从至关于共计40条写入,而读取总数没变,所以平均下来每台服务器承担了10条写入和50条读取(主库不承担读取操做)。所以,虽然写入没变,可是读取大大分摊了,提升了系统性能。另外,当读取被分摊后,又间接提升了写入的性能。因此,整体性能提升了,说白了就是拿机器和带宽换性能;

  2)增长冗余,提升服务可用性,当一台数据库服务器宕机后能够调整另一台从库以最快速度恢复服务

 

2.主从复制原理

 依赖进制日志,binary-log.

 二进制日志中记录引发数据库发生改变的语句

  Insert 、delete、updatecreate table

3.Scale-upScale-out区别

Scale Out是指Application能够在水平方向上扩展。通常对数据中心的应用而言,Scale out指的是当添加更多的机器时,应用仍然能够很好的利用这些机器的资源来提高本身的效率从而达到很好的扩展性。

Scale Up是指Application能够在垂直方向上扩展。通常对单台机器而言,Scale Up值得是当某个计算节点(机器)添加更多的CPU Cores,存储设备,使用更大的内存时,应用能够很充分的利用这些资源来提高本身的效率从而达到很好的扩展性。

 

 

4.解决问题

数据如何不被丢失

备份

读写分离

数据库负载均衡

高可用

 

5.环境搭建

  1. 准备环境

两台windows操做系统 ip分别: 172.27.185.1(主)172.27.185.2()

  1. 链接服务(172.27.185.1)服务器上,给从节点分配帐号权限。

GRANT REPLICATION SLAVE ON *.* TO 'root'@'172.27.185.2' IDENTIFIED BY 'root';

  1. 在主服务my.ini文件新增

server-id=200

log-bin=mysql-bin

relay-log=relay-bin

relay-log-index=relay-bin-index

重启mysql服务

  1. 从服务my.ini文件新增

server-id = 210

replicate-do-db =itmayiedu #须要同步数据库

重启mysql服务

  1. 服务同步数据库

stop slave;

change

master to master_host='172.27.185.1',master_user='root',master_password='root';

start slave;

show slave status;

 

13)、MyCat

1.什么  Mycat

是一个开源的分布式数据库系统,可是由于数据库通常都有本身的数据库引擎,而Mycat并无属于本身的独有数据库引擎,全部严格意义上说并不能算是一个完整的数据库系统,只能说是一个在应用和数据库之间起桥梁做用的中间件。

Mycat中间件出现以前,MySQL主从复制集群,若是要实现读写分离,通常是在程序段实现,这样就带来了一个问题,即数据段和程序的耦合度过高,若是数据库的地址发生了改变,那么个人程序也要进行相应的修改,若是数据库不当心挂掉了,则同时也意味着程序的不可用,而对于不少应用来讲,并不能接受;

  引入Mycat中间件能很好地对程序和数据库进行解耦,这样,程序只需关注数据库中间件的地址,而无需知晓底层数据库是如何提供服务的,大量的通用数据聚合、事务、数据源切换等工做都由中间件来处理;

  Mycat中间件的原理是对数据进行分片处理,从原有的一个库,被切分为多个分片数据库,全部的分片数据库集群构成完成的数据库存储,有点相似磁盘阵列中的RAID0.

 

 

2、JavaEE

1基础部分

1)、BSCS区别

C/S (Client - Server  客户端-服务器端)

典型应用:QQ软件 ,飞秋,红蜘蛛。

特色:

1)必须下载特定的客户端程序。

2)服务器端升级,客户端升级。

B/S (Broswer -Server 浏览器端- 服务器端)

典型应用: 腾讯官方(www.qq.com)  163新闻网站, 蚂蚁课堂官网(俗称:网站)

特色:

1)不须要安装特定的客户端(只须要安装浏览器便可!!)

2)服务器端升级,浏览器不须要升级!!!!

javaweb的程序就是b/s软件结构!!!

2)、DNS解析过程

一、在浏览器中输入www.qq.com域名,操做系统会先检查本身本地的hosts文件是否有这个网址映射关系,若是有,就先调用这个IP地址映射,完成域名解析。 
二、若是hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,若是有,直接返回,完成域名解析。 

 

3)、外网映射工具

外网映射工具的做用,主要将本地服务映射外网

应用场景:支付回调、微信开发、对接第三方接口等

映射工具Ngrok、花生壳等。

4)、静态资源和动态资源的区别

静态资源: 当用户屡次访问这个资源,资源的源代码永远不会改变的资源。

动态资源:当用户屡次访问这个资源,资源的源代码可能会发送改变。

5)、Servlet

1.Sevlet的生命周期(重点)

构造方法: 建立servlet对象的时候调用。

默认状况下,第一次访问servlet的时候建立servlet对象         只调用1次。证实servlet对象在tomcat是单实例的。

init方法: 建立完servlet对象的时候调用。只调用1次。

service方法: 每次发出请求时调用。调用n次。

destroy方法: 销毁servlet对象的时候调用。中止服务器或者从新部署web应用时销毁servlet对象。只调用1次。

2.怎么证实Servlet单例的?

由于Servlet是经过Java反射机制,读取web.xml配置中的servlet-class 完整路径进行反射默认执行无参构造函数,因此只要servlet执行构造函数永远只执行一遍,Servlet是单例的。

3.Servlet的多线程并发问题

注意: servlet对象在tomcat服务器是单实例多线程的。

由于servlet是多线程的,因此当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引起线程安全问题。

解决办法:

1)把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)

2)建议在servlet类中尽可能不要使用成员变量。若是确实要使用成员,必须同步。并且尽可能缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以免由于同步而致使并发效率下降。

6)、转发与重定向区别?

1)转发

a)地址栏不会改变

b)转发只能转发到当前web应用内的资源

c)能够在转发过程当中,能够把数据保存到request域对象中

2)重定向

a)地址栏会改变,变成重定向到地址。

b)重定向能够跳转到当前web应用,或其余web应用,甚至是外部域名网站。

c)不能再重定向的过程,把数据保存到request中。

7)、重定向实现原理

重定向会发送两次请求,浏览器认识状态码302,会再次向服务器发送一次请求,获取请求头的中location的value值进行重定向。

8)、JavaWeb哪些会话技术

Cookie会话数据保存在浏览器客户端

 服务器建立Cookie,Cookie内容以响应头方式发送给客户端存放在本地,当下次发送请求时.会Cookie信息以请求方式发送给服务器端

注意:Cookie信息不能夸浏览器访问

Session会话保存与服务器端

   服务器建立Session,Session内容存放服务器端上,以响应头方式将SessionId发送给客户端保存,当下次发送请求时,会将SessionID 请求头方式发送给服务器端

  注意: 浏览器关闭,只是清除Sessionid,并无清除Session

1.Cookie的实现原理

1)服务器建立cookie对象,把会话数据存储到cookie对象中。

new Cookie("name","value");

2 服务器发送cookie信息到浏览器

response.addCookie(cookie);

 

举例: set-cookie: name=eric  (隐藏发送了一个set-cookie名称的响应头)

3)浏览器获得服务器发送的cookie,而后保存在浏览器端。

4)浏览器在下次访问服务器时,会带着cookie信息

   举例: cookie: name=eric  (隐藏带着一个叫cookie名称的请求头)

5)服务器接收到浏览器带来的cookie信息

request.getCookies();

2.Cookie的应用场景

购物车、显示用户上次访问的时间

3.Session的实现原理

1)第一次访问建立session对象,给session对象分配一个惟一的ID,叫JSESSIONID

new HttpSession();

2)把JSESSIONID做为Cookie的值发送给浏览器保存

Cookie cookie = new Cookie("JSESSIONID", sessionID);

response.addCookie(cookie);

3)第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器

4)服务器获得JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。

if(找到){

return map.get(sessionID);

}

Map<String,HttpSession>]

<"s001", s1>

<"s001,"s2>

5)若是找到对应编号的session对象,直接返回该对象

6)若是找不到对应编号的session对象,建立新的session对象,继续走1的流程

结论:经过JSESSION的cookie值在服务器找session对象!!!!!

9)、token

1.什么是token

token其实就是一个令牌,具备随机性,相似于sessionId。

在对接一些第三方平台的时候,为了可以保证数据安全性,一般会使用一些令牌进行交互

例如: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

2.如何自定义token

token生成规则,只要保证token生成一个不重复的惟一字符串便可。

使用jdk自带的uuid生成规则。

10)、UUID

1.什么是UUID

UUID含义是通用惟一识别码 (Universally Unique Identifier),这是一个软件建构的标准,也是被开源软件基金会 (Open Software Foundation, OSF) 
的组织应用在分布式计算环境 (Distributed Computing Environment, DCE) 领域的一部分。

     UUID 的目的,是让分布式系统中的全部元素,都能有惟一的辨识资讯,而不须要透过中央控制端来作辨识资讯的指定。如此一来,每一个人均可以创建不与其它人冲突的 UUID。
在这样的状况下,就不需考虑数据库创建时的名称重复问题。目前最普遍应用的 UUID,便是微软的 Microsoft's Globally Unique Identifiers (GUIDs),而其余重要的应用,
则有 Linux ext2/ext3 档案系统、LUKS 加密分割区、GNOME、KDE、Mac OS X 等等

 

2.UUID组成

UUID保证对在同一时空中的全部机器都是惟一的。一般平台会提供生成的API。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字
UUID由如下几部分的组合:
1)当前日期和时间,UUID的第一个部分与时间有关,若是你在生成一个UUID以后,过几秒又生成一个UUID,则第一个部分不一样,其他相同。
2)时钟序列。
3)全局惟一的IEEE机器识别号,若是有网卡,从网卡MAC地址得到,没有网卡以其余方式得到。
UUID的惟一缺陷在于生成的结果串会比较长。关于UUID这个标准使用最广泛的是微软的GUID(Globals Unique Identifiers)。在ColdFusion中能够用CreateUUID()函数很简单地生成UUID,
其格式为:xxxxxxxx-xxxx- xxxx-xxxxxxxxxxxxxxxx(8-4-4-16),其中每一个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。而标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12);

3.UUID代码

UUID.randomUUID().toString()

 

11)、什么Filter

Filter Servlet技术中的过滤器,主要作拦截请求做用,通常用于防护XSS攻击、权限、登陆判断等。

 

2网络通信

1)、http部分

1.什么是http协议

http协议: 对浏览器客户端 和  服务器端 之间数据传输的格式规范

2.Http格式的分类

  请求行

  请求

  请求内容

  响应

  响应

  响应内容

3.httpshttp区别

虽说 HTTPS 有很大的优点,但其相对来讲,仍是存在不足之处的:

  (1)HTTPS 协议握手阶段比较费时,会使页面的加载时间延长近 50%,增长 10% 到 20% 的耗电;

  (2)HTTPS 链接缓存不如 HTTP 高效,会增长数据开销和功耗,甚至已有的安全措施也会所以而受到影响;

  (3)SSL 证书须要钱,功能越强大的证书费用越高,我的网站、小网站没有必要通常不会用。

  4)SSL 证书一般须要绑定 IP,不能在同一 IP 上绑定多个域名,IPv4 资源不可能支撑这个消耗。

  (5)HTTPS 协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么做用。最关键的,SSL 证书的信用链体系并不安全,特别是在某些国家能够控制 CA 根证书的状况下,中间人攻击同样可行。

4.https请求方式

常见的请求方式: GET 、 POST、 HEAD、 TRACE、 PUT、 CONNECT 、DELETE

经常使用的请求方式: GET  和 POST

表单提交:

<form action="提交地址" method="GET/POST">

<form>

GET   vs  POST 区别

1)GET方式提交

a)地址栏(URI)会跟上参数数据。以?开头,多个参数之间以&分割。

GET /day09/testMethod.html?name=eric&password=123456 HTTP/1.1

Host: localhost:8080

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-cn,en-us;q=0.8,zh;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Referer: http://localhost:8080/day09/testMethod.html

Connection: keep-alive

 

b)GET提交参数数据有限制,不超过1KB。

c)GET方式不适合提交敏感密码。

d)注意: 浏览器直接访问的请求,默认提交方式是GET方式

2)POST方式提交

a)参数不会跟着URI后面。参数而是跟在请求的实体内容中。没有?开头,多个参数之间以&分割。

POST /day09/testMethod.html HTTP/1.1

Host: localhost:8080

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: zh-cn,en-us;q=0.8,zh;q=0.5,en;q=0.3

Accept-Encoding: gzip, deflate

Referer: http://localhost:8080/day09/testMethod.html

Connection: keep-alive

 

name=eric&password=123456

 

b)POST提交的参数数据没有限制。

c)POST方式提交敏感数据。

3.2 请求头

Accept: text/html,image/*      -- 浏览器接受的数据类型

Accept-Charset: ISO-8859-1     -- 浏览器接受的编码格式

Accept-Encoding: gzip,compress  --浏览器接受的数据压缩格式

Accept-Language: en-us,zh-       --浏览器接受的语言

Host: www.it315.org:80          --(必须的)当前请求访问的目标地址(主机:端口)

If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT  --浏览器最后的缓存时间

Referer: http://www.it315.org/index.jsp      -- 当前请求来自于哪里

User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)  --浏览器类型

Cookie:name=eric                     -- 浏览器保存的cookie信息

Connection: close/Keep-Alive            -- 浏览器跟服务器链接状态。close: 链接关闭  keep-alive:保存链接。

Date: Tue, 11 Jul 2000 18:23:51 GMT      -- 请求发出的时间

 

5.客户端模拟http请求工具

Postmen(谷歌插件)、RestClient

6.服务器模拟http请求工具

httpclientHttpURLConnection

7.前端ajax请求  

   

$.ajax({

type : 'post',

dataType : "text",

url : "http://a.a.com/a/FromUserServlet",

data : "userName=余胜军&userAge=19",

success : function(msg) {

alert(msg);

}

});

 

 

 

 

 

 

 

 

 

 

3框架部分

1)、Spring

1.Spring概述

Spring框架,能够解决对象建立以及对象之间依赖关系的一种框架。

且能够和其余框架一块儿使用;SpringStruts,  Springhibernate

(起到整合(粘合)做用的一个框架)

Spring提供了一站式解决方案:

1Spring Core  spring的核心功能: IOC容器, 解决对象建立及依赖关系

2Spring Web  Springweb模块的支持。

-à 能够与struts整合,strutsaction建立交给spring

    -à spring mvc模式

3Spring DAO  Spring jdbc操做的支持  【JdbcTemplate模板工具类】

4Spring ORM  springorm的支持:

à 既能够与hibernate整合,【session

à 也能够使用spring的对hibernate操做的封装

5Spring AOP  切面编程

6SpringEE   spring javaEE其余模块的支持

2.SpringBeanId重复会怎么样?

报错。

3.什么SpringIOC

 SpringIOC就是把每一个beanbean之间的关系交给第三方容器进行管理,这个容器就是Spring

什么AOP

4.什么SpringAOP

Aop  aspect object programming  面向切面编程

功能: 让关注点代码与业务代码分离!

关注点,

重复代码就叫作关注点;

切面,

 关注点造成的类,就叫切面()

 面向切面编程,就是指 对不少功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”。

切入点,

执行目标对象方法,动态植入切面代码。

能够经过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

应用场景:事物、日志、权限控制

5.SpringAOP建立方式

A 注解方式实现AOP编程

步骤:

1) 先引入aop相关jar文件     aspectj  aop优秀组件)

spring-aop-3.2.5.RELEASE.jar   spring3.2源码】

aopalliance.jar   spring2.5源码/lib/aopalliance】

aspectjweaver.jar   spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

aspectjrt.jar   spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

 

注意: 用到spring2.5版本的jar文件,若是用jdk1.7可能会有问题。

须要升级aspectj组件,即便用aspectj-1.8.2版本中提供jar文件提供。

 

 

2bean.xml中引入aop名称空间

 

 

3) 开启aop注解

 

4) 使用注解

@Aspect 指定一个类为切面类

@Pointcut("execution(* com.itmayiedu.service.UserService.add(..))")  指定切入点表达式

 

@Before("pointCut_()") 前置通知: 目标方法以前执行

@After("pointCut_()") 后置通知:目标方法以后执行(始终执行)

@AfterReturning("pointCut_()")     返回后通知: 执行方法结束前执行(异常不执行)

@AfterThrowing("pointCut_()") 异常通知:  出现异常时候执行

@Around("pointCut_()") 环绕通知: 环绕目标方法执行

 

@Component

@Aspect

public class Aop {

@Before("execution(* com.itmayiedu.service.UserService.add(..))")

public void begin() {

System.out.println("前置通知");

}

 

@After("execution(* com.itmayiedu.service.UserService.add(..))")

public void commit() {

System.out.println("后置通知");

}

 

@AfterReturning("execution(* com.itmayiedu.service.UserService.add(..))")

public void afterReturning() {

System.out.println("运行通知");

}

 

@AfterThrowing("execution(* com.itmayiedu.service.UserService.add(..))")

public void afterThrowing() {

System.out.println("异常通知");

}

 

@Around("execution(* com.itmayiedu.service.UserService.add(..))")

public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

       System.out.println("我是环绕通知-");

       proceedingJoinPoint.proceed();

       System.out.println("我是环绕通知-");

}

 

}

 

 

 

B XML方式实现AOP编程

 

Xml实现aop编程:

1) 引入jar文件  【aop 相关jar4个】

2) 引入aop名称空间

3aop 配置

* 配置切面类 (重复执行代码造成的类)

* aop配置

拦截哪些方法 / 拦截到方法后应用通知代码

 

 

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd">

 

<!-- dao 实例 -->

<bean id="userDao" class="com.itmayiedu.UserDao"></bean>

<bean id="orderDao" class="com.itmayiedu.OrderDao"></bean>

 

<!-- 切面类 -->

<bean id="aop" class="com.itmayiedu.Aop"></bean>

 

<!-- Aop配置 -->

<aop:config>

<!-- 定义一个切入点表达式: 拦截哪些方法 -->

<aop:pointcut expression="execution(* com.itmayiedu.*.*(..))" id="pt"/>

<!-- 切面 -->

<aop:aspect ref="aop">

<!-- 环绕通知 -->

<aop:around method="around" pointcut-ref="pt"/>

<!-- 前置通知: 在目标方法调用前执行 -->

<aop:before method="begin" pointcut-ref="pt"/>

<!-- 后置通知: -->

<aop:after method="after" pointcut-ref="pt"/>

<!-- 返回后通知 -->

<aop:after-returning method="afterReturning" pointcut-ref="pt"/>

<!-- 异常通知 -->

<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>

 

</aop:aspect>

</aop:config>

</beans>  

 

 

 

6.Spring是单例仍是多例?

  Spring默认是单例

7.Spring做用域

A singleton 

当一个bean 做用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,而且全部对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把 一个bean定义设置为singleton做用域时,Spring IOC容器只会建立该bean定义的惟一实例。这个单一实例会被存储到单例缓存(singleton cache)中,而且全部针对该bean的后续请求和引用都 将返回被缓存的对象实例,这里要注意的是singleton做用域和GOF设计模式中的单例是彻底不一样的,单例设计模式表示一个ClassLoader中 只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时 候,springIOC容器中只会存在一个该bean

B prototype

prototype做用域部署的bean,每一次请求(将其注入到另外一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,至关与一个new的操做,对于prototype做用域的bean,有一点很是重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例漠不关心了。无论何种做用域,容器都会调用全部对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype做用域的对象并释听任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton做用域bean占用资源的一种可行方式是,经过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)

C request

request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,配置实例:

requestsessionglobal session使用的时候首先要在初始化webweb.xml中作以下配置:

D session

session做用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效

8.IOC容器建立对象

建立对象, 有几种方式:

1) 调用无参数构造器

2) 带参数构造器

3) 工厂建立对象

工厂类,静态方法建立对象

工厂类,非静态方法建立对象

9.依赖注入有哪些方式

Spring中,如何给对象的属性赋值?  DI, 依赖注入】

1) 经过构造函数

2) 经过set方法给属性注入值

3) p名称空间

4) 注解

10.事物的概述

A  原子性(Atomicity

  原子性是指事务包含的全部操做要么所有成功,要么所有失败回滚,所以事务的操做若是成功就必需要彻底应用到数据库,若是操做失败则不能对数据库有任何影响。

B  一致性(Consistency)

  一致性是指事务必须使数据库从一个一致性状态变换到另外一个一致性状态,也就是说一个事务执行以前和执行以后都必须处于一致性状态。

  拿转帐来讲,假设用户A和用户B二者的钱加起来一共是5000,那么无论AB之间如何转帐,转几回帐,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

C  隔离性(Isolation)

  隔离性是当多个用户并发访问数据库时,好比操做同一张表时,数据库为每个用户开启的事务,不能被其余事务的操做所干扰,多个并发事务之间要相互隔离。

  即要达到这么一种效果:对于任意两个并发的事务T1T2,在事务T1看来,T2要么在T1开始以前就已经结束,要么在T1结束以后才开始,这样每一个事务都感受不到有其余事务在并发地执行。

  关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。

D  持久性(Durability)

  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即使是在数据库系统遇到故障的状况下也不会丢失提交事务的操做。

例如咱们在使用JDBC操做数据库时,在提交事务方法后,提示用户事务操做完成,当咱们程序执行完成直到看到提示后,就能够认定事务以及正确提交,即便这时候数据库出现了问题,也必需要将咱们的事务彻底执行完成,不然就会形成咱们看到提示事务处理完毕,可是数据库由于故障而没有执行事务的重大错误。

11.Spring事物控制

编程式事务控制

本身手动控制事务,就叫作编程式事务控制。

Jdbc代码:

Conn.setAutoCommite(false);  // 设置手动控制事务

Hibernate代码:

Session.beginTransaction();    // 开启一个事务

【细粒度的事务控制: 能够对指定的方法、指定的方法的某几行添加事务控制】

(比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚.)

 

声明式事务控制

Spring提供了对事务的管理, 这个就叫声明式事务管理。

    Spring声明事物有xml方式和注解方式

Spring提供了对事务控制的实现。用户若是想用Spring的声明式事务管理,只须要在配置文件中配置便可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦。

Spring声明式事务管理,核心实现就是基于Aop

【粗粒度的事务控制: 只能给整个方法应用事务,不能够对方法的某几行应用事务。】

(由于aop拦截的是方法。)

 

Spring声明式事务管理器类:

Jdbc技术:DataSourceTransactionManager

Hibernate技术:HibernateTransactionManager

12.Spring事物传播行为7

Spring中事务的定义:

Propagationkey属性肯定代理应该给哪一个方法增长事务行为。这样的属性最重要的部份是传播行为。)有如下选项可供使用:

  • PROPAGATION_REQUIRED--支持当前事务,若是当前没有事务,就新建一个事务。这是最多见的选择。
  • PROPAGATION_SUPPORTS--支持当前事务,若是当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY--支持当前事务,若是当前没有事务,就抛出异常。 
  • PROPAGATION_REQUIRES_NEW--新建事务,若是当前存在事务,把当前事务挂起。 
  • PROPAGATION_NOT_SUPPORTED--以非事务方式执行操做,若是当前存在事务,就把当前事务挂起。 
  • PROPAGATION_NEVER--以非事务方式执行,若是当前存在事务,则抛出异常。

13.SpringMVC流程

1  用户发送请求至前端控制器DispatcherServlet

2  DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3  处理器映射器找到具体的处理器(能够根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(若是有则生成)一并返回给DispatcherServlet

4  DispatcherServlet调用HandlerAdapter处理器适配器。

5  HandlerAdapter通过适配调用具体的处理器(Controller,也叫后端控制器)

6  Controller执行完成返回ModelAndView

7  HandlerAdaptercontroller执行结果ModelAndView返回给DispatcherServlet

8  DispatcherServletModelAndView传给ViewReslover视图解析器。

9  ViewReslover解析后返回具体View

10DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11DispatcherServlet响应用户。

2)、SpringBoot

1.什么SpringBoot

在您第1次接触和学习Spring框架的时候,是否由于其繁杂的配置而退却了?在你第n次使用Spring框架的时候,是否以为一堆反复黏贴的配置有一些厌烦?那么您就不妨来试试使用Spring Boot来让你更易上手,更简单快捷地构建Spring应用!

Spring Boot让咱们的Spring应用变的更轻量化。好比:你能够仅仅依靠一个Java类来运行一个Spring引用。你也能够打包你的应用为jar并经过使用java -jar来运行你的Spring Web应用。

Spring Boot的主要优势:

为全部Spring开发者更快的入门

开箱即用,提供各类默认配置来简化项目配置

内嵌式容器简化Web项目

没有冗余代码生成和XML配置的要求

本章主要目标完成Spring Boot基础项目的构建,而且实现一个简单的Http请求处理,经过这个例子对Spring Boot有一个初步的了解,并体验其结构简单、开发快速的特性。

 

2.SpringBoot优势

为全部Spring开发者更快的入门

开箱即用,提供各类默认配置来简化项目配置

内嵌式容器简化Web项目

没有冗余代码生成和XML配置的要求

本章主要目标完成Spring Boot基础项目的构建,而且实现一个简单的Http请求处理,经过这个例子对Spring Boot有一个初步的了解,并体验其结构简单、开发快速的特性。

 

3.注解介绍:

1.@RestController

在上加上RestController 表示修饰Controller全部的方法返回JSON格式,直接能够编写

Restful接口

2.@EnableAutoConfiguration

注解:做用在于让 Spring Boot   根据应用所声明的依赖来对 Spring 框架进行自动配置
        这个注解告诉Spring Boot根据添加的jar依赖猜想你想如何配置Spring。因为spring-boot-starter-web添加了Tomcat和Spring MVC,因此auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。

3.SpringApplication.run(HelloController.class, args);

   标识为启动类---Springboot默认端口号为8080

4.启动方式

SpringBoot启动方式:2

@RestController@EnableAutoConfiguration

@ComponentScan(basePackages="com.itmayiedu.controller")@EnableAutoConfiguration

5.Web开发
静态资源访问

在咱们开发Web应用的时候,须要引用大量的jscss、图片等静态资源。

默认配置

Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合以下规则:举例:咱们能够在src/main/resources/目录下建立static,在该位置放置一个图片文件。启动程序后,尝试访问http://localhost:8080/D.jpg 如能显示图片,配置成功。

 

全局捕获异常-- @ExceptionHandler 表示拦截异常

@ControllerAdvice controller 的一个辅助类,最经常使用的就是做为全局异常处理的切面类@ControllerAdvice 能够指定扫描范围。@ControllerAdvice 约定了几种可行的返回值,若是是直接返回 model 类的话,须要使用@ResponseBody 进行 json 转换

@ControllerAdvice

public class GlobalExceptionHandler {

@ExceptionHandler(RuntimeException.class)

@ResponseBody

public Map<String, Object> exceptionHandler() {

Map<String, Object> map = new HashMap<String, Object>();

map.put("errorCode", "101");

map.put("errorMsg", "系統错误!");

return map;

}

}

渲染Web页面

经过 @RestController来处理请求,因此返回的内容为json对象

模板引擎

ThymeleafFreeMarkerVelocityGroovyMustache

Spring Boot建议使用这些模板引擎,避免使用JSP,若必定要使用JSP将没法实现

Spring Boot的多种特性,具体可见后文:支持JSP的配置 。当你使用上述模板引擎中的任何一个,它们默认的模板配置路径为:src/main/resources/templates

 

使用Freemarker模板引擎渲染web视图

使用JSP渲染Web视图

 

springboot整合使用JdbcTemplate

maven多引入spring-boot-starter-jdbcmysql-connector-java

springboot整合使用mybatis

maven 引入mybatis-spring-boot-starter

配置数据源

spring.datasource.url=jdbc:mysql://localhost:3306/test

spring.datasource.username=root

spring.datasource.password=root

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

 

Mapper代码

public interface UserMapper {

@Select("SELECT * FROM USERS WHERE NAME = #{name}")

User findByName(@Param("name") String name);

}

启动

@ComponentScan(basePackages = "com.itmayiedu")

@MapperScan(basePackages = "com.itmayiedu.mapper")

@SpringBootApplication

public class App {

public static void main(String[] args) {

SpringApplication.run(App.class, args);

}

}

 

springboot整合使用springjpa

maven引入 spring-boot-starter-data-jpa

springboot整合多数据源

事物管理

springboot整合事物管理

springboot默认集成事物,只主要在方法上加上 @Transactional便可

SpringBoot分布式事物管理

使用springboot+jta+atomikos 分布式事物管理

maven引入spring-boot-starter-jta-atomikos

日志管理

使用log4j记录日志

新建log4j配置文件

使用AOP统一处理Web请求日志

maven引入spring-boot-starter-aop

@Aspect

@Component

public class WebLogAspect {

private Logger logger = LoggerFactory.getLogger(getClass());

@Pointcut("execution(public * com.itmayiedu.controller..*.*(..))")

public void webLog() {

}

@Before("webLog()")

public void doBefore(JoinPoint joinPoint) throws Throwable {

// 接收到请求,记录请求内容

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

HttpServletRequest request = attributes.getRequest();

// 记录下请求内容

logger.info("URL : " + request.getRequestURL().toString());

logger.info("HTTP_METHOD : " + request.getMethod());

logger.info("IP : " + request.getRemoteAddr());

Enumeration<String> enu = request.getParameterNames();

while (enu.hasMoreElements()) {

String name = (String) enu.nextElement();

logger.info("name:{},value:{}", name, request.getParameter(name));

}

}

@AfterReturning(returning = "ret", pointcut = "webLog()")

public void doAfterReturning(Object ret) throws Throwable {

// 处理完请求,返回内容

logger.info("RESPONSE : " + ret);

}

}

缓存支持

注解配置与EhCache使用

maven引入spring-boot-starter-cache

新建ehcache.xml 文件,采用默认配置

代码使用:

@CacheConfig(cacheNames = "baseCache")

public interface UserMapper {

@Select("select * from users where name=#{name}")

@Cacheable

UserEntity findName(@Param("name") String name);

}

清除缓存

@Autowired

private CacheManager cacheManager;

@RequestMapping("/remoKey")

public void remoKey() {

cacheManager.getCache("baseCache").clear();

}

使用Redis作集中式缓存

 

使用 @Scheduled建立定时任务

Spring Boot的主类中加入 @EnableScheduling注解,启用定时任务的配置

@Component

public class ScheduledTasks {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Scheduled(fixedRate = 5000)

    public void reportCurrentTime() {

System.out.println("如今时间:" + dateFormat.format(new Date()));

    }

}

使用 @Async实现异步调用

启动加上 @EnableAsync ,须要执行异步方法上加入 @Async

自定义参数

@Value("${name}")

private String name;

@ResponseBody

@RequestMapping("/getValue")

public String getValue() {

return name;

}

 

多环境配置

spring.profiles.active=pre

 

application-dev.properties:开发环境

application-test.properties:测试环境

application-prod.properties:生产环境

修改端口号

server.port=8888

server.context-path=/itmayiedu

SpringBoot yml 使用

建立application.yml

server:

  port:  8090

  context-path: /itmayiedu

 

在使用 yml 来配置的时候不要保留对应的 applicaiont-dev.propertiesapplication-proc.properties… 等

properties 配置文件,不然,SpringBoot 会优先去 properties 配置文件中查找。

发布打包

使用mvn package 打包

使用java jar 包名

若是报错没有主清单,pom文件中新增

 

 

3)、MybatisHibernate区别

 Mybatis是轻量级封装,Hibernate是重量级封装

 Mybatis SQL语句获得对象,hibernate是对象获得SQL语句

 

String resource = "mybatis.xml";

// 读取配置文件

Reader reader = Resources.getResourceAsReader(resource);

// 获取会话工厂

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

SqlSession openSession = sqlSessionFactory.openSession();

// 查询

String sql = "com.itmayiedu.mapper.UserMapper.delUser";

int reuslt = openSession.delete(sql,1);

4)、Mybatis#$区别

优先使用 #{}。由于 ${} 会致使 sql 注入的问题

 

4安全与防护部分

1.表单重复提交解决方案(防止Http重复提交)

A JavaScript来防止表单重复提交

产生缘由

网络延时、从新刷新点击浏览器的【后退】按钮回退到表单页面后进行再次提交

使用javascript 解决

 既然存在上述所说的表单重复提交问题,那么咱们就要想办法解决,比较经常使用的方法是采用JavaScript来防止表单重复提交,具体作法以下:

修改form.jsp页面,添加以下的JavaScript代码来防止表单重复提交

代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Form表单</title>

<script type="text/javascript">

var isFlag = false; //表单是否已经提交标识,默认为false

 

function submitFlag() {

 

if (isFlag == false) {

isFlag = true;

return true;

} else {

return false;

}

 

}

</script>

</head>

 

<body>

<form action="${pageContext.request.contextPath}/DoFormServlet"

method="post" onsubmit="return submitFlag()">

用户名:<input type="text" name="userName"> <input type="submit"

value="提交" id="submit">

</form>

</body>

</html>

 除了用这种方式以外,常常见的另外一种方式就是表单提交以后,将提交按钮设置为不可用,让用户没有机会点击第二次提交按钮,代码以下:

 

function dosubmit(){

    //获取表单提交按钮

    var btnSubmit = document.getElementById("submit");

    //将表单提交按钮设置为不可用,这样就能够避免用户再次点击提交按钮

    btnSubmit.disabled= "disabled";

    //返回true让表单能够正常提交

    return true;

}

 

 

B 使用后端提交解决

 对于【场景二】和【场景三】致使表单重复提交的问题,既然客户端没法解决,那么就在服务器端解决,在服务器端解决就须要用到session了。

  具体的作法:在服务器端生成一个惟一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。而后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一块儿提交到服务器端,而后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,若是不一致,那就是重复提交了,此时服务器端就能够不处理重复提交的表单。若是相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
  在下列状况下,服务器程序将拒绝处理用户提交的表单请求:

存储Session域中的Token(令牌)与表单提交的Token(令牌)不一样。

当前用户的Session中不存在Token(令牌)。

用户提交的表单数据中没有Token(令牌)。

转发代码:

@WebServlet("/ForwardServlet")

public class ForwardServlet extends HttpServlet {

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

req.getSession().setAttribute("sesionToken", TokenUtils.getToken());

req.getRequestDispatcher("form.jsp").forward(req, resp);

}

}

 

转发页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Form表单</title>

 

</head>

 

<body>

<form action="${pageContext.request.contextPath}/DoFormServlet"

method="post" onsubmit="return dosubmit()">

<input type="hidden" name="token" value="${sesionToken}"> 用户名:<input type="text"

name="userName"> <input type="submit" value="提交" id="submit">

</form>

</body>

</html>

后端Java代码:

@WebServlet("/DoFormServlet")

public class DoFormServlet extends HttpServlet {

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

req.setCharacterEncoding("UTF-8");

boolean flag = isFlag(req);

if (!flag) {

resp.getWriter().write("已经提交...");

System.out.println("数据已经提交了..");

return;

}

String userName = req.getParameter("userName");

try {

Thread.sleep(300);

} catch (Exception e) {

// TODO: handle exception

}

System.out.println("往数据库插入数据...." + userName);

resp.getWriter().write("success");

}

 

public boolean isFlag(HttpServletRequest request) {

HttpSession session = request.getSession();

String sesionToken = (String) session.getAttribute("sesionToken");

String token = request.getParameter("token");

if (!(token.equals(sesionToken))) {

return false;

}

session.removeAttribute("sesionToken");

return true;

}

}

 

 

 

2.如何防护XSS攻击

XSS攻击使用Javascript脚本注入进行攻击

解决办法:就是请求可能会发送的特殊字符、javascript标签转换为html代码执行

例如在表单中注入: <script>location.href='http://www.itmayiedu.com'</script>

注意:谷歌浏览器 已经防止了XSS攻击,为了演示效果,最好使用火狐浏览器

实例:

演示:

代码: fromToXss.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

</head>

<body>

<form action="XssDemo" method="post">

<input type="text" name="userName"> <input type="submit">

</form>

</body>

</html>

代码: XssDemo

import java.io.IOException;

 

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

@WebServlet("/XssDemo")

public class XssDemo extends HttpServlet {

 

@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

String userName = req.getParameter("userName");

req.setAttribute("userName", userName);

req.getRequestDispatcher("showUserName.jsp").forward(req, resp);

}

 

 

}

 

 

代码: showUserName.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

 

</head>

<body>userName:${userName}

 

</body>

</html>

 

7.1 解決方案

使用Fileter过滤器过滤器注入标签

FilterDemo

import java.io.IOException;

import java.util.Map;

 

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.annotation.WebFilter;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

/**

 * 使用Filter 打印参数

 *

 * @author Administrator

 *

 */

 

public class FilterDemo implements Filter {

public FilterDemo() {

System.out.println("FilterDemo 构造函数被执行...");

}

 

/**

 * 销毁

 */

public void destroy() {

System.out.println("destroy");

}

 

public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse,

FilterChain paramFilterChain) throws IOException, ServletException {

System.out.println("doFilter");

HttpServletRequest request = (HttpServletRequest) paramServletRequest;

XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(request);

paramFilterChain.doFilter(xssRequestWrapper, paramServletResponse);

 

}

/**

 * 初始化

 */

public void init(FilterConfig paramFilterConfig) throws ServletException {

System.out.println("init");

}

}

 

 

XssAndSqlHttpServletRequestWrapper

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang3.StringEscapeUtils;

import org.apache.commons.lang3.StringUtils;

 

/**

 * 防止XSS攻击

 */

public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {

HttpServletRequest request;

public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {

super(request);

this.request = request;

}

@Override

public String getParameter(String name) {

String value = request.getParameter(name);

System.out.println("name:" + name + "," + value);

if (!StringUtils.isEmpty(value)) {

// 转换Html

value = StringEscapeUtils.escapeHtml4(value);

}

return value;

}

}

 

 

3.跨域实战解决方案 

跨域缘由产生:在当前域名请求网站中,默认不容许经过ajax请求发送其余域名。

XMLHttpRequest cannot load 跨域问题解决办法

A 使用后台response添加header

后台response添加header,response.setHeader("Access-Control-Allow-Origin", "*"); 支持全部网站

B 使用JSONP

JSONP的优缺点:

JSONP支持get请求不支持psot请求

什么SQL语句注入

C 使用接口网关

使用nginx转发。

配置:

server {

        listen       80;

        server_name  www.itmayiedu.com;

        location /A {

    proxy_pass  http://a.a.com:81/A;

index  index.html index.htm;

        }

location /B {

    proxy_pass  http://b.b.com:81/B;

index  index.html index.htm;

        }

    }

 

相关:

 

 

D 使用内部服务器转发

 内部服务器使用HttpClient技术进行转发

 

 

4.什么是SQL语句注入

Sql语句若是是经过拼接方式执行,传入参数 ‘  or 1=1 发生语句成立,致使数据错误。

应该使用PreparedStatement 先编译 执行 经过?号穿参数的方式进行执行

5.怎么防护DDOC

A nginx配置DDOS

限制请求速度

设置NginxNginx Plus的链接请求在一个真实用户请求的合理范围内。好比,若是你以为一个正经常使用户每两秒能够请求一次登陆页面,你就能够设置Nginx每两秒钟接收一个客户端IP的请求(大约等同于每分钟30个请求)。

limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m;

server {

...

location /login.html {

limit_req zone=one;

...

}

}

 

 

`limit_req_zone`命令设置了一个叫one的共享内存区来存储请求状态的特定键值,在上面的例子中是客户端IP($binary_remote_addr)location块中的`limit_req`经过引用one共享内存区来实现限制访问/login.html的目的。

限制请求速度

 

设置NginxNginx Plus的链接数在一个真实用户请求的合理范围内。好比,你能够设置每一个客户端IP链接/store不能够超过10个。

 

5缓存部分

1.什么是NOSQL?

NoSQL 是 Not Only SQL 的缩写,意即"不只仅是SQL"的意思,泛指非关系型的数据库。强调Key-Value Stores和文档数据库的优势,而不是单纯的反对RDBMS。

NoSQL产品是传统关系型数据库的功能阉割版本,经过减小用不到或不多用的功能,来大幅度提升产品性能

NoSQL产品 redis、mongodb MembaseHBase 

2.什么Redis?

Redis 是彻底开源免费的,遵照BSD协议,是一个高性能的key-value数据库。

Redis 与其余 key - value 缓存产品有如下三个特色:

Redis支持数据的持久化,能够将内存中的数据保存在磁盘中,重启的时候能够再次加载进行使用。

Redis不只仅支持简单的key-value类型的数据,同时还提供listsetzsethash等数据结构的存储。

Redis支持数据的备份,即master-slave模式的数据备份。

 

3.Redis应用场景

   主要可以体现 解决数据库的访问压力。

   例如:短信验证码时间有效期session共享解决方案

4.Redis优点

性能极高 – Redis能读的速度是110000/s,写的速度是81000/s

丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets Ordered Sets 数据类型操做。

原子 – Redis的全部操做都是原子性的,同时Redis还支持对几个操做全并后的原子性执行。

丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过时等等特性。

5.Redis与其余key-value存储有什么不一样?

Redis有着更为复杂的数据结构而且提供对他们的原子性操做,这是一个不一样于其余数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。

Redis运行在内存中可是能够持久化到磁盘,因此在对不一样数据集进行高速读写时须要权衡内存,由于数据量不能大于硬件内存。在内存数据库方面的另外一个优势是,相比在磁盘上相同的复杂的数据结构,在内存中操做起来很是简单,这样Redis能够作不少内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,由于他们并不须要进行随机访问。

6.Redis的基本数据类型

A 字符串类型(String)

redis 127.0.0.1:6379> SET mykey "redis"

OK

redis 127.0.0.1:6379> GET mykey

"redis"

 

在上面的例子中,SETGETredis中的命令,而mykey是键的名称。

Redis字符串命令用于管理Redis中的字符串值。如下是使用Redis字符串命令的语法。

redis 127.0.0.1:6379> COMMAND KEY_NAME

Shell

示例

redis 127.0.0.1:6379> SET mykey "redis"

OK

redis 127.0.0.1:6379> GET mykey

"redis"

Shell

在上面的例子中,SETGETredis中的命令,而mykey是键的名称。

Redis字符串命令

下表列出了一些用于在Redis中管理字符串的基本命令。

编号

命令

描述说明

1

SET key value

此命令设置指定键的值。

2

GET key

获取指定键的值。

3

GETRANGE key start end

获取存储在键上的字符串的子字符串。

4

GETSET key value

设置键的字符串值并返回其旧值。

5

GETBIT key offset

返回在键处存储的字符串值中偏移处的位值。

6

MGET key1 [key2..]

获取全部给定键的值

7

SETBIT key offset value

存储在键上的字符串值中设置或清除偏移处的位

8

SETEX key seconds value

使用键和到期时间来设置值

9

SETNX key value

设置键的值,仅当键不存在时

10

SETRANGE key offset value

在指定偏移处开始的键处覆盖字符串的一部分

11

STRLEN key

获取存储在键中的值的长度

12

MSET key value [key value …]

为多个键分别设置它们的值

13

MSETNX key value [key value …]

为多个键分别设置它们的值,仅当键不存在时

14

PSETEX key milliseconds value

设置键的值和到期时间(以毫秒为单位)

15

INCR key

将键的整数值增长1

16

INCRBY key increment

将键的整数值按给定的数值增长

17

INCRBYFLOAT key increment

将键的浮点值按给定的数值增长

18

DECR key

将键的整数值减1

19

DECRBY key decrement

按给定数值减小键的整数值

20

APPEND key value

将指定值附加到键

 

B 列表类型(List)

Redis列表是简单的字符串列表,按照插入顺序排序。你能够添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多能够包含 232 - 1 个元素 (4294967295, 每一个列表超过40亿个元素)

 

redis 127.0.0.1:6379> LPUSH runoobkey redis

(integer) 1

redis 127.0.0.1:6379> LPUSH runoobkey mongodb

(integer) 2

redis 127.0.0.1:6379> LPUSH runoobkey mysql

(integer) 3

redis 127.0.0.1:6379> LRANGE runoobkey 0 10

 

1) "mysql"

2) "mongodb"

3) "redis"

 

Redis 列表命令

下表列出了列表相关的基本命令:

序号

命令及描述

1

BLPOP key1 [key2 ] timeout 
移出并获取列表的第一个元素, 若是列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

2

BRPOP key1 [key2 ] timeout 
移出并获取列表的最后一个元素, 若是列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

3

BRPOPLPUSH source destination timeout 
从列表中弹出一个值,将弹出的元素插入到另一个列表中并返回它; 若是列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

4

LINDEX key index 
经过索引获取列表中的元素

5

LINSERT key BEFORE|AFTER pivot value 
在列表的元素前或者后插入元素

6

LLEN key 
获取列表长度

7

LPOP key 
移出并获取列表的第一个元素

8

LPUSH key value1 [value2] 
将一个或多个值插入到列表头部

9

LPUSHX key value 
将一个值插入到已存在的列表头部

10

LRANGE key start stop 
获取列表指定范围内的元素

11

LREM key count value 
移除列表元素

12

LSET key index value 
经过索引设置列表元素的值

13

LTRIM key start stop 
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间以内的元素都将被删除。

14

RPOP key 
移除并获取列表最后一个元素

15

RPOPLPUSH source destination 
移除列表的最后一个元素,并将该元素添加到另外一个列表并返回

16

RPUSH key value1 [value2] 
在列表中添加一个或多个值

17

RPUSHX key value 
为已存在的列表添加值

 

 

C 集合(Set)

RedisSetstring类型的无序集合。集合成员是惟一的,这就意味着集合中不能出现重复的数据。

Redis 中 集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是O(1)

集合中最大的成员数为 232 - 1 (4294967295, 每一个集合可存储40多亿个成员)

 

实例

redis 127.0.0.1:6379> SADD runoobkey redis

(integer) 1

redis 127.0.0.1:6379> SADD runoobkey mongodb

(integer) 1

redis 127.0.0.1:6379> SADD runoobkey mysql

(integer) 1

redis 127.0.0.1:6379> SADD runoobkey mysql

(integer) 0

redis 127.0.0.1:6379> SMEMBERS runoobkey

 

1) "mysql"

2) "mongodb"

3) "redis"

在以上实例中咱们经过 SADD 命令向名为 runoobkey 的集合插入的三个元素。

 

Redis 集合命令

下表列出了 Redis 集合基本命令:

序号

命令及描述

1

SADD key member1 [member2] 
向集合添加一个或多个成员

2

SCARD key 
获取集合的成员数

3

SDIFF key1 [key2] 
返回给定全部集合的差集

4

SDIFFSTORE destination key1 [key2] 
返回给定全部集合的差集并存储在 destination

5

SINTER key1 [key2] 
返回给定全部集合的交集

6

SINTERSTORE destination key1 [key2] 
返回给定全部集合的交集并存储在 destination

7

SISMEMBER key member 
判断 member 元素是不是集合 key 的成员

8

SMEMBERS key 
返回集合中的全部成员

9

SMOVE source destination member 
member 元素从 source 集合移动到 destination 集合

10

SPOP key 
移除并返回集合中的一个随机元素

11

SRANDMEMBER key [count] 
返回集合中一个或多个随机数

12

SREM key member1 [member2] 
移除集合中一个或多个成员

13

SUNION key1 [key2] 
返回全部给定集合的并集

14

SUNIONSTORE destination key1 [key2] 
全部给定集合的并集存储在 destination 集合中

15

SSCAN key cursor [MATCH pattern] [COUNT count] 
迭代集合中的元素

 

 

D 有序集合(sorted set)

Redis 有序集合和集合同样也是string类型元素的集合,且不容许重复的成员。

不一样的是每一个元素都会关联一个double类型的分数。redis正是经过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是惟一的,但分数(score)却能够重复。

集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每一个集合可存储40多亿个成员)

实例

redis 127.0.0.1:6379> ZADD runoobkey 1 redis

(integer) 1

redis 127.0.0.1:6379> ZADD runoobkey 2 mongodb

(integer) 1

redis 127.0.0.1:6379> ZADD runoobkey 3 mysql

(integer) 1

redis 127.0.0.1:6379> ZADD runoobkey 3 mysql

(integer) 0

redis 127.0.0.1:6379> ZADD runoobkey 4 mysql

(integer) 0

redis 127.0.0.1:6379> ZRANGE runoobkey 0 10 WITHSCORES

 

1) "redis"

2) "1"

3) "mongodb"

4) "2"

5) "mysql"

6) "4"

在以上实例中咱们经过命令 ZADD redis 的有序集合中添加了三个值并关联上分数。

 

Redis 有序集合命令

下表列出了 redis 有序集合的基本命令:

序号

命令及描述

1

ZADD key score1 member1 [score2 member2] 
向有序集合添加一个或多个成员,或者更新已存在成员的分数

2

ZCARD key 
获取有序集合的成员数

3

ZCOUNT key min max 
计算在有序集合中指定区间分数的成员数

4

ZINCRBY key increment member 
有序集合中对指定成员的分数加上增量 increment

5

ZINTERSTORE destination numkeys key [key ...] 
计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key

6

ZLEXCOUNT key min max 
在有序集合中计算指定字典区间内成员数量

7

ZRANGE key start stop [WITHSCORES] 
经过索引区间返回有序集合成指定区间内的成员

8

ZRANGEBYLEX key min max [LIMIT offset count] 
经过字典区间返回有序集合的成员

9

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 
经过分数返回有序集合指定区间内的成员

10

ZRANK key member 
返回有序集合中指定成员的索引

11

ZREM key member [member ...] 
移除有序集合中的一个或多个成员

12

ZREMRANGEBYLEX key min max 
移除有序集合中给定的字典区间的全部成员

13

ZREMRANGEBYRANK key start stop 
移除有序集合中给定的排名区间的全部成员

14

ZREMRANGEBYSCORE key min max 
移除有序集合中给定的分数区间的全部成员

15

ZREVRANGE key start stop [WITHSCORES] 
返回有序集中指定区间内的成员,经过索引,分数从高到底

16

ZREVRANGEBYSCORE key max min [WITHSCORES] 
返回有序集中指定分数区间内的成员,分数从高到低排序

17

ZREVRANK key member 
返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序

18

ZSCORE key member 
返回有序集中,成员的分数值

19

ZUNIONSTORE destination numkeys key [key ...] 
计算给定的一个或多个有序集的并集,并存储在新的 key

20

ZSCAN key cursor [MATCH pattern] [COUNT count] 
迭代有序集合中的元素(包括元素成员和元素分值)

 

E 哈希(Hash)

Redis hash 是一个string类型的fieldvalue的映射表,hash特别适合用于存储对象。

Redis 中每一个 hash 能够存储 232 - 1 键值对(40多亿)。

实例

127.0.0.1:6379>  HMSET runoobkey name "redis tutorial" description "redis basic commands for caching" likes 20 visitors 23000

OK

127.0.0.1:6379>  HGETALL runoobkey

1) "name"

2) "redis tutorial"

3) "description"

4) "redis basic commands for caching"

5) "likes"

6) "20"

7) "visitors"

8) "23000"

hset  key  mapHey MapValue

在以上实例中,咱们设置了 redis 的一些描述信息(name, description, likes, visitors) 到哈希表的 runoobkey 中。

 

Redis hash 命令

下表列出了 redis hash 基本的相关命令:

序号

命令及描述

1

HDEL key field2 [field2] 
删除一个或多个哈希表字段

2

HEXISTS key field 
查看哈希表 key 中,指定的字段是否存在。

3

HGET key field 
获取存储在哈希表中指定字段的值。

4

HGETALL key 
获取在哈希表中指定 key 的全部字段和值

5

HINCRBY key field increment 
为哈希表 key 中的指定字段的整数值加上增量 increment

6

HINCRBYFLOAT key field increment 
为哈希表 key 中的指定字段的浮点数值加上增量 increment

7

HKEYS key 
获取全部哈希表中的字段

8

HLEN key 
获取哈希表中字段的数量

9

HMGET key field1 [field2] 
获取全部给定字段的值

10

HMSET key field1 value1 [field2 value2 ] 
同时将多个 field-value (-)对设置到哈希表 key 中。

11

HSET key field value 
将哈希表 key 中的字段 field 的值设为 value

12

HSETNX key field value 
只有在字段 field 不存在时,设置哈希表字段的值。

13

HVALS key 
获取哈希表中全部值

14

HSCAN key cursor [MATCH pattern] [COUNT count] 
迭代哈希表中的键值对。

 

7.什么是redis的主从复制

概述

1redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库能够进行读写操做,当发生写操做的时候自动将数据同步到从数据库,而从数据库通常是只读的,并接收主数据库同步过来的数据,一个主数据库能够有多个从数据库,而一个从数据库只能有一个主数据库。

2、经过redis的复制功能能够很好的实现数据库的读写分离,提升服务器的负载能力。主数据库主要进行写操做,而从数据库负责读操做。

主从复制过程

主从复制过程:见下图

 

 

过程:

1:当一个从数据库启动时,会向主数据库发送sync命令,

2:主数据库接收到sync命令后会开始在后台保存快照(执行rdb操做),并将保存期间接收到的命令缓存起来

3:当快照完成后,redis会将快照文件和全部缓存的命令发送给从数据库。

4:从数据库收到后,会载入快照文件并执行收到的缓存的命令。

修改redis.conf

修改从redis中 redis.conf文件

slaveof 192.168.33.130 6379  

masterauth 123456--- redis服务器配置了密码,则须要配置

 

8.什么是哨兵机制

Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器,该系统执行如下三个任务:

·        监控(Monitoring)哨兵(sentinel) 会不断地检查你的MasterSlave是否运做正常。

·        提醒(Notification):当被监控的某个 Redis出现问题时哨兵(sentinel) 能够经过 API 向管理员或者其余应用程序发送通知。

·        自动故障迁移(Automatic failover):当一个Master不能正常工做时,哨兵(sentinel) 会开始一次自动故障迁移操做,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其余Slave改成复制新的Master; 当客户端试图链接失效的Master,集群也会向客户端返回新Master的地址,使得集群能够使用Master代替失效Master

哨兵(sentinel) 是一个分布式系统,你能够在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪一个Slave做为新的Master.

每一个哨兵(sentinel) 会向其它哨兵(sentinel)masterslave定时发送消息,以确认对方是否,若是发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的主观认为宕机” Subjective Down,简称sdown).

哨兵群中的多数sentinel,都报告某一master没响应,系统才认为该master"完全死亡"(:客观上的真正down,Objective Down,简称odown),经过必定的vote算法,从剩下的slave节点中,选一台提高为master,而后自动修改相关配置.

虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你能够在启动一个普通 Redis 服务器时经过给定 --sentinel 选项来启动哨兵(sentinel).

哨兵(sentinel) 的一些设计思路和zookeeper很是相似

单个哨兵(sentinel)

 

10.2 哨兵模式修改配置

实现步骤:

1.拷贝到etc目录

cp sentinel.conf  /usr/local/redis/etc

2.修改sentinel.conf配置文件

sentinel monitor mymast  192.168.110.133 6379 1  #主节点 名称 IP 端口号 选举次数

3. 修改心跳检测 5000毫秒

sentinel down-after-milliseconds mymaster 5000

4.sentinel parallel-syncs mymaster 2 --- 作多多少合格节点

5. 启动哨兵模式

./redis-server /usr/local/redis/etc/sentinel.conf --sentinel &

6. 中止哨兵模式

 

9.Redis事物

Redis 事务能够一次执行多个命令, 而且带有如下两个重要的保证:

事务是一个单独的隔离操做:事务中的全部命令都会序列化、按顺序地执行。事务在执行的过程当中,不会被其余客户端发送来的命令请求所打断。

事务是一个原子操做:事务中的命令要么所有被执行,要么所有都不执行。

一个事务从开始到执行会经历如下三个阶段:

开始事务。

命令入队。

执行事务。

如下是一个事务的例子, 它先以 MULTI 开始一个事务, 而后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的全部命令:

redis 127.0.0.1:6379> MULTI

OK

 

redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"

QUEUED

 

redis 127.0.0.1:6379> GET book-name

QUEUED

 

redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"

QUEUED

 

redis 127.0.0.1:6379> SMEMBERS tag

QUEUED

 

redis 127.0.0.1:6379> EXEC

1) OK

2) "Mastering C++ in 21 days"

3) (integer) 3

4) 1) "Mastering Series"

   2) "C++"

   3) "Programming"

 

 

 

10.Redis持久化

什么是Redis持久化,就是内存数据保存到硬盘。

Redis 持久化存储 (AOF RDB 两种模式)

A RDB持久化

RDB 是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
优势:使用单独子进程来进行持久化,主进程不会进行任何 IO 操做,保证了 redis 的高性能
缺点:RDB 是间隔一段时间进行持久化,若是持久化之间 redis 发生故障,会发生数据丢失。因此这种方式更适合数据要求不严谨的时候

这里说的这个执行数据写入到临时文件的时间点是能够经过配置来本身肯定的,经过配置redis n 秒内若是超过 m key 被修改这执行一次 RDB 操做。这个操做就相似于在这个时间点来保存一次 Redis 的全部数据,一次快照数据。全部这个持久化方法也一般叫作 snapshots

RDB 默认开启,redis.conf 中的具体配置参数以下;

#dbfilename:持久化数据存储在本地的文件

dbfilename dump.rdb

#dir:持久化数据存储在本地的路径,若是是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下

dir ./

##snapshot触发的时机,save    

##以下为900秒后,至少有一个变动操做,才会snapshot  

##对于此值的设置,须要谨慎,评估系统的变动操做密集程度  

##能够经过“save “””来关闭snapshot功能  

#save时间,如下分别表示更改了1key时间隔900s进行持久化存储;更改了10key300s进行存储;更改10000key60s进行存储。

save 900 1

save 300 10

save 60 10000

##snapshot时出现错误没法继续时,是否阻塞客户端变动操做错误可能由于磁盘已满/磁盘故障/OS级别异常等  

stop-writes-on-bgsave-error yes  

##是否启用rdb文件压缩,默认为“yes”,压缩每每意味着额外的cpu消耗,同时也意味这较小的文件尺寸以及较短的网络传输时间  

rdbcompression yes  

 

B AOF持久化

Append-only file,将操做 + 数据以格式化指令的方式追加到操做日志文件的尾部,在 append 操做返回后(已经写入到文件或者即将写入),才进行实际的数据变动,日志文件保存了历史全部的操做过程;当 server 须要数据恢复时,能够直接 replay 此日志文件,便可还原全部的操做过程。AOF 相对可靠,它和 mysql bin.logapache.logzookeeper txn-log 简直殊途同归。AOF 文件内容是字符串,很是容易阅读和解析。
优势:能够保持更高的数据完整性,若是设置追加 file 的时间是 1s,若是 redis 发生故障,最多会丢失 1s 的数据;且若是日志写入不完整支持 redis-check-aof 来进行日志修复;AOF 文件没被 rewrite 以前(文件过大时会对命令进行合并重写),能够删除其中的某些命令(好比误操做的 flushall)。
缺点:AOF 文件比 RDB 文件大,且恢复速度慢。

咱们能够简单的认为 AOF 就是日志文件,此文件只会记录变动操做”(例如:set/del ),若是 server 中持续的大量变动操做,将会致使 AOF 文件很是的庞大,意味着 server 失效后,数据恢复的过程将会很长;事实上,一条数据通过屡次变动,将会产生多条 AOF 记录,其实只要保存当前的状态,历史的操做记录是能够抛弃的;由于 AOF 持久化模式还伴生了“AOF rewrite”
AOF 的特性决定了它相对比较安全,若是你指望数据更少的丢失,那么能够采用 AOF 模式。若是 AOF 文件正在被写入时忽然 server 失效,有可能致使文件的最后一次记录是不完整,你能够经过手工或者程序的方式去检测并修正不完整的记录,以便经过 aof 文件恢复可以正常;同时须要提醒,若是你的 redis 持久化手段中有 aof,那么在 server 故障失效后再次启动前,须要检测 aof 文件的完整性。

AOF 默认关闭,开启方法,修改配置文件 reds.confappendonly yes

##此选项为aof功能的开关,默认为“no”,能够经过“yes”来开启aof功能  

##只有在“yes”下,aof重写/文件同步等特性才会生效  

appendonly yes  

 

##指定aof文件名称  

appendfilename appendonly.aof  

 

##指定aof操做中文件同步策略,有三个合法值:always everysec no,默认为everysec  

appendfsync everysec  

##aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示不暂缓“yes”表示暂缓,默认为“no”  

no-appendfsync-on-rewrite no  

 

##aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”  

auto-aof-rewrite-min-size 64mb  

 

##相对于上一次”rewrite,本次rewrite触发时aof文件应该增加的百分比。  

##每一次rewrite以后,redis都会记录下此时aof”文件的大小(例如A),那么当aof文件增加到A*(1 + p)以后  

##触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。  

auto-aof-rewrite-percentage 100  

 

 

AOF 是文件操做,对于变动操做比较密集的 server,那么必将形成磁盘 IO 的负荷加剧;此外 linux 对文件操做采起了延迟写入手段,即并不是每次 write 操做都会触发实际磁盘操做,而是进入了 buffer 中,当 buffer 数据达到阀值时触发实际写入(也有其余时机),这是 linux 对文件系统的优化,可是这却有可能带来隐患,若是 buffer 没有刷新到磁盘,此时物理机器失效(好比断电),那么有可能致使最后一条或者多条 aof 记录的丢失。经过上述配置文件,能够得知 redis 提供了 3 aof 记录同步选项:

always:每一条 aof 记录都当即同步到文件,这是最安全的方式,也觉得更多的磁盘操做和阻塞延迟,是 IO 开支较大。

everysec:每秒同步一次,性能和安全都比较中庸的方式,也是 redis 推荐的方式。若是遇到物理服务器故障,有可能致使最近一秒内 aof 记录丢失(可能为部分丢失)

noredis 并不直接调用文件同步,而是交给操做系统来处理,操做系统能够根据 buffer 填充状况 / 通道空闲时间等择机触发同步;这是一种普通的文件操做方式。性能较好,在物理服务器故障时,数据丢失量会因 OS 配置有关。

其实,咱们能够选择的太少,everysec 是最佳的选择。若是你很是在乎每一个数据都极其可靠,建议你选择一款关系性数据库吧。
AOF 文件会不断增大,它的大小直接影响故障恢复的时间, 并且 AOF 文件中历史操做是能够丢弃的。AOF rewrite 操做就是压缩”AOF 文件的过程,固然 redis 并无采用基于原 aof 文件来重写的方式,而是采起了相似 snapshot 的方式:基于 copy-on-write,全量遍历内存中数据,而后逐个序列到 aof 文件中。所以 AOF rewrite 可以正确反应当前内存数据的状态,这正是咱们所须要的;*rewrite 过程当中,对于新的变动操做将仍然被写入到原 AOF 文件中,同时这些新的变动操做也会被 redis 收集起来(buffercopy-on-write 方式下,最极端的多是全部的 key 都在此期间被修改,将会耗费 2 倍内存),当内存数据被所有写入到新的 aof 文件以后,收集的新的变动操做也将会一并追加到新的 aof 文件中,此后将会重命名新的 aof 文件为 appendonly.aof, 此后全部的操做都将被写入新的 aof 文件。若是在 rewrite 过程当中,出现故障,将不会影响原 AOF 文件的正常工做,只有当 rewrite 完成以后才会切换文件,由于 rewrite 过程是比较可靠的。*

触发 rewrite 的时机能够经过配置文件来声明,同时 redis 中能够经过 bgrewriteaof 指使人工干预。

redis-cli -h ip -p port bgrewriteaof

由于 rewrite 操做 /aof 记录同步 /snapshot 都消耗磁盘 IOredis 采起了“schedule”策略:不管是人工干预仍是系统触发,snapshot rewrite 须要逐个被执行。

AOF rewrite 过程并不阻塞客户端请求。系统会开启一个子进程来完成。

 

C AOFRDB区别

OF RDB 各有优缺点,这是有它们各自的特色所决定:

1) AOF 更加安全,能够将数据更加及时的同步到文件中,可是 AOF 须要较多的磁盘 IO 开支,AOF 文件尺寸较大,文件内容恢复数度相对较慢。
*2) snapshot,安全性较差,它是正常时期数据备份以及 master-slave 数据同步的最佳手段,文件尺寸较小,恢复数度较快。

能够经过配置文件来指定它们中的一种,或者同时使用它们(不建议同时使用),或者所有禁用,在架构良好的环境中,master 一般使用 AOFslave 使用 snapshot,主要缘由是 master 须要首先确保数据完整性,它做为数据备份的第一选择;slave 提供只读服务(目前 slave 只能提供读取服务),它的主要目的就是快速响应客户端 read 请求;可是若是你的 redis 运行在网络稳定性差 / 物理环境糟糕状况下,建议你 master slave 均采起 AOF,这个在 master slave 角色切换时,能够减小人工数据备份”/“人工引导数据恢复的时间成本;若是你的环境一切很是良好,且服务须要接收密集性的 write 操做,那么建议 master 采起 snapshot,而 slave 采用 AOF

11.Redis发布订阅

Redis 发布订阅(pub/sub)是一种消息通讯模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端能够订阅任意数量的频道。

下图展现了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 client5 client1 之间的关系:

 

当有新消息经过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

 

 

如下实例演示了发布订阅是如何工做的。在咱们实例中咱们建立了订阅频道名为 redisChat:

redis 127.0.0.1:6379> SUBSCRIBE redisChat

 

Reading messages... (press Ctrl-C to quit)

1) "subscribe"

2) "redisChat"

3) (integer) 1

如今,咱们先从新开启个 redis 客户端,而后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。

redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"

 

(integer) 1

 

redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"

 

(integer) 1

 

# 订阅者的客户端会显示以下消息

1) "message"

2) "redisChat"

3) "Redis is a great caching technique"

1) "message"

2) "redisChat"

3) "Learn redis by runoob.com"

 

Redis 发布订阅命令

下表列出了 redis 发布订阅经常使用命令:

序号

命令及描述

1

PSUBSCRIBE pattern [pattern ...] 
订阅一个或多个符合给定模式的频道。

2

PUBSUB subcommand [argument [argument ...]] 
查看订阅与发布系统状态。

3

PUBLISH channel message 
将信息发送到指定的频道。

4

PUNSUBSCRIBE [pattern [pattern ...]] 
退订全部给定模式的频道。

5

SUBSCRIBE channel [channel ...] 
订阅给定的一个或多个频道的信息。

6

UNSUBSCRIBE [channel [channel ...]] 
指退订给定的频道。

 

 

12).Redis如何集群?

架构细节:

(1)全部的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.

(2)节点的fail是经过集群中超过半数的节点检测失效时才生效.

(3)客户端与redis节点直连,不须要中间proxy.客户端不须要链接集群全部节点,链接集群中任何一个可用节点便可

(4)redis-cluster把全部的物理节点映射到[0-16383]slot,cluster 负责维护node<->slot<->value。Redis 集群中内置了 16384 个哈希槽,当须要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,而后把结果对 16384 求余数,这样每一个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大体均等的将哈希槽映射到不一样的节点

redis-cluster投票:容错-集群中全部master参与投票,若是半数以上master节点与其中一个 master节点通讯超过(cluster-node-timeout),认为该master节点挂掉.

集群管理工具(redis-trib.rb)是使用ruby脚本语言编写的安装rubyredis接口

搭建集群

建立集群

./redis-trib.rb create --replicas 1 192.168.242.137:7001 192.168.242.137:7002

192.168.242.137:7003 192.168.242.137:7004 192.168.242.137:7005  192.168.242.137:7006

链接集群./redis-cli -h 192.168.242.137 -p 7001 c

查看集群信息:cluster info

jedis链接集群

redis.clients.jedis.JedisPoolConfig

redis.clients.jedis.JedisCluster

13).无磁盘复制

经过前面的复制过程咱们了解到,主库接收到SYNC的命令时会执行RDB过程,即便在配置文件中禁用RDB持久化也会生成,那么若是主库所在的服务器磁盘IO性能较差,那么这个复制过程就会出现瓶颈,庆幸的是,Redis2.8.18版本开始实现了无磁盘复制功能

Redis在与从数据库进行复制初始化时将不会将快照存储到磁盘,而是直接经过网络发送给从数据库,避免了IO性能差问题。开启无磁盘复制:repl-diskless-sync yes

6nginx

1.什么nginx

 

nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。由俄罗斯的程序设计师Igor Sysoev所开发,官方测试nginx可以支支撑5万并发连接,而且cpu、内存等资源消耗却很是低,运行很是稳定,因此如今不少知名的公司都在使用nginx。

2.nginx应用场景

一、http服务器。Nginx是一个http服务能够独立提供http服务。能够作网页静态服务器。

二、虚拟主机。能够实如今一台服务器虚拟出多个网站。例如我的网站使用的虚拟主机。

三、反向代理,负载均衡。当网站的访问量达到必定程度后,单台服务器不能知足用户的请求时,须要用多台服务器集群能够使用nginx作反向代理。而且多台服务器能够平均分担负载,不会由于某台服务器负载高宕机而某台服务器闲置的状况。

 

 

3.nginx优缺点

占内存小,能够实现高并发链接、处理响应快。

能够实现http服务器、虚拟主机、反向代理、负载均衡。

nginx配置简单

能够不暴露真实服务器IP地址

4.nginx.conf 介绍

A nginx.conf文件的结构

nginx的配置由特定的标识符(指令符)分为多个不一样的模块。 
指令符分为简单指令块指令

  • 简单指令格式:[name parameters;]
  • 块指令格式:和简单指令格式有同样的结构,但其结束标识符不是分号,而是大括号{},块指令内部能够包含simple directives 和block directives, 能够称块指令为上下文(e.g. events, http, server, location)

conf文件中,全部不属于块指令的简单指令都属于main上下文的,http块指令属于main上下文,server块指令http上下文。

B 配置静态访问

Web server很重要一部分工做就是提供静态页面的访问,例如images, html page。nginx能够经过不一样的配置,根据request请求,从本地的目录提供不一样的文件返回给客户端。 
打开安装目录下的nginx.conf文件,默认配置文件已经在http指令块中建立了一个空的server块,在nginx-1.8.0中的http块中已经建立了一个默认的server块。内容以下:

server {

        listen       80;

        server_name  localhost;

        location / {

            root   html;

            index  index.html index.htm;

        }

        error_page   500 502 503 504  /50x.html;

        location = /50x.html {

            root   html;

        }

}  

 

5.nginx实现反向代理

A 什么是反向代理?

反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的链接请求,而后将请求转发给内部网络上的服务器,并将从服务器上获得的结果返回给internet上请求链接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

 

启动一个Tomcat 127.0.0.1:8080

使用nginx反向代理 8080.itmayiedu.com 直接跳转到127.0.0.1:8080

B Host文件新增

127.0.0.1 8080.itmayiedu.com

127.0.0.1 b8081.itmayiedu.com

C nginx.conf 配置

 

配置信息:

    server {

        listen       80;

        server_name  8080.itmayiedu.com;

        location / {

    proxy_pass  http://127.0.0.1:8080;

index  index.html index.htm;

        }

    }

     server {

        listen       80;

        server_name  b8081.itmayiedu.com;

        location / {

    proxy_pass  http://127.0.0.1:8081;

index  index.html index.htm;

        }

    }

 

 

6.nginx实现负载均衡

A 什么是负载均衡

负载均衡 创建在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增长吞吐量、增强网络数据处理能力、提升网络的灵活性和可用性。

负载均衡,英文名称为Load Balance,其意思就是分摊到多个操做单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工做任务。

 

 

B 负载均衡策略

一、轮询(默认)
每一个请求按时间顺序逐一分配到不一样的后端服务器,若是后端服务器down掉,能自动剔除。 
upstream backserver { 
server 192.168.0.14; 
server 192.168.0.15; 


二、指定权重
指定轮询概率,weight和访问比率成正比,用于后端服务器性能不均的状况。 
upstream backserver { 
server 192.168.0.14 weight=10; 
server 192.168.0.15 weight=10; 


三、IP绑定 ip_hash
每一个请求按访问ip的hash结果分配,这样每一个访客固定访问一个后端服务器,能够解决session的问题。 
upstream backserver { 
ip_hash; 
server 192.168.0.14:88; 
server 192.168.0.15:80; 

C 配置代码

upstream backserver {

     server 127.0.0.1:8080;

     server 127.0.0.1:8081;

    }

 

    server {

        listen       80;

        server_name  www.itmayiedu.com;

        location / {

    proxy_pass  http://backserver;

index  index.html index.htm;

        }

    }

 

D 宕机轮训配置规则

 

    server {

        listen       80;

        server_name  www.itmayiedu.com;

        location / {

    proxy_pass  http://backserver;

index  index.html index.htm;

proxy_connect_timeout 1;

            proxy_send_timeout 1;

            proxy_read_timeout 1;

        }

}

 

7.负载均衡服务器有哪些?

LVS、Ngnix、Tengine(taobao 开发的 Nginx 升级版)、HAProxy(高可用、负载均衡)、Keepalived(故障转移,备机,linux 环境下的组件)

 

 

8.nginx解决网站跨域问题

配置:

server {

        listen       80;

        server_name  www.itmayiedu.com;

        location /A {

    proxy_pass  http://a.a.com:81/A;

index  index.html index.htm;

        }

location /B {

    proxy_pass  http://b.b.com:81/B;

index  index.html index.htm;

        }

    }

 

9.nginx配置防盗链

location ~ .*\.(jpg|jpeg|JPG|png|gif|icon)$ {

        valid_referers blocked http://www.itmayiedu.com www.itmayiedu.com;

        if ($invalid_referer) {

            return 403;

        }

}

 

 

10.nginx配置DDOS

A 限制请求速度

设置NginxNginx Plus的链接请求在一个真实用户请求的合理范围内。好比,若是你以为一个正经常使用户每两秒能够请求一次登陆页面,你就能够设置Nginx每两秒钟接收一个客户端IP的请求(大约等同于每分钟30个请求)。

limit_req_zone $binary_remote_addr zone=one:10m rate=30r/m;

server {

...

location /login.html {

limit_req zone=one;

...

}

}

 

 

`limit_req_zone`命令设置了一个叫one的共享内存区来存储请求状态的特定键值,在上面的例子中是客户端IP($binary_remote_addr)location块中的`limit_req`经过引用one共享内存区来实现限制访问/login.html的目的。

B 限制请求速度

 

设置NginxNginx Plus的链接数在一个真实用户请求的合理范围内。好比,你能够设置每一个客户端IP链接/store不能够超过10个。

11.什么Keepalived

Keepalived是一个免费开源的,用C编写的相似于layer3, 4 & 7交换机制软件,具有咱们平时说的第3层、第4层和第7层交换机的功能。主要提供loadbalancing(负载均衡)和 high-availability(高可用)功能,负载均衡实现须要依赖Linux的虚拟服务内核模块(ipvs),而高可用是经过VRRP协议实现多台机器之间的故障转移服务。 
 
上图是Keepalived的功能体系结构,大体分两层:用户空间(user space)和内核空间(kernel space)。 
内核空间:主要包括IPVSIP虚拟服务器,用于实现网络服务的负载均衡)和NETLINK(提供高级路由及其余相关的网络功能)两个部份。 
用户空间

  • WatchDog:负载监控checkersVRRP进程的情况
  • VRRP Stack:负载负载均衡器之间的失败切换FailOver,若是只用一个负载均稀器,则VRRP不是必须的。
  • Checkers:负责真实服务器的健康检查healthchecking,是keepalived最主要的功能。换言之,能够没有VRRP Stack,但健康检查healthchecking是必定要有的。
  • IPVS wrapper:用户发送设定的规则到内核ipvs代码
  • Netlink Reflector:用来设定vrrpvip地址等。

Keepalived的全部功能是配置keepalived.conf文件来实现的。

 

12.集群状况下Session共享解决方案

A nginx或者haproxy作的负载均衡)

Nginx 作的负载均衡能够添加ip_hash这个配置,

haproxy作的负载均衡能够用 balance source这个配置。

从而使同一个ip的请求发到同一台服务器。

B 利用数据库同步session

C 利用cookie同步session数据原理图以下

 

 

缺点:安全性差、http请求须要带参数增长了带宽消耗

D 使用Session集群存放Redis

使用spring-session框架,底层实现原理是重写httpsession

引入maven依赖

<!--spring boot redis应用基本环境配置 -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-redis</artifactId>

        </dependency>

        <!--spring session redis应用基本环境配置,须要开启redis后才能够使用,否则启动Spring boot会报错 -->

        <dependency>

            <groupId>org.springframework.session</groupId>

            <artifactId>spring-session-data-redis</artifactId>

        </dependency>

 

建立SessionConfig

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;

 

//这个类用配置redis服务器的链接

//maxInactiveIntervalInSecondsSpringSession的过时时间(单位:秒)

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)

public class SessionConfig {

 

// 冒号后的值为没有配置文件时,制动装载的默认值

@Value("${redis.hostname:localhost}")

String HostName;

@Value("${redis.port:6379}")

int Port;

 

@Bean

public JedisConnectionFactory connectionFactory() {

JedisConnectionFactory connection = new JedisConnectionFactory();

connection.setPort(Port);

connection.setHostName(HostName);

return connection;

}

}

 

 

初始化Session

//初始化Session配置

public class SessionInitializer extends AbstractHttpSessionApplicationInitializer{

    public SessionInitializer() {

        super(SessionConfig.class);

    }

}

 

 

控制器层代码

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

 

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.SpringApplication;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 

@RestController

public class SessionController {

 

@Value("${server.port}")

private String PORT;

 

public static void main(String[] args) {

SpringApplication.run(SessionController.class, args);

}

 

@RequestMapping("/index")

public String index() {

return "index:" + PORT;

}

 

/**

 *

 * @methodDesc: 功能描述:(session存放值)

 * @author: 余胜军

 * @param: @param

 *             httpSession

 * @param: @param

 *             sessionKey

 * @param: @param

 *             sessionValue

 * @param: @return

 * @createTime:2017108日 下午3:55:26

 * @returnType:@param httpSession

 * @returnType:@param sessionKey

 * @returnType:@param sessionValue

 * @returnType:@return String

 * @copyright:上海每特教育科技有限公司

 * @QQ:644064779

 */

@RequestMapping("/setSession")

public String setSession(HttpServletRequest request, String sessionKey, String sessionValue) {

HttpSession session = request.getSession(true);

session.setAttribute(sessionKey, sessionValue);

return "success,port:" + PORT;

}

 

/**

 *

 * @methodDesc: 功能描述:(Session获取值)

 * @author: 余胜军

 * @param: @param

 *             httpSession

 * @param: @param

 *             sessionKey

 * @param: @return

 * @createTime:2017108日 下午3:55:47

 * @returnType:@param httpSession

 * @returnType:@param sessionKey

 * @returnType:@return String

 * @copyright:上海每特教育科技有限公司

 * @QQ:644064779

 */

@RequestMapping("/getSession")

public String getSession(HttpServletRequest request, String sessionKey) {

HttpSession session =null;

try {

 session = request.getSession(false);

} catch (Exception e) {

  e.printStackTrace();

}

String value=null;

if(session!=null){

value = (String) session.getAttribute(sessionKey);

}

return "sessionValue:" + value + ",port:" + PORT;

}

 

}

 

13.高并发解决方案

业务数据库  - 数据水平分割(分区分表分库)、读写分离SQL优化

业务应用 - 逻辑代码优化(算法优化)、公共数据缓存

应用服务器 - 反向静态代理、配置优化、负载均衡(apache分发,多tomcat实例)

系统环境 - JVM调优

页面优化 - 减小页面链接数、页面尺寸瘦身

1、动态资源和静态资源分离;

2CDN

3、负载均衡;

4、分布式缓存;

5、数据库读写分离或数据切分(垂直或水平);

6、服务分布式部署。

 

 

 

7消息中间件

1.消息中间件产生的背景

在客户端与服务器进行通信.客户端调用后,必须等待服务对象完成处理返回结果才能继续执行。

 客户与服务器对象的生命周期紧密耦合,客户进程和服务对象进程都都必须正常运行;若是因为服务对象崩溃或者网络故障致使用户的请求不可达,客户受到异常

点对点通讯: 客户的一次调用只发送给某个单独的目标对象。

(画图演示)

2.什么消息中间件 


面向消息的中间件(MessageOrlented MiddlewareMOM)较好的解决了以上问
题。发送者将消息发送给消息服务器,消息服务器将消感存放在若千队列中,在合适
的时候再将消息转发给接收者。

这种模式下,发送和接收是异步的,发送者无需等
两者的生命周期未必相同: 发送消息的时候接收者不必定运行,接收消息的时候
发送者也不必定运行;一对多通讯: 对于一个消息能够有多个接收者。

3.JMS介绍

A 什么JMS

JMS是java的消息服务,JMS的客户端之间能够经过JMS服务进行异步的消息传输。

B 什么消息模型

○ Point-to-Point(P2P) --- 点对点

○ Publish/Subscribe(Pub/Sub)---  发布订阅

即点对点和发布订阅模型

 

P2P (对点)

P2P

  1. P2P模式图 
  2. 涉及到的概念 
    1. 消息队列(Queue)
    2. 发送者(Sender)
    3. 接收者(Receiver)
    4. 每一个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
  3. P2P的特色
    1. 每一个消息只有一个消费者(Consumer)(即一旦被消费,消息就再也不在消息队列中)
    2. 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息以后,无论接收者有没有正在运行,它不会影响到消息被发送到队列
    3. 接收者在成功接收消息以后需向队列应答成功

若是你但愿发送的每一个消息都应该被成功处理的话,那么你须要P2P模式。

应用场景

A用户与B用户发送消息

 

 

Pub/Sub (发布与订阅)

Pub/Sub模式图 

涉及到的概念 

主题(Topic)

发布者(Publisher)

订阅者(Subscriber) 
客户端将消息发送到主题。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特色

每一个消息能够有多个消费者

发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须建立一个订阅者以后,才能消费发布者的消息,并且为了消费消息,订阅者必须保持运行的状态。

为了缓和这样严格的时间相关性,JMS容许订阅者建立一个可持久化的订阅。这样,即便订阅者没有被激活(运行),它也能接收到发布者的消息。

若是你但愿发送的消息能够不被作任何处理、或者被一个消息者处理、或者能够被多个消费者处理的话,那么能够采用Pub/Sub模型

消息的消费 
JMS中,消息的产生和消息是异步的。对于消费来讲,JMS的消息者能够经过两种方式来消费消息。 
○ 同步 
订阅者或接收者调用receive方法来接收消息,receive方法在可以接收到消息以前(或超时以前)将一直阻塞 
○ 异步 
订阅者或接收者能够注册为一个消息监听器。当消息到达以后,系统自动调用监听器的onMessage方法。

  应用场景:

   用户注册、订单修改库存、日志存储

   画图演示

 

 

 

4.MQ产品的分类

 

A RabbitMQ

是使用Erlang编写的一个开源的消息队列,自己支持不少的协议:AMQPXMPP, SMTP, STOMP,也正是如此,使的它变的很是重量级,更适合于企业级的开发。同时实现了一个经纪人(Broker)构架,这意味着消息在发送给客户端时先在中心队列排队。对路由(Routing),负载均衡(Load balance)或者数据持久化都有很好的支持。

B Redis

是一个Key-ValueNoSQL数据库,开发维护很活跃,虽然它是一个Key-Value数据库存储系统,但它自己支持MQ功能,因此彻底能够当作一个轻量级的队列服务来使用。对于RabbitMQRedis的入队和出队操做,各执行100万次,每10万次记录一次执行时间。测试数据分为128Bytes512Bytes1K10K四个不一样大小的数据。实验代表:入队时,当数据比较小时Redis的性能要高于RabbitMQ,而若是数据大小超过了10KRedis则慢的没法忍受;出队时,不管数据大小,Redis都表现出很是好的性能,而RabbitMQ的出队性能则远低于Redis

 

入队

出队

 

128B

512B

1K

10K

128B

512B

1K

10K

Redis

16088

15961

17094

25

15955

20449

18098

9355

RabbitMQ

10627

9916

9370

2366

3219

3174

2982

1588

C ZeroMQ

号称最快的消息队列系统,尤为针对大吞吐量的需求场景。ZMQ可以实现RabbitMQ不擅长的高级/复杂的队列,可是开发人员须要本身组合多种技术框架,技术上的复杂度是对这MQ可以应用成功的挑战。ZeroMQ具备一个独特的非中间件的模式,你不须要安装和运行一个消息服务器或中间件,由于你的应用程序将扮演了这个服务角色。你只须要简单的引用ZeroMQ程序库,能够使用NuGet安装,而后你就能够愉快的在应用程序之间发送消息了。可是ZeroMQ仅提供非持久性的队列,也就是说若是down机,数据将会丢失。其中,TwitterStorm中使用ZeroMQ做为数据流的传输。

D ActiveMQ

Apache下的一个子项目。 相似于ZeroMQ,它可以以代理人和点对点的技术实现队列。同时相似于RabbitMQ,它少许代码就能够高效地实现高级应用场景。RabbitMQZeroMQActiveMQ均支持经常使用的多种语言客户端 C++Java.Net,PythonPhpRuby等。

E Jafka/Kafka

KafkaApache下的一个子项目,是一个高性能跨语言分布式Publish/Subscribe消息队列系统,而Jafka是在Kafka之上孵化而来的,即Kafka的一个升级版。具备如下特性:快速持久化,能够在O(1)的系统开销下进行消息持久化;高吞吐,在一台普通的服务器上既能够达到10W/s的吞吐速率;彻底的分布式系统,BrokerProducerConsumer都原生自动支持分布式,自动实现复杂均衡;支持Hadoop数据并行加载,对于像Hadoop的同样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka经过Hadoop的并行加载机制来统一了在线和离线的消息处理,这一点也是本课题所研究系统所看重的。Apache Kafka相对于ActiveMQ是一个很是轻量级的消息系统,除了性能很是好以外,仍是一个工做良好的分布式系统。

其余一些队列列表HornetQApache QpidSparrowStarlingKestrelBeanstalkdAmazon SQS就再也不一一分析。

5.MQ怎么保证消息幂等问题

 

  1. 发送端MQ-client 将消息发送给服务端MQ-server
  2. 服务端MQ-server将消息落地
  3. 服务端MQ-server 回ACK(表示确认) 2.若是3丢失 发送端在超时后,又会发送一遍,此时重发是MQ-client发起的,消息处理的是MQ-server 为了不2 重复落地,对每条MQ消息系统内部须要生成一个inner-msg-id,做为去重和幂等的依据,这个内部消息ID 的特色是

在分布式环境中,MQ通信产生网络延迟,重试补偿中,会形成MQ重复消费。

解决办法:

① 使用日志+msg-id保存报文信息,做为去重和幂等的依据。

② 消费端代码抛出异常,不须要重试补偿,使用日志记录报文,下次发版本解决。

6.MQ哪些协议

Stomp、XMPP

Stomp协议,英文全名Streaming Text Orientated Message Protocol,中文名称为 ‘流文本定向消息协议’。是一种以纯文本为载体的协议(以文本为载体的意思是它的消息格式规范中没有相似XMPP协议那样的xml格式要求,你能够将它看做‘半结构化数据’)。目前Stomp协议有两个版本:V1.1和V1.2。

一个标准的Stomp协议包括如下部分:命令/信息关键字、头信息、文本内容。以下图所示:

 

如下为一段简单的协议信息示例:

CONNECT

accept-version:1.2

someparam1:value1

someparam2:value2

上面的示例中,咱们使用了Stomp协议的CONNECT命令,它的意思为链接到Stomp代理端,而且携带了要求代理端的版本信息和两个自定义的K-V信息(请注意’^@’符号,STOMP协议中用它来表示NULL)。

XMPP基于XML,用于IM系统的开发。国内比较流行的XMPP服务器叫作Openfire,它使用MINA做为下层的网络IO框架(不是MINA2是MINA1);国外用的比较多的XMPP服务器叫作Tigase,它的官网号称单节点能够支撑50万用户在线,集群能够支持100万用户在线:(http://projects.tigase.org/

 

3、微服务与分布式

1什么RPC远程调用?

RPC 的全称是 Remote Procedure Call 是一种进程间通讯方式。
它容许程序调用另外一个地址空间(一般是共享网络的另外一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即不管是调用本地接口/服务的仍是远程的接口/服务,本质上编写的调用代码基本相同。
好比两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,因为不在一个内存空间,不能直接调用,这时候须要经过就能够应用RPC框架的实现来解决

2什么SOASOAP区别是什么?

SOA是一种面向服务架构,是将相同业务逻辑抽取出来组成单独服务。

SOAPWebService面向服务协议, 采用xml,由于比较中,如今不是特别流行。

3什么微服务架构

微服务架构师一种架构模式,它提倡将单一应用程序划分红一组小的服务,服务之间互相

协调、互相配合没用户提供最终价值。每一个服务运行在其独立的进程中,服务与服务间采用轻量级的同窗机制互相沟通(通畅采用Http+restful API),每一个服务都围绕着具体业务进行构建,而且可以被独立的部署到生成环境、类生存环境等。另外,应尽可能避免同一的、集中式服务管理机制。

4微服务SOA区别

SOA实现

微服务架构实现

企业级,自顶向下开展实施

团队级,自定向上开展实施

服务由多个子系统组成

一个系统被拆分红多个服务

集成式服务(esb、wssoap)

集成方式简单(http、restjson)

 

5RPC远程调用有哪些框架?

SpringCloudDubboDubboxHessianHttpClient、thrift等。

 

6什么是SpringCloud

SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现(Eureka)、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,能够在开发人员的电脑上跑。

另外说明spring cloud是基于Springboot的,因此须要开发中对Springboot有必定的了解SpringCloud使用Eureka做为注册中心,使用rest+ribbon或者feign,断路器Hystrix、zuul接口网

服务提供者与消费关系 服务提供者:提供服务被人调用 消费者:调用被人服务

服务的注册与发现(Eureka ) 咱们须要用的的组件上Spring Cloud NetflixEureka ,eureka是一个服务注册和发现模块

服务注册

建立eurekaserver 项目

引入maven依赖spring-cloud-starter-eureka-server

配置application.yml

eureka:

  instance:

    hostname: localhost

  client:

    registerWithEureka: false

    fetchRegistry: false

    serviceUrl:

      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

启动EurekaServer

服务提供者

服务消费者(rest+ribbon-负载均衡)

在微服务架构中,业务都会被拆分红一个独立的服务,服务与服务的通信是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另外一种是feign

ribbon:ribbon是一个负载均衡客户端,能够很好的控制httptcp的一些行为。Feign默认集成了ribbon

spring-cloud-starter-ribbon

架构

一个服务注册中心,eureka server,端口为8761

service-hi工程跑了两个实例,端口分别为8762,8763,分别向服务注册中心注册

sercvice-ribbon端口为8764,向服务注册中心注册

sercvice-ribbon经过restTemplate调用service-hihi接口时,由于用ribbon进行了负载均衡,会轮流的调用service-hi87628763 两个端口的hi接口;

@LoadBalanced注解代表这个restRemplate开启负载均衡的功能

服务消费者(Feign-负载均衡)

Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只须要建立一个接口并注解。 它具备可插拔的注解特性,可以使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。

Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。

Feign 采用的是基于接口的注解:Feign 整合了ribbon

spring-cloud-starter-feign

定义接口 @FeignClient(value = "service-hi")

Hystrix断路器spring-cloud-starter-hystrix --- @HystrixCommand(fallbackMethod = "hiError") 改造HelloService类,在hiService方法上加上 @HystrixCommand注解。该注解对该方法建立了熔断器的功能,并指定了fallbackMethod熔断方法,熔断方法直接返回了一个字符串,字符串为”hi,+name+,sorry,error!

Feign是自带断路器,配置文件中feign.hystrix.enabled=true,接口上 @FeignClient(value = "service-hi",fallback=SchedualServiceHiHystric.class)

Hystrix Dashboard (断路器:Hystrix 仪表盘)

zuulnginx

使用Zuul构建API Gateway

Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先通过负载均衡(zuulNgnix),再到达服务网关(zuul集群),而后再到具体的服务。

ZuulNetflix的基于JVM的路由器和服务器端负载均衡器--Zuul的规则引擎容许规则和过滤器基本上用任何JVM语言编写,内置支持JavaGroovy

spring-cloud-starter-zuul

在其入口applicaton类加上注解 @EnableZuulProxy,开启zuul的功能

application.yml配置

zuul:

routes:

api-a:

     path: /api-a/**

     service-id: service-ribbon

api-b:

     path: /api-b/**

     service-id: service-feign

zuul不只只是路由,而且还能过滤extends ZuulFilter(过滤器)

    @Override

    public String filterType() {

return "pre";

    }

重写此方法,filterType,返回一个字符串表明过滤器的类型,在zuul中定义了四种不一样生命周期的过滤器类型preroutingposterror

filterOrder:过滤的顺序。oshouldFilter:这里能够写逻辑判断,是否要过滤,本文true,永远过滤。

run:过滤器的具体逻辑。可用很复杂,包括查sqlnosql去判断该请求到底有没有权限访问。

spring cloud config

Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client

mavenspring-cloud-config-server

构建config-server

Application类加上 @EnableConfigServer

application.properties文件配置如下

spring.application.name=config-server

server.port=8888

spring.cloud.config.server.git.uri=https://gitee.com/itmayi/itmayiedu2.git

spring.cloud.config.server.git.searchPaths=/itmayi/itmayiedu2.git

spring.cloud.config.label=master

spring.cloud.config.server.git.username=

spring.cloud.config.server.git.password=

构建config-client

mavenspring-cloud-starter-config

配置文件bootstrap.properties

spring.application.name=config-client

spring.cloud.config.label=master

spring.cloud.config.profile=dev

spring.cloud.config.uri= http://localhost:8888/

server.port=8881

高可用的分布式配置中心(Spring Cloud Config) 配置中心如何从远程git读取配置文件,当服务实例不少时,都从配置中心读取文件,这时能够考虑将配置中心作成一个微服务,将其集群化,从而达到高可用

 

7Dubbo

1)、什么是Dubbo

Duubbo是一个RPC远程调用框架, 分布式服务治理框架

什么是Dubbo服务治理?

服务与服务之间会有不少个Url、依赖关系、负载均衡、容错、自动注册服务。

2)、Dubbo有哪些协议?

默认用的dubbo协议、HttpRMIHessian

3)、Dubbo整个架构流程

分为四大模块

生产者、消费者、注册中心、监控中心

生产者:提供服务

消费者: 调用服务

注册中心:注册信息(rediszk)

监控中心:调用次数、关系依赖等。

首先生产者将服务注册到注册中心(zk),使用zk持久节点进行存储,消费订阅zk节点,一旦有节点变动,zk经过事件通知传递给消费者,消费能够调用生产者服务。

服务与服务之间进行调用,都会在监控中心中,存储一个记录。

 

4)、DubboxDubbo区别

Dubox使用http协议+rest风格传入json或者xml格式进行远程调用。Dubbo使用Dubbo协议。

 

Dubbo-admin管理平台搭建

 

Dubbo集群、负载均衡、容错:Dubbo-admin管理平台在其Dubbo-admin管理平台上操做便可

5)、核心部分

1.透明化的远程方法调用,就像调用本地方法同样调用远程方法

2.软负载均衡及容错机制,可在内网替代F5等硬件负鞭均衡器,下降成本,减小单点。

3.服务自动注册与发现,再也不须要写死服务提供方地址,注册中心基于楼口名查询服务提供者的IP地址,而且可以平滑添加或删除服务提供者

4.Dubbo采用全Spring 配置方式,功明化接入应用,对应用没有任何API侵只需用Spring加载Dubbo的配置便可,

Dubbo基FSpring的Schema扩入,展进行加载。

8SpringCloudDubbo区别

1)、相同:

dubbospringcloud均可以实现RPC远程调用

dubbospringcloud均可以使用分布式、微服务场景下。

2)、区别:

dubbo比较强的背景,国内有必定影响力。

dubbo使用zkredis做为做为注册中心

springcloud使用eureka做为注册中心

dubbo支持多种协议默认使用dubbo协议。

Springcloud只能支持http协议。

Springcloud一套完整的微服务解决方案。

Dubbo目前已经中止更新,SpringCloud更新速度快。

 

9Zookeeper

1)、什么Zookeeper

Zookeeper是一个工具,能够实现集群中的分布式协调服务。

所谓的分布式协调服务,就是在集群的节点中进行可靠的消息传递,来协调集群的工做。

Zookeeper之因此可以实现分布式协调服务,靠的就是它可以保证分布式数据一致性。

所谓的分布式数据一致性,指的就是能够在集群中保证数据传递的一致性。

 Zookeeper可以提供的分布式协调服务包括:数据发布订阅、负载均衡、命名服务、分布式协调/通知、集群管理、分布式锁、分布式队列等功能

2)、Zookeeper特色

Zookeeper工做在集群中,对集群提供分布式协调服务,它提供的分布式协调服务具备以下的特色:

顺序一致性

从同一个客户端发起的事务请求,最终将会严格按照其发起顺序被应用到zookeeper中

原子性

全部事物请求的处理结果在整个集群中全部机器上的应用状况是一致的,即,要么整个集群中全部机器都成功应用了某一事务,要么都没有应用,必定不会出现集群中部分机器应用了改事务,另一部分没有应用的状况。

单一视图

不管客户端链接的是哪一个zookeeper服务器,其看到的服务端数据模型都是一致的。

可靠性

一旦服务端成功的应用了一个事务,并完成对客户端的响应,那么该事务所引发的服务端状态变动将会一直保留下来,除非有另外一个事务又对其进行了改变。

实时性

zookeeper并非一种强一致性,只能保证顺序一致性和最终一致性,只能称为达到了伪实时性。

3)、zookeeper的数据模型

zookeeper中能够保存数据,正是利用zookeeper能够保存数据这一特色,咱们的集群经过在zookeeper里存取数据来进行消息的传递。

zookeeper中保存数据的结构很是相似于文件系统。都是由节点组成的树形结构。不一样的是文件系统是由文件夹和文件来组成的树,而zookeeper中是由ZNODE来组成的树。

每个ZNODE里均可以存放一段数据,ZNODE下还能够挂载零个或多个子ZNODE节点,从而组成一个树形结构。

 

4)、Zookeeper应用场景

数据发布订阅

负载均衡

命名服务

分布式协调

集群管理

配置管理

分布式队列

分布式

5)、Zookeeper特性

1Zookeeper:一个leader,多个follower组成的集群

2、全局数据一致:每一个server保存一份相同的数据副本,client不管链接到哪一个server,数据都是一致的

3、分布式读写,更新请求转发,由leader实施

4、更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行

5、数据更新原子性,一次数据更新要么成功,要么失败

6、实时性,在必定时间范围内,client能读到最新数据

Zookeeper数据结构

层次化的目录结构

每一个节点在zookeeper中叫作znode,而且其有一个惟一的路径标识

节点Znode能够包含数据和子节点(可是EPHEMERAL类型的节点不能有子节Znode有两种类型:短暂(ephemeral)、EPHEMERAL_SEQUENTIAL、持久(persistent)、PERSISTENT_SEQUENTIAL

在分布式系统中,顺序号能够被用于为全部的事件进行全局排序,这样客户端能够经过顺序号推断事件的顺序 myid (内容为服务器标识 : 0)

zkServer.sh start stop status……

Zookeeper客户端

Java操做Zookeeper

依赖:org.apache.zookeeper

建立节点的类型: CreateMode,提供四种首点象型

PERSISTENT(持久节点)

PERSISTENT SEQUENTIAL(持久顺序节点)

EPHEMERAL(临时节点)

EPHEMERAL SEQUENTAL(临时顺序节点)

Zookeeper客户端链接

Watcher

ZooKeeper中,接口类Watcher用于表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperStateEventType两个枚举类,分别表明了通知状态和事件类型,同时定义了事件的回调方法:processWatchedEvent event

Zookeeper实战分布式锁

线程进程资源竞争

线程进程同步的方式和机制

临界区

经过对多线程的串行化来访问公共资源或一段代码

synchronized 修饰的java方法 仅用于线程同步

互斥量

采用互斥对象机制。只有拥有互斥对象的线程才有访问公共资源竞争的问题源的权限synchronized 修饰的代码块java.util.concurrent.locks.Lock分布式锁的主要实现机制

分布式锁实现的技术

基于数据实现分布式锁( 性能较差,容易出现单点故障)

基于缓存实现分布式锁( 锁没有失效事件,容易死锁)

基于Zookeeper实现分布式锁(建立临时节点)

10什么是分布式锁

简单的理解就是:分布式锁是一个在不少环境中很是有用的原语,它是不一样的系统或是同一个系统的不一样主机之间互斥操做共享资源的有效方法。

1)、Zookeeper实现分布式锁

分布式锁使用zk,在zk上建立一个临时节点,使用临时节点做为锁,由于节点不容许重复。

若是能建立节点成功,生成订单号,若是建立节点失败,就等待。临时节点zk关闭,释放锁,其余节点就能够从新生成订单号。

2)、Redis分布式锁思考

通常的锁只能针对单机下同一进程的多个线程,或单机的多个进程。多机状况下,对同一个资源访问,须要对每台机器的访问进程或线程加锁,这即是分布式锁。分布式锁能够利用多机的共享缓存(例如redis)实现。redis的命令文档[1],实现及分析参考文档[2]

利用redisgetsetnxgetsetdel四个命令能够实现基于redis的分布式锁:

get key:表示从redis中读取一个keyvalue,若是key没有对应的value,返回nil,若是存储的值不是字符串类型,返回错误

setnx key value:表示往redis中存储一个键值对,但只有当key不存在时才成功,返回1;不然失败,返回0,不改变keyvalue

getset key:将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当旧值不存在时返回nil,当旧值不为字符串类型,返回错误

del key:表示删除key,当key不存在时不作操做,返回删除的key数量

关于加锁思考,循环中: 
0setnxvalue是当前机器时间+预估运算时间做为锁的失效时间。这是为了防止得到锁的线程挂掉而没法释放锁而致使死锁。 
0.1、返回1,证实已经得到锁,返回啦 
0.2、返回0,得到锁失败,须要检查锁超时时间 
1get 获取到锁,利用失效时间判断锁是否失效。 
1.1、取锁超时时间的时刻可能锁被删除释放,此时并无拿到锁,应该从新循环加锁逻辑。 
2、取锁超时时间成功 
2.1、锁没有超时,休眠一下,从新循环加锁 
2.2、锁超时,但此时不能直接释放锁删除。由于此时可能多个线程都读到该锁超时,若是直接删除锁,全部线程均可能删除上一个删除锁又新上的锁,会有多个线程进入临界区,产生竞争状态。 
3、此时采用乐观锁的思想,用getset再次获取锁的旧超时时间。 
3.1、若是此时得到锁旧超时时间成功 
3.1.1、等于上一次得到的锁超时时间,证实两次操做过程当中没有别人动过这个锁,此时已经得到锁 
3.1.2、不等于上一次得到的锁超时时间,说明有人先动过锁,获取锁失败。虽然修改了别人的过时时间,但由于冲突的线程相差时间极短,因此修改后的过时时间并没有大碍。此处依赖全部机器的时间一致。 
3.2、若是此时得到锁旧超时时间失败,证实当前线程是第一个在锁失效后又加上锁的线程,因此也得到锁 
4、其余状况都没有得到锁,循环setnx

关于解锁的思考: 
在锁的时候,若是锁住了,回传超时时间,做为解锁时候的凭证,解锁时传入锁的键值和凭证。我思考的解锁时候有两种写法: 
1、解锁前get一下键值的value,判断是否是和本身的凭证同样。但这样存在一些问题:

get时返回nil的可能,此时表示有别的线程拿到锁并用完释放

get返回非nil,可是不等于自身凭证。因为有getset那一步,当两个竞争线程都在这个过程当中时,存在持有锁的线程凭证不等于value,而是value是稍慢那一步线程设置的value

2、解锁前用凭证判断锁是否已经超时,若是没有超时,直接删除;若是超时,等着锁自动过时就好,省得误删别人的锁。但这种写法一样存在问题,因为线程调度的不肯定性,判断到删除之间可能过去好久,并非绝对意义上的正确解锁。

 

public class RedisLock {

 

    private static final Logger logger = LoggerFactory.getLogger(RedisLock.class);

 

    //显然jedis还须要本身配置来初始化

    private Jedis jedis = new Jedis();

 

    //默认锁住15秒,尽力规避锁时间过短致使的错误释放

    private static final long DEFAULT_LOCK_TIME = 15 * 1000;

 

    //尝试锁住一个lock,设置尝试锁住的次数和超时时间(毫秒),默认最短15

    //成功时返回这把锁的key,解锁时须要凭借锁的lockkey

    //失败时返回空字符串

    public String lock(String lock, int retryCount, long timeout) {

        Preconditions.checkArgument(retryCount > 0 && timeout > 0, "retry count <= 0 or timeout <= 0 !");

        Preconditions.checkArgument(retryCount < Integer.MAX_VALUE && timeout < Long.MAX_VALUE - DEFAULT_LOCK_TIME,

                "retry count is too big or timeout is too big!");

        String $lock = Preconditions.checkNotNull(lock) + "_redis_lock";

        long $timeout = timeout + DEFAULT_LOCK_TIME;

        String ret = null;

        //重试必定次数,仍是拿不到,就放弃

        try {

            long i, status;

            for (i = 0, status = 0; status == 0 && i < retryCount; ++i) {

                //尝试加锁,并设置超时时间为当前机器时间+超时时间

                if ((status = jedis.setnx($lock, ret = Long.toString(System.currentTimeMillis() + $timeout))) == 0) {

                    //获取锁失败,查看锁是否超时

                    String time = jedis.get($lock);

                    //在加锁和检查之间,锁被删除了,尝试从新加锁

                    if (time == null) {

                        continue;

                    }

                    //锁的超时时间戳小于当前时间,证实锁已经超时

                    if (Long.parseLong(time) < System.currentTimeMillis()) {

                        String oldTime = jedis.getSet($lock, Long.toString(System.currentTimeMillis() + $timeout));

                        if (oldTime == null || oldTime.equals(time)) {

                            //拿到锁了,跳出循环

                            break;

                        }

                    }

                    try {

                        TimeUnit.MILLISECONDS.sleep(1L);

                    } catch (InterruptedException e) {

                        logger.error("lock key:{} sleep failed!", lock);

                    }

                }

            }

            if (i == retryCount && status == 0) {

                logger.info("lock key:{} failed!", lock);

                return "";

            }

            //给锁加上过时时间

            jedis.pexpire($lock, $timeout);

            logger.info("lock key:{} succsee!", lock);

            return ret;

        } catch (Exception e) {

            logger.error("redis lock key:{} failed! cached exception: ", lock, e);

            return "";

        }

    }

 

    //释放lock的锁,须要传入lockkey

    //尽力确保删除属于本身的锁,可是不保证作获得

    public void releaseLock(String lock, String key) {

        String $lock = Preconditions.checkNotNull(lock) + "_redis_lock";

        Preconditions.checkNotNull(key);

        try {

            long timeout = Long.parseLong(key);

            //锁尚未超时,锁还属于本身能够直接删除

            //但因为线程运行的不肯定性,其实不能彻底保证删除时锁还属于本身

            //真正执行删除操做时,距离上语句判断可能过了好久

            if (timeout <= System.currentTimeMillis()) {

                jedis.del($lock);

                logger.info("release lock:{} with key:{} success!", lock, key);

            } else {

                logger.info("lock:{} with key:{} timeout! wait to expire", lock, key);

            }

        } catch (Exception e) {

            logger.error("redis release {}  with key:{} failed! cached exception: ", lock, key, e);

        }

    }

}

 

3)、Zookeeper Redis实现分布式锁的区别

1.基于缓存实现分布式锁

 没有失效事件,容易死锁

 阻塞式

2.基于Zookeeper实现分布式锁

 实现相对简单

 可靠性高

 性能较好

 

11Java实现定时任务有哪些方式

Thread 

public class Demo01 {

static long count = 0;

public static void main(String[] args) {

Runnable runnable = new Runnable() {

@Override

public void run() {

while (true) {

try {

Thread.sleep(1000);

count++;

System.out.println(count);

} catch (Exception e) {

// TODO: handle exception

}

}

}

};

Thread thread = new Thread(runnable);

thread.start();

}

}

 

TimerTask

/**

 * 使用TimerTask类实现定时任务

*/

public class Demo02 {

static long count = 0;

 

public static void main(String[] args) {

TimerTask timerTask = new TimerTask() {

 

@Override

public void run() {

count++;

System.out.println(count);

}

};

Timer timer = new Timer();

// 天数

long delay = 0;

// 秒数

long period = 1000;

timer.scheduleAtFixedRate(timerTask, delay, period);

}

 

}

 

 

ScheduledExecutorService

使用ScheduledExecutorService是从Java

JavaSE5的java.util.concurrent里,作为并发工具类被引进的,这是最理想的定时任务实现方式。

public class Demo003 {

public static void main(String[] args) {

Runnable runnable = new Runnable() {

public void run() {

// task to run goes here

System.out.println("Hello !!");

}

};

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();

// 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间

service.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.SECONDS);

}

}

 

Quartz

建立一个quartz_demo项目

引入maven依赖

<dependencies>

<!-- quartz -->

<dependency>

<groupId>org.quartz-scheduler</groupId>

<artifactId>quartz</artifactId>

<version>2.2.1</version>

</dependency>

<dependency>

<groupId>org.quartz-scheduler</groupId>

<artifactId>quartz-jobs</artifactId>

<version>2.2.1</version>

</dependency>

</dependencies>

 

任务调度类

public class MyJob implements Job {

 

public void execute(JobExecutionContext context) throws JobExecutionException {

System.out.println("quartz MyJob date:" + new Date().getTime());

}

 

}

 

启动

  //1.建立Scheduler的工厂

      SchedulerFactory sf = new StdSchedulerFactory();

      //2.从工厂中获取调度器实例

      Scheduler scheduler = sf.getScheduler();

 

 

      //3.建立JobDetail

      JobDetail jb = JobBuilder.newJob(MyJob.class)

              .withDescription("this is a ram job") //job的描述

              .withIdentity("ramJob", "ramGroup") //job namegroup

              .build();

 

      //任务运行的时间,SimpleSchedle类型触发器有效

      long time=  System.currentTimeMillis() + 3*1000L; //3秒后启动任务

      Date statTime = new Date(time);

 

      //4.建立Trigger

          //使用SimpleScheduleBuilder或者CronScheduleBuilder

      Trigger t = TriggerBuilder.newTrigger()

                  .withDescription("")

                  .withIdentity("ramTrigger", "ramTriggerGroup")

                  //.withSchedule(SimpleScheduleBuilder.simpleSchedule())

                  .startAt(statTime)  //默认当前时间启动

                  .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) //两秒执行一次

                  .build();

 

      //5.注册任务和定时器

      scheduler.scheduleJob(jb, t);

 

      //6.启动 调度器

      scheduler.start();

 

12分布式状况下定时任务会出现哪些问题?

分布式集群的状况下,怎么保证定时任务不被重复执行

1)、分布式定时任务解决方案

①使用zookeeper实现分布式锁 缺点(须要建立临时节点、和事件通知不易于扩展)

②使用配置文件作一个开关  缺点发布后,须要重启

③数据库惟一约束,缺点效率低

④使用分布式任务调度平台

  XXLJOB

 

13分布式事物解决方案

1)、全局事物

使用全局事物两段提交协议,遵循XA协议规范,使用开源框架jta+automatic 

什么是两段提交协议:在第一阶段,全部参与全局事物的节点都开始准备,告诉事物管理器

它们准备好了,在是第二阶段,事物管理器告诉资源执行ROLLBACK仍是Commit,只要任何一方为ROLLBACK ,则直接回滚。

参考案例:springboot集成automatic+jta

 

2)、本地消息表

这种实现方式的思路,实际上是源于ebay,后来经过支付宝等公司的布道,在业内普遍使用。其基本的设计思想是将远程分布式事务拆分红一系列的本地事务。若是不考虑性能及设计优雅,借助关系型数据库中的表便可实现。

举个经典的跨行转帐的例子来描述。

第一步,伪代码以下,扣款1W,经过本地事务保证了凭证消息插入到消息表中。 

第二步,通知对方银行帐户上加1W了。那问题来了,如何通知到对方呢?

一般采用两种方式:

1. 采用时效性高的MQ,由对方订阅消息并监听,有消息时自动触发事件 
2. 采用定时轮询扫描的方式,去检查消息表的数据。

两种方式其实各有利弊,仅仅依靠MQ,可能会出现通知失败的问题。而过于频繁的定时轮询,效率也不是最佳的(90%是无用功)。因此,咱们通常会把两种方式结合起来使用。

解决了通知的问题,又有新的问题了。万一这消息有重复被消费,往用户账号上多加了钱,那岂不是后果很严重?

仔细思考,其实咱们能够消息消费方,也经过一个“消费状态表”来记录消费状态。在执行“加款”操做以前,检测下该消息(提供标识)是否已经消费过,消费完成后,经过本地事务控制来更新这个“消费状态表”。这样子就避免重复消费的问题。

总结:上诉的方式是一种很是经典的实现,基本避免了分布式事务,实现了“最终一致性”。可是,关系型数据库的吞吐量和性能方面存在瓶颈,频繁的读写消息会给数据库形成压力。因此,在真正的高并发场景下,该方案也会有瓶颈和限制的。

 

3)、MQ(非事物)

一般状况下,在使用非事务消息支持的MQ产品时,咱们很难将业务操做与对MQ的操做放在一个本地事务域中管理。通俗点描述,仍是以上述提到的“跨行转帐”为例,咱们很难保证在扣款完成以后对MQ投递消息的操做就必定能成功。这样一致性彷佛很难保证。 
先从消息生产者这端来分析,请看伪代码:

 

根据上述代码及注释,咱们来分析下可能的状况:

1. 操做数据库成功,向MQ中投递消息也成功,皆大欢喜 
2. 操做数据库失败,不会向MQ中投递消息了 
3. 操做数据库成功,可是向MQ中投递消息时失败,向外抛出了异常,刚刚执行的更新数据库的操做将被回滚

从上面分析的几种状况来看,貌似问题都不大的。那么咱们来分析下消费者端面临的问题:

1. 消息出列后,消费者对应的业务操做要执行成功。若是业务执行失败,消息不能失效或者丢失。须要保证消息与业务操做一致 
2. 尽可能避免消息重复消费。若是重复消费,也不能所以影响业务结果

如何保证消息与业务操做一致,不丢失?

主流的MQ产品都具备持久化消息的功能。若是消费者宕机或者消费失败,均可以执行重试机制的(有些MQ能够自定义重试次数)。

如何避免消息被重复消费形成的问题?

1. 保证消费者调用业务的服务接口的幂等性 
2. 经过消费日志或者相似状态表来记录消费状态,便于判断(建议在业务上自行实现,而不依赖MQ产品提供该特性)

 

总结:这种方式比较常见,性能和吞吐量是优于使用关系型数据库消息表的方案。若是MQ自身和业务都具备高可用性,理论上是能够知足大部分的业务场景的。不过在没有充分测试的状况下,不建议在交易业务中直接使用。

 

4)、MQ(事务消息)

5)、其余补偿方式

作过支付宝交易接口的同窗都知道,咱们通常会在支付宝的回调页面和接口里,解密参数,而后调用系统中更新交易状态相关的服务,将订单更新为付款成功。同时,只有当咱们回调页面中输出了success字样或者标识业务处理成功相应状态码时,支付宝才会中止回调请求。不然,支付宝会每间隔一段时间后,再向客户方发起回调请求,直到输出成功标识为止。 
其实这就是一个很典型的补偿例子,跟一些MQ重试补偿机制很相似。

通常成熟的系统中,对于级别较高的服务和接口,总体的可用性一般都会很高。若是有些业务因为瞬时的网络故障或调用超时等问题,那么这种重试机制实际上是很是有效的。

固然,考虑个比较极端的场景,假如系统自身有bug或者程序逻辑有问题,那么重试1W次那也是无济于事的。那岂不是就发生了“明明已经付款,却显示未付款不发货”相似的悲剧?

其实为了交易系统更可靠,咱们通常会在相似交易这种高级别的服务代码中,加入详细日志记录的,一旦系统内部引起相似致命异常,会有邮件通知。同时,后台会有定时任务扫描和分析此类日志,检查出这种特殊的状况,会尝试经过程序来补偿并邮件通知相关人员。

在某些特殊的状况下,还会有“人工补偿”的,这也是最后一道屏障。

6)、补充资料

1.什么是两段提交协议

 

1、协议概述

 两阶段提交协议two phase commit protocol,2PC)能够保证数据的强一致性,许多分布式关系型数据管理系统采用此协议来完成分布式事务。它是协调全部分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。同时也是解决一致性问题的一致性算法。该算法可以解决不少的临时性系统故障(包括进程、网络节点、通讯等故障),被普遍地使用。可是,它并不可以经过配置来解决全部的故障,在某些状况下它还须要人为的参与才能解决问题。参与者为了可以从故障中恢复,它们都使用日志来记录协议的状态,虽然使用日志下降了性能可是节点可以从故障中恢复。

  在两阶段提交协议中,系统通常包含两类机器(或节点):
  协调者coordinator,一般一个系统中只有一个;
  事务参与者 participants,cohorts或workers,通常包含多个;
  在数据存储系统中能够理解为数据副本的个数,协议中假设:
  每一个节点都会记录写前日志并持久性存储,即便节点发生故障日志也不会丢失;
  节点不会发生永久性故障并且任意两个节点均可以互相通讯;

  当事务的最后一步完成以后,协调器执行协议,参与者根据本地事务,可以成功完成回复赞成提交事务或者回滚事务。

2、执行过程

  顾名思义,两阶段提交协议由两个阶段组成。在正常的执行下,这两个阶段的执行过程以下所述:

1)阶段1:请求阶段(commit-request phase,或称表决阶段,voting phase)

  在请求阶段,协调者将通知事务参与者准备提交或取消事务,而后进入表决过程。在表决过程当中,参与者将告知协调者本身的决策:赞成(事务参与者本地做业执行成功)或取消(本地做业执行故障)。

2)阶段2:提交阶段(commit phase)

  在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当全部的参与者赞成提交事务协调者才通知全部的参与者提交事务,不然协调者将通知全部的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操做。

  注意  两阶段提交协议与两阶段锁协议不一样,两阶段锁协议为一致性控制协议。

3)该协议的执行过程能够经过下图来描述:

 

               (a)成功                                   (b)失败

3、协议的特色

  两阶段提交协议最大的劣势是其经过阻塞完成的协议,在节点等待消息的时候处于阻塞状态,节点中其余进程则须要等待阻塞进程释放资源才能使用。若是协调器发生了故障,那么参与者将没法完成事务则一直等待下去。如下状况可能会致使节点发生永久阻塞:

  (1)若是参与者发送赞成提交消息给协调者,进程将阻塞直至收到协调器的提交或回滚的消息。若是协调器发生永久故障,参与者将一直等待,这里能够采用备份的协调器,全部参与者将回复发给备份协调器,由它承担协调器的功能。

  (2)若是协调器发送“请求提交”消息给参与者,它将被阻塞直到全部参与者回复了,若是某个参与者发生永久故障,那么协调器也不会一直阻塞,由于协调器在某一时间内还未收到某参与者的消息,那么它将通知其余参与者回滚事务。

  同时两阶段提交协议没有容错机制,一个节点发生故障整个事务都要回滚,代价比较大。

4、工做过程

  下面咱们经过一个例子来讲明两阶段提交协议的工做过程:

  A组织B、C和D三我的去爬长城:若是全部人都赞成去爬长城,那么活动将举行;若是有一人不一样意去爬长城,那么活动将取消。用2PC算法解决该问题的过程以下:

  首先A将成为该活动的协调者,B、C和D将成为该活动的参与者。

1)阶段1:

  ①A发邮件给B、C和D,提出下周三去登山,问是否赞成。那么此时A须要等待B、C和D的邮件。

  ②B、C和D分别查看本身的日程安排表。B、C发现本身在当日没有活动安排,则发邮件告诉A它们赞成下周三去爬长城。因为某种缘由, D白天没有查看邮 件。那么此时A、B和C均须要等待。到晚上的时候,D发现了A的邮件,而后查看日程安排,发现周三当天已经有别的安排,那么D回复A说活动取消吧。

2)阶段2:

  ①此时A收到了全部活动参与者的邮件,而且A发现D下周三不能去登山。那么A将发邮件通知B、C和D,下周三爬长城活动取消。

  ②此时B、C回复A“太惋惜了”,D回复A“很差意思”。至此该事务终止。

  经过该例子能够发现,2PC协议存在明显的问题。假如D一直不能回复邮件,那么A、B和C将不得不处于一直等待的状态。而且B和C所持有的资源,即下周三不能安排其它活动,一直不能释放。其它等待该资源释放的活动也将不得不处于等待状态。

  基于此,后来有人提出了三阶段提交协议,在其中引入超时的机制,将阶段1分解为两个阶段:在超时发生之前,系统处于不肯定阶段;在超市发生之后,系统则转入肯定阶段。

  2PC协议包含协调者和参与者,而且两者都有发生问题的可能性。假如协调者发生问题,咱们能够选出另外一个协调者来提交事务。例如,班长组织活动,若是班长生病了,咱们能够请副班长来组织。若是协调者出问题,那么事务将不会取消。例如,班级活动但愿每一个人都能去,假若有一位同窗不能去了,那么直接取消活动便可。或者,若是大多数人去的话那么活动如期举行(2PC变种)。为了可以更好地解决实际的问题,2PC协议存在不少的变种,例如:树形2PC协议 (或称递归2PC协议)、动态2阶段提交协议(D2PC)等。

4、项目问题

1支付项目支付流程

 

 

 

2支回调怎么保证幂等性

产生:第三方支付网关,重试机制形成幂等性

判断支付结果标识 注意回调接口中若是调用耗时代码,使用mq异步推送

3支回调数据安全性

Token对称加密  base64 加签rsa

4回调中,项目宕机。

使用对帐第三方支付交易接口)去进行查询。

对帐第三方交易平台交易结果

5网页受权OAuth2.

 

 

联合登陆步骤:

蚂蚁课堂生成受权链接,跳转到腾讯企业

选择受权QQ用户,受权成功后,就会跳转到原地址

 

 

 

受权链接:

回调地址 :受权成功后,跳转到回调地址

跳转到回调地址:传一些参数

 

 

跳转到回调地址:

传一个受权code有效期 10分钟  受权code使用完毕以后,直接删除,不能重复使用

受权码的做用:使用受权码换取aess_token,使用aess_token换取openid

 

openid做用: 惟一用户主键(受权系统会员主键,不代码腾讯userid)

 

 

 

openid和咱们用户表中存放一个openid进行关联

 

使用openid调用腾讯会员接口查询QQ信息

本地回调

 

6大家登陆流程

 

 

7在开发中遇到了那些难题是怎么解决的?

1)、跨域

跨域缘由产生:在当前域名请求网站中,默认不容许经过ajax请求发送其余域名。

XMLHttpRequest cannot load 跨域问题解决办法

1)、使用后台response添加header

后台response添加header,response.setHeader("Access-Control-Allow-Origin", "*"); 支持全部网站

2)、使用JSONP

JSONP的优缺点:

JSONP支持get请求不支持psot请求

什么SQL语句注入

3)、使用接口网关

使用nginx转发。

配置:

server {

        listen       80;

        server_name  www.itmayiedu.com;

        location /A {

    proxy_pass  http://a.a.com:81/A;

index  index.html index.htm;

        }

location /B {

    proxy_pass  http://b.b.com:81/B;

index  index.html index.htm;

        }

    }

 

相关:

 

 

4)、使用内部服务器转发

 内部服务器使用HttpClient技术进行转发

 

 

2)、同步接口保证数据一致性问题

例如A调用B接口,B接口没有及时反应,怎么进行补偿?

日志记录,任务调度定时补偿,自动重试机制。

 

3)、任务调度幂等性问题

①使用zookeeper实现分布式锁 缺点(须要建立临时节点、和事件通知不易于扩展)

②使用配置文件作一个开关  缺点发布后,须要重启

③数据库惟一约束,缺点效率低

④使用分布式任务调度平台

  XXLJOB

4)、MQ幂等性问题

解决办法:

① 使用日志+msg-id保存报文信息,做为去重和幂等的依据。

② 消费端代码抛出异常,不须要重试补偿,使用日志记录报文,下次发版本解决。

5、其余技术

1Shiro

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、受权、密码和会话管理。使用Shiro的易于理解的API,您能够快速、轻松地得到任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

1)、框架解释: 

subject:主体,能够是用户也能够是程序,主体要访问系统,系统须要对主体进行认证、受权。

securityManager:安全管理器,主体进行认证和受权都 是经过securityManager进行。它包含下面的认证器和受权器。

authenticator:认证器,主体进行认证最终经过authenticator进行的。 

authorizer:受权器,主体进行受权最终经过authorizer进行的。

  sessionManagerweb应用中通常是用web容器对session进行管理,shiro也提供一套session管理的方式。能够实现单点登陆。

SessionDao  经过SessionDao管理session数据,针对个性化的session数据存储须要使用sessionDao

cache Manager:缓存管理器,主要对session和受权数据进行缓存,好比将受权数据经过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。

  realm:域,领域,至关于数据源,经过realm存取认证、受权相关数据。(它的主要目的是与数据库打交道,查询数据库中的认证的信息(好比用户名和密码),查询受权的信息(好比权限的code等,因此这里能够理解为调用数据库查询一系列的信息,通常状况下在项目中采用自定义的realm,由于不一样的业务需求不同))

 

2)、认证原理

1、经过ini配置文件建立securityManager

2、调用subject.login方法主体提交认证,提交的token

3securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。

4ModularRealmAuthenticator调用IniRealm(realm传入token) ini配置文件中查询用户信息

5IniRealm根据输入的tokenUsernamePasswordToken,即这里的token是用户从页面输入的信息)从 shiro-first.ini查询用户信息(这里是测试阶段,后面都是查询的数据库,注入service,调用dao),根据帐号查询用户信息(帐号和密码)若是查询到用户信息,就给ModularRealmAuthenticator返回用户信息(帐号和密码)若是查询不到,就给ModularRealmAuthenticator返回null

6ModularRealmAuthenticator接收IniRealm返回Authentication认证信息若是返回的认证信息是nullModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException 若是返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,若是不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException

小结:

ModularRealmAuthenticator做用进行认证,须要调用realm查询用户信息(在数据库中存在用户信息)ModularRealmAuthenticator行密码对比(认证过程)。

realm:须要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),若是查到用户返回认证信息,若是查询不到返回null

 

3)、散列算法

md5+salt(这个盐通常是随机盐,即开发人员给定义随机的字符串或者数字便可)+散列次数这里的md5是原始的md5的加密了一次的密码+随机盐,而后对这个新的密码password=md5+salt,进行散列:如何进行散列呢:就是屡次md5加密md5(md5(md5(md5(password)))),这是4次散列,每次密码的破解的难度都加大。

 

4)、受权原理

原理:

1、对subject进行受权,调用方法isPermitted"permission"

2SecurityManager执行受权,经过ModularRealmAuthorizer执行受权

3ModularRealmAuthorizer执行realm(自定义的CustomRealm)从数据库查询权限数据调用realm的受权方法:doGetAuthorizationInfo

4realm从数据库查询权限数据,返回ModularRealmAuthorizer

5ModularRealmAuthorizer调用PermissionResolver进行权限串比对

6、若是比对后,isPermitted"permission"realm查询到权限数据中,说明用户访问permission串有权限,不然 没有权限,抛出异常。

5)、受权方式

shiro的受权方式有三种:

1—— 编程式:经过写if/else 受权代码块完成:(这种比较少用,通常在项目中采用后两种)

Subject subject = SecurityUtils.getSubject();

if(subject.hasRole(“admin”)) {

//有权限

} else {

//无权限

}

2——  注解式:经过在执行的Java方法上放置相应的注解完成:

@RequiresRoles("admin")

public void hello() {

//有权限

}

3——  JSP/GSP 标签:在JSP/GSP 页面经过相应的标签完成:

jsp页面导入shiro的标签既能够使用shiro的标签来进行权限的判断:

Jsp页面添加:

<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>

 

 

 

2Hessian

Hessian 是一个基于 binary-RPC 实现的远程通信 library。使用二进制传输数据。

Hessian一般经过Web应用来提供服务,经过接口暴露。

ServletSpringDispatcherServlet均可以把请求转发给Hessian服务。

由如下两种方式提供,分别为:

com.caucho.hessian.server.HessianServlet

org.springframework.web.servlet.DispatcherServlet

使用hessian提供的方式建立代理对象调用服务

HessianProxyFactory factory = new HessianProxyFactory();

HelloService proxy = (HelloService)

factory.create(HelloService.class,

"http://localhost:8080/hessian_server/hessian"

);

Object users = proxy.findAllUser();

System.out.println(users);

3Lucene

LuceneApache的一个全文检索引擎工具包,经过lucene可让程序员快速开发一个全文检索功能。

Lucene和搜索引擎不是一回事-Lucene是一个工具包,它不能独立运行,不能单独对外提供服务。

搜索引擎能够独立运行对外提供搜索服务。

1)、全文检索

全文检索首先对要搜索的文档进行分词,而后造成索引,经过查询索引来查询文档。

全文检索就是先建立索引,而后根据索引来进行搜索的过程,就叫全文检索。

好比:字典,

字典的偏旁部首页,就相似于luence的索引---索引域

字典的具体内容,就相似于luence的文档内容-文档域

 

全文检索的流程:索引流程、搜索流程

 

索引流程:采集数据—》文档处理存储到索引库中

搜索流程:输入查询条件—》经过lucene的查询器查询索引—》从索引库中取出结—》视图渲染

Lucene自己不能进行视图渲染。

 

2)、索引流程

全文检索搜索的内容的格式是多种多样的,好比:视频、mp3、图片、文档等等。

对于这种格式不一样的数据,须要先将他们采集到本地,而后统一封装到lucene的文档对象中,也就是说须要将存储的内容进行统一才能对它进行查询。

1)、采集数据的方式

对于互联网中的数据,使用爬虫工具(http工具)将网页爬取到本地

对于数据库中的数据,使用jdbc程序进行数据采集

对于文件系统的数据,使用io流采集

由于目前搜索引擎主要搜索数据的来源是互联网,搜索引擎使用一种爬虫程序抓取网页( 经过http抓取html网页信息)

如下是一些爬虫项目-Solr-Nutch-jsoup-heritrix

 

2)、文档域

文档域存储的信息就是采集到的信息,经过Document对象来存储,具体说是经过Document对象中field域来存储数据。

好比:数据库中一条记录会存储一个一个Document对象,数据库中一列会存储成Document中一个field域。

文档域中,Document对象之间是没有关系的。并且每一个Document中的field域也不必定同样。

 

3)、索引域

索引域主要是为了搜索使用的。索引域内容是通过lucene分词以后存储的。

  

  倒排索引结构是根据内容(词语)找文档,倒排索引结构也叫反向索引结构,包括索引和文档两部分,

  索引即词汇表,它是在索引中匹配搜索关键字,因为索引内容量有限而且采用固定优化算法搜索速度很快,

  找到了索引中的词汇,词汇与文档关联,从而最终找到了文档。

  IndexWriter是索引过程的核心组件,经过IndexWriter能够建立新索引、更新索引、删除索引操做。 IndexWriter须要经过Directory对索引进行存储操做。

  Directory描述了索引的存储位置,底层封装了I/O操做,负责对索引进行存储。

 它是一个抽象类,它的子类经常使用的包括FSDirectory(在文件系统存储索引)、RAMDirectory(在内存存储索引)。

4)、分词

 Lucene中分词主要分为两个步骤:分词、过滤

分词:将field域中的内容一个个的分词。

过滤:将分好的词进行过滤,好比去掉标点符号、大写转小写、词的型还原

(复数转单数、过去式转成如今式)、停用词过滤

停用词:单独应用没有特殊意义的词。好比的、啊、等,英文中的this is a the等等。

 

同一个域中相同的语汇单元(Token)对应同一个Term(词),

它记录了语汇单元的内容及所在域的域名等,还包括来该token出现的频率及位置。

l不一样的域中拆分出来的相同的单词对应不一样的term

l相同的域中拆分出来的相同的单词对应相同的term

 

(3)、搜索流程

查询对象Query-索引搜索对象IndexSearcher-结果集TopDocs

索引读取对象IndexReader

索引目录流对象Directory

索引库

1)、Field

Field的属性

Ø 是否分词(Tokenized

是:对该field存储的内容进行分词,分词的目的,就是为了索引。

否:不须要对field存储的内容进行分词,不分词,不表明不索引,而是将整个内容进行索引。

是否索引(Indexed

是否存储(Stored

Field的经常使用类型

字符串-StringField

Long-LongField

重载方法支持多种类型-StoredField

字符串--TextField

建立查询对象的方式

Ø 经过Query子类来建立查询对象

Query子类经常使用的有:TermQueryNumericRangeQueryBooleanQuery

不能输入lucene的查询语法,不须要指定分词器

Ø

经过QueryParser来建立查询对象(经常使用)

QueryParserMultiFieldQueryParser

能够输入lucene的查询语法、能够指定分词器

 

2)、查询语法

域名+“:”+搜索的关键字

域名+:+[最小值 TO 最大值]

组合条件查询--and--or--not

条件1 AND 条件2

条件1 OR 条件2

条件1 NOT 条件2

Lucene搜索结果可经过TopDocs遍历,TopDocs类提供了少许的属性

totalHits--匹配搜索条件的总记录数

scoreDocs--顶部匹配记录

3)、相关度排序

Tf(词在同一个文档中出现的频率)越高,说明词的权重越高

Df(词在多个文档中出现的频率)越高,说明词的权重越低

4)、设置boost值影响打分--Boost:加权值,默认是1.0f

建立索引时设置boost

搜索时设置boost

 

5)、中文分词器

Lucene自带的中文分词器--CJKAnalyzer--StandardAnalyzer

一般使用IK-analyzer中文分词器,使用luke来查询中文分词效果

4Solr

Solrapache的顶级开源项目,它是使用java开发 ,基于lucene的全文检索服务器。

Solrlucene提供了更多的查询语句,并且它可扩展、可配置,同时它对lucene的性能进行了优化。

 

1)、Solr是如何实现全文检索的呢?

索引流程:solr客户端(浏览器、java程序)能够向solr服务端发送POST请求,

请求内容是包含Field等信息的一个xml文档,经过该文档,solr实现对索引的维护(增删改)

搜索流程:solr客户端(浏览器、java程序)能够向solr服务端发送GET请求,solr服务器返回一个xml文档。

Solr一样没有视图渲染的功能

 

2)、Solrlucene的区别。

Lucene是一个全文检索引擎工具包,它只是一个jar包,不能独立运行,对外提供服务。

 

Solr是一个全文检索服务器,它能够单独运行在servlet容器,能够单独对外提供搜索和索引功能。

Solrlucene在开发全文检索功能时,更快捷、更方便。

3)、目录结构

Solrcoresolrhome/collection1/conf(核心配置solrconfig.xml)&data

 

(4)、界面经常使用功能

DashboardLoggingCloudCore Adminjava propertiesTread DumpCore selector AnalysisdataimportDocumentQuery

 

5)、多solrcore的配置

schema.xml文件中,主要配置了solrcore的一些数据信息,包括FieldFieldType的定义等信息,

solr中,FieldFieldType都须要先定义后使用。

Filed

dynamicField

uniqueKey

copyField

FieldType

中文分词器也须要配置FieldTypeField

 

配置业务Field-根据需求配置

 

6)、dataimport

该插件能够将数据库中指定的sql语句的结果导入到solr索引库中。

Dataimportjar

修改solrconfig.xml文件,添加lib标签

<lib dir="${solr.install.dir:../..}/contrib/dataimporthandler/lib" regex=".*\.jar" />

MySQL数据库驱动包

<lib dir="${solr.install.dir:../..}/contrib/db/lib" regex=".*\.jar" />

solrconfig.xml中,添加一个dataimportrequestHandler

 

7)、Solrj就是solr服务器的java客户端。

配置springmvc.xml--使用org.apache.solr.client.solrj.impl.HttpSolrClient构造注入

5FastDFS

FastDFS是用c语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

富文本编辑器的使用KindEditor

轮播图

须要使用HttpClient调用rest发布的服务。把结果传递给jspJsp中须要的json数据格式。

freemarker-是一个用Java语言编写的模板引擎,它基于模板来生成文本输出

6RabbitMQ

AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不一样产品,不一样的开发语言等条件的限制。Erlang中的实现有 RabbitMQErlang(['ə:læŋ])是一种通用的面向并发的编程语言,它由瑞典电信设备制造商爱立信所辖的CS-Lab开发,目的是创造一种能够应对大规模并发活动的编程语言和运行环境。

MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通讯方法。应用程序经过读写出入队列的消息(针对应用程序的数据)来通讯,而无需专用链接来连接它们,RabbitMQ是一个在AMQP基础上完成的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。

 

1)、消息队列

1)、1.点对点的队列

功能:一个生产者P发送消息到队列Q,一个消费者C接收

生产者实现思路:

建立链接工厂ConnectionFactory,设置服务地址127.0.0.1,端口号5672,设置用户名、密码、virtual host,从链接工厂中获取链接connection,使用链接建立通道channel,使用通道channel建立队列queue,使用通道channel向队列中发送消息,关闭通道和链接。

消费者实现思路

建立链接工厂ConnectionFactory,设置服务地址127.0.0.1,端口号5672,设置用户名、密码、virtual host,从链接工厂中获取链接connection,使用链接建立通道channel,使用通道channel建立队列queue, 建立消费者并监听队列,从队列中读取消息。

 

2)、2 工做队列模式Work Queue

功能描述:一个生产者发送消息到队列中,有多个消费者共享一个队列,每一个消费者获取的消息是惟一的。为了保证服务器同一时刻只发送一条消息给消费者,保证资源的合理利用。channal.basicQos(1);这样是为了保证多个消费者接收的消息数量不同,能者多劳,若是不设置,那么消费者是平均分配消息(例如10条消息,每一个消费者接收5条)

 

3)、3 发布/订阅模式Publish/Subscribe

这个多是消息队列中最重要的队列了,其余的都是在它的基础上进行了扩展。

功能实现:一个生产者发送消息,多个消费者获取消息(一样的消息),包括一个生产者,一个交换机,多个队列,多个消费者。

思路解读(重点理解):

1)一个生产者,多个消费者

2)每个消费者都有本身的一个队列

3)生产者没有直接发消息到队列中,而是发送到交换机

4)每一个消费者的队列都绑定到交换机上

5)消息经过交换机到达每一个消费者的队列

注意:交换机没有存储消息功能,若是消息发送到没有绑定消费队列的交换机,消息则丢失。

 

4)、4 路由模式Routing

功能:生产者发送消息到交换机并指定一个路由key,消费者队列绑定到交换机时要制定路由keykey匹配就能接受消息,key不匹配就不能接受消息),例如:咱们能够把路由key设置为insert ,那么消费者队列key指定包含insert才能够接收消息,消费者队列key定义为update或者delete就不能接收消息。很好的控制了更新,插入和删除的操做。

5)、5 通配符模式Topics

说明:此模式实在路由key模式的基础上,使用了通配符来管理消费者接收消息。

生产者P发送消息到交换机Xtype=topic,交换机根据绑定队列的routing key的值进行通配符匹配;

符号#:匹配一个或者多个词lazy.# 能够匹配lazy.irs或者lazy.irs.cor

符号*:只能匹配一个词lazy.* 能够匹配lazy.irs或者lazy.cor

(2)、总结

RabbitMQ提供了6种模式,

分别是HelloWorldWork QueuePublish/SubscribeRoutingTopicsRPC Request/reply

其中Publish/SubscribeRoutingTopics三种模式能够统一归为Exchange模式,只是建立时交换机的类型不同,分别是fanoutdirecttopicSpring提供了rabbitmq的一个实现,因此集成起来很方便。

6、大数据技术

一、Hadoop

(1)HDFS

1).HDFS采用master/slave架构。

一个HDFS集群是由一个Namenode和必定数目的Datanodes组成。Namenode是一个中心服务器,负责管理文件系统的名字空间(namespace)以及客户端对文件的访问。

集群中的Datanode通常是一个节点一个,负责管理它所在节点上的存储。HDFS暴露了文件系统的名字空间,用户可以以文件的形式在上面存储数据。从内部看,一个文件其实被分红一个或多个数据块,这些块存储在一组Datanode上。Namenode执行文件系统的名字空间操做,好比打开、关闭、重命名文件或目录。它也负责肯定数据块到具体Datanode节点的映射。Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的建立、删除和复制。

2).文件系统的名字空间 (namespace)

HDFS支持传统的层次型文件组织结构。用户或者应用程序能够建立目录,而后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统相似:用户能够建立、删除、移动或重命名文件。当前,HDFS不支持用户磁盘配额和访问权限控制,也不支持硬连接和软连接。可是HDFS架构并不妨碍实现这些特性。

Namenode负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被Namenode记录下来。

应用程序能够设置HDFS保存的文件的副本数目。文件副本的数目称为文件的副本系数,这个信息也是由Namenode保存的。

3).数据复制

HDFS被设计成可以在一个大集群中跨机器可靠地存储超大文件。它将每一个文件存储成一系列的数据块,除了最后一个,全部的数据块都是一样大小的。

为了容错,文件的全部数据块都会有副本。每一个文件的数据块大小和副本系数都是可配置的。应用程序能够指定某个文件的副本数目。

副本系数能够在文件建立的时候指定,也能够在以后改变。HDFS中的文件都是一次性写入的,而且严格要求在任什么时候候只能有一个写入者。

Namenode全权管理数据块的复制,它周期性地从集群中的每一个Datanode接收心跳信号和块状态报告(Blockreport)

接收到心跳信号意味着该Datanode节点工做正常。块状态报告包含了一个该Datanode上全部数据块的列表。

4).副本选择

为了下降总体的带宽消耗和读取延时,HDFS会尽可能让读取程序读取离它最近的副本。若是在读取程序的同一个机架上有一个副本,

那么就读取该副本。若是一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。

 

1)安全模式

 

Namenode启动后会进入一个称为安全模式的特殊状态。

 

2).文件系统元数据的持久化

 

Namenode上保存着HDFS的名字空间。对于任何对文件系统元数据产生修改的操做,Namenode都会使用一种称为EditLog的事务日志记录下来

 

3).通信协议

 

全部的HDFS通信协议都是创建在TCP/IP协议之上。客户端经过一个可配置的TCP端口链接到Namenode,经过ClientProtocol协议与Namenode交互。而Datanode使用DatanodeProtocol协议与Namenode交互

 

4).集群均衡

 

HDFS的架构支持数据均衡策略。若是某个Datanode节点上的空闲空间低于特定的临界点,按照均衡策略系统就会自动地将数据从这个Datanode移动到其余空闲的Datanode

 

9.Web接口

 

10.Shell命令

 

11.Secondary NameNode

 

SecondaryNameNode有两个做用,一是镜像备份,二是日志与镜像的按期合并

 

12.HDFS权限管理

 

Hadoop分布式文件系统(HDFS)容许管理员为每一个目录设置配额。 新创建的目录没有配额。 最大的配额是Long.Max_Value

 

13).命令手册

 

用户命令

 

archive 建立一个hadoop档案文件

 

distcp 递归地拷贝文件或目录

 

fs 运行一个常规的文件系统客户端

 

fsck 运行HDFS文件系统检查工具------查看HDFS文件对应的文件块信息(Block)和位置信息

 

jar 运行jar文件。用户能够把他们的Map Reduce代码捆绑到jar文件中,使用这个命令执行

 

job 用于和Map Reduce做业交互和命令。

 

pipes 运行pipes做业。

 

version 打印版本信息。

 

CLASSNAME hadoop脚本可用于调调用任何类。

 

管理命令

 

balancer 运行集群平衡工具

 

daemonlog 获取或设置每一个守护进程的日志级别

 

datanode 运行一个HDFSdatanode

 

dfsadmin 运行一个HDFSdfsadmin客户端

 

jobtracker 运行MapReduce job Tracker节点。

 

namenode 运行namenode。有关升级,回滚

 

secondarynamenode 运行HDFSsecondary namenode

 

tasktracker 运行MapReducetask Tracker节点

 

 

 

FS Shell

 

cat 将路径指定文件的内容输出到stdout

 

chgrp 改变文件所属的组

 

chmod 改变文件的权限

 

chown 改变文件的拥有者

 

copyFromLocal 除了限定源路径是一个本地文件外,和put命令类似。

 

copyToLocal 除了限定目标路径是一个本地文件外,和get命令相似

 

cp 将文件从源路径复制到目标路径

 

du 显示目录中全部文件的大小

 

dus 显示文件的大小。

 

expunge 清空回收站。

 

get 复制文件到本地文件系统

 

getmerge 接受一个源目录和一个目标文件做为输入,而且将源目录中全部的文件链接成本地目标文件

 

ls

 

lsr

 

mkdir

 

movefromLocal

 

mv 将文件从源路径移动到目标路径。

 

put 从本地文件系统中复制单个或多个源路径到目标文件系统。也支持从标准输入中读取输入写入目标文件系统。

 

rm

 

rmr delete的递归版本

 

setrep 改变一个文件的副本系数

 

stat 返回指定路径的统计信息

 

tail 将文件尾部1K字节的内容输出到stdout

 

test

 

text 将源文件输出为文本格式

 

touchz 建立一个0字节的空文件。

 

 

 

DistCp(分布式拷贝)是用于大规模集群内部和集群之间拷贝的工具。 它使用Map/Reduce实现文件分发,错误处理和恢复,以及报告生成

 

 

 

(1)Map/Reduce

 

1.概述

 

Hadoop Map/Reduce是一个使用简易的软件框架,基于它写出来的应用程序可以运行在由上千个商用机器组成的大型集群上,并以一种可靠容错的方式并行处理上T级别的数据集。一个Map/Reduce 做业(job) 一般会把输入的数据集切分为若干独立的数据块,由 map任务(task)以彻底并行的方式处理它们。框架会对map的输出先进行排序,而后把结果输入给reduce任务。一般做业的输入和输出都会被存储在文件系统中。整个框架负责任务的调度和监控,以及从新执行已经失败的任务。

 

Map/Reduce框架由一个单独的master JobTracker 和每一个集群节点一个slave TaskTracker共同组成。master负责调度构成一个做业的全部任务,这些任务分布在不一样的slave上,master监控它们的执行,从新执行已经失败的任务。而slave仅负责执行由master指派的任务。

 

MR有两个阶段组成:MapReduce,用户只需实现map()reduce()两个函数,便可实现分布式计算。

 

2.MapReduce的执行步骤

 

A Map任务处理

 

1.1 读取HDFS中的文件。每一行解析成一个<k,v>。每个键值对调用一次map函数。                <0,hello you>   <10,hello me>                    

 

1.2 覆盖map(),接收1.1产生的<k,v>,进行处理,转换为新的<k,v>输出。          <hello,1> <you,1> <hello,1> <me,1>

 

  1.3 1.2输出的<k,v>进行分区。默认分为一个区。详见《Partitioner-Partitioner负责控制map输出结果key的分割。

 

  1.4 对不一样分区中的数据进行排序(按照k)、分组。分组指的是相同keyvalue放到一个集合中。 排序后:<hello,1> <hello,1> <me,1> <you,1>  分组后:<hello,{1,1}><me,{1}><you,{1}>

 

  1.5 (可选)对分组后的数据进行归约。详见《Combiner》(文件倒叙索引)

 

使用IK分词器分词。

 

lucene搜索引擎分析

 

B Reduce任务处理

 

  2.1 多个map任务的输出,按照不一样的分区,经过网络copy到不一样的reduce节点上。(shuffle)详见《shuffle过程分析》

 

  2.2 对多个map的输出进行合并、排序。覆盖reduce函数,接收的是分组后的数据,实现本身的业务逻辑, <hello,2> <me,1> <you,1>处理后,产生新的<k,v>输出。

 

  2.3 reduce输出的<k,v>写到HDFS中。

 

// combiner

 

public class LastSearchComb extends Reducer<Text, Text, Text, Text> {

 

@Override

 

protected void reduce(Text arg0, Iterable<Text> arg1, Context arg2) throws IOException, InterruptedException {

 

ik分词器分词

 

}

 

}

 

3.数据流

 

file->mapmappertask) 按groupshuffle)到reducereducerTask->directory

 

(2)Yarn

 

Yarn主要由四个重要角色组成:ResourceManager顾名思义资源管理器--NodeManager节点管理器--ApplicaitonMaster主要负责监控应用---Container容器是资源调度的单位

 

一、Kafka

 

1)、Kafka概述

 

Kafka总体结构图、Consumertopic关系、Kafka消息分发、Consumer的负载均衡、Kafka文件存储机制、Kafka partition segment

 

2)、Kafka名词解释和工做方式

 

l 1)、 Producer :消息生产者,就是向kafka broker发消息的客户端。

 

l 2)、Consumer :消息消费者,向kafka broker取消息的客户端

 

l 3)、 Topic :能够理解为一个队列。

 

l 4)、Consumer Group CG):这是kafka用来实现一个topic消息的广播(发给全部的consumer)和单播(发给任意一个consumer)的手段。

 

一个topic能够有多个CGtopic的消息会复制(不是真的复制,是概念上的)到全部的CG,但每一个partion只会把消息发给该CG中的一个consumer。若是须要实现广播,只要每一个consumer有一个独立的CG就能够了。要实现单播只要全部的consumer在同一个CG

 

CG还能够将consumer进行自由的分组而不须要屡次发送消息到不一样的topic

 

 

 

l 5)、Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker能够容纳多个topic

 

l 6)、Partition:为了实现扩展性,一个很是大的topic能够分布到多个broker(即服务器)上,一个topic能够分为多个partition,每一个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的idoffset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的总体(多个partition间)的顺序。

 

   7)、Offset**kafka的存储文件都是按照offset.kafka来命名,用offset作名字的好处是方便查找。例如你想找位于2049的位置,

 

只要找到2048.kafka的文件便可。固然the first offset就是00000000000.kafka

 

 

 

3)、Consumertopic关系

 

对于Topic中的一条特定的消息,只会被订阅此Topic的每一个group中的其中一个consumer消费,此消息不会发送给一个group的多个consumer 那么一个group中全部的consumer将会交错的消费整个Topic,每一个groupconsumer消息消费互相独立,咱们能够认为一个group是一个”订阅”者。  在kafka,一个partition中的消息只会被group中的一个consumer消费(同一时刻)

 

一个Topic中的每一个partions,只会被一个”订阅者”中的一个consumer消费,不过一个consumer能够同时消费多个partitions中的消息。 kafka的设计原理决定,对于一个topic,同一个group中不能有多于partitions个数的consumer同时消费,不然将意味着某些consumer将没法获得消息。

 

kafka只能保证一个partition中的消息被某个consumer消费时是顺序的;事实上,从Topic角度来讲,当有多个partitions,消息仍不是全局有序的。

 

 

 

4)、Kafka消息的分发

 

Producer客户端负责消息的分发

 

Producer消息发送的应答机制

 

设置发送数据是否须要服务端的反馈,有三个值0,1,-1

 

0: producer不会等待broker发送ack

 

1: leader接收到消息以后发送ack

 

-1: 当全部的follower都同步消息成功后发送ack

 

request.required.acks=0

 

5)、Consumer的负载均衡 

 

当一个group,consumer加入或者离开时,会触发partitions均衡.均衡的最终目的,是提高topic的并发消费能力

 

6)、kafka文件存储机制

 

Kafka文件存储中,同一个topic下有多个不一样partition,每一个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。 每一个partion(目录)至关于一个巨型文件被平均分配到多个大小相等segment()数据文件中。但每一个段segment file消息数量不必定相等,这种特性方便old segment file快速被删除。默认保留7天的数据

 

 

 

集群时,broker.id=?不能重复

 

建立topic

 

查看topic

 

建立producer

 

建立consumer

 

 

 

7)、springboot集成kafka

 

//1、准备一个TopologyBuilder

 

//storm框架支持多语言,在Java环境下建立一个拓扑,须要使用TopologyBuilder

 

TopologyBuilder topologyBuilder = new TopologyBuilder();

 

//MySpout类,在已知的英文句子中,所及发送一条句子出去

 

topologyBuilder.setSpout("mySpout", new MySpout(), 2); //继承BaseRichSpout

 

//MySplitBolt类,主要是将一行一行的文本内容切割成单词

 

topologyBuilder.setBolt("mybolt1", new MySplitBolt(), 2).shuffleGrouping("mySpout"); //继承BaseRichBolt

 

//MyCountBolt类,负责对单词的频率进行累加

 

topologyBuilder.setBolt("mybolt2", new MyCountBolt(), 4).fieldsGrouping("mybolt1", new Fields("word")); //继承BaseRichBolt重写execute()方法

 

 

 

//2、建立一个configuration,用来指定当前topology 须要的worker的数量

 

//启动topology的配置信息

 

Config config = new Config();

 

//定义你但愿集群分配多少个工做进程给你来执行这个topology

 

config.setNumWorkers(2);

 

//3、提交任务  -----两种模式 本地模式和集群模式

 

//这里将拓扑名称写死了mywordcount,因此在集群上打包运行的时候,不用写拓扑名称了!也可用arg[0]

 

StormSubmitter.submitTopology("mywordcount", config, topologyBuilder.createTopology());

 

二、Storm

 

Storm-Flume+Kafka+Storm+Redis构建大数据实时处理系统

 

1)、Storm框架组成

 

Storm框架主要由7部分组成

 

1)、Topology:一个实时应用的计算任务被打包做为Topology发布,这同HadoopMapReduce任务类似。

 

2)、SpoutStorm中的消息源,用于为Topology生产消息(数据),通常是从外部数据源(如Message QueueRDBMSNoSQLRealtime Log

 

不间断地读取数据并发送给Topology消息(tuple元组)。

 

3)、BoltStorm中的消息处理者,用于为Topology进行消息的处理,Bolt能够执行过滤,聚合, 查询数据库等操做,并且能够一级一级的进行处理。

 

4)、Stream:产生的数据(tuple元组)。

 

5)、Stream grouping:在Bolt任务中定义的Stream进行区分。

 

6)、Task:每一个Spout或者Bolt在集群执行许多任务。

 

7)、WorkerTopology跨一个或多个Worker节点的进程执行。

 

2)、Storm 分布式计算结构

 

Storm 分布式计算结构称为 topology(拓扑),由 stream(数据流), spout(数据流的生成者), bolt(运算)组成。

 

spout 表明了一个 Storm topology 的主要数据入口,充当采集器的角色,链接到数据源,将数据转化为一个个 tuple,并将 tuple 做为数据流进行发射。

 

bolt 能够理解为计算程序中的运算或者函数,将一个或者多个数据流做为输入,对数据实施运算后,选择性地输出一个或者多个数据流。bolt 能够订阅多个由 spout 或者其余bolt 发射的数据流,这样就能够创建复杂的数据流转换网络。

 

实现BaseRichBolt接口,重写单词计数方法

 

SentenceSpout spout = new SentenceSpout();

 

SplitSentenceBolt splitBolt = new SplitSentenceBolt();

 

WordCountBolt countBolt = new WordCountBolt();

 

ReportBolt reportBolt = new ReportBolt();

 

TopologyBuilder builder = new TopologyBuilder();//建立了一个TopologyBuilder实例

 

 

 

//设置两个Executeor(线程),默认一个

 

builder.setSpout(SENTENCE_SPOUT_ID, spout,2);

 

 

 

//SplitSentenceBolt单词分割器设置4Task2Executeor(线程)

 

builder.setBolt(SPLIT_BOLT_ID, splitBolt,2).setNumTasks(4).shuffleGrouping(SENTENCE_SPOUT_ID);

 

 

 

//WordCountBolt单词计数器设置4Executeor(线程)

 

builder.setBolt(COUNT_BOLT_ID, countBolt,4).fieldsGrouping( SPLIT_BOLT_ID, new Fields("word"));

 

 

 

//globalGrouping是把WordCountBolt发射的全部tuple路由到惟一的ReportBolt

 

builder.setBolt(REPORT_BOLT_ID, reportBolt).globalGrouping(COUNT_BOLT_ID);

 

Config config = new Config();//Config类是一个HashMap<String,Object>的子类,用来配置topology运行时的行为

 

//设置worker数量

 

//config.setNumWorkers(2);

 

LocalCluster cluster = new LocalCluster();

 

//本地提交

 

cluster.submitTopology(TOPOLOGY_NAME, config, builder.createTopology());

 

cluster.killTopology(TOPOLOGY_NAME);        

 

cluster.shutdown();

 

 

 

Spoutstorm用于数据生成的一个组件,。一般,Spout会实现一个IRichSpout接口。

 

 

 

IRichSpout接口有如下重要的方法:

 

 

 

1.open:−提供Spout以及spout的执行环境。executors会运行这个方法来初始化spout

 

 

 

2.nextTuple:经过收集器发送产生的数据。

 

 

 

3.close−关闭Spout时调用close方法。

 

 

 

4.declareOutputFields:声明输出元组的schema

 

 

 

5.ack:处理特定的元组

 

 

 

6.fail:指定一个特定的不用处理和再加工元组。

 

 

 

1、构建四台服务器,上面会创建管理员,叫作nimbus,用来协调任务、分配任务;

 

2、在下面的各个服务器上构建supervisor,用来处理数据;在supervisor上会启动对应的worker,每一个worker对应一个端口:670067016702;每一个机器都一致。

 

3Client提交任务是将其提交给nimbus,由nimbus分配任务:首先nimbus会获取空闲worker的信息,而后根据并发数分配任务(topology会产生任务信息:

 

有一个并发数为myspout2mybolt12mybolt24worker2等,共造成8task,每一个worker中有8/2=4个,即每一个worker上有4个线程,

 

task的划分是用taskid去取模来分配),分配完成后,将这些信息放在zk上。

 

4Supervisor中会经过watcher监听zktopology中有三个任务类型,myspoutmybolt1mybolt2三种,这三种类型会分配给2worker

 

如上图的前两个supervisor,此时第三个supervisor会空闲;

 

5、当这个数据分配好后,开始链接外部数据源,而后开始运做,myspout链接外面的datasourcemyspout随机发的话数据会经过myspout发送给两个bolt1(在两台服务器上)

 

bolt1上将sentence切分开,造成一个个单词,bolt1将数据分给bolt2的时候是按照field进行的,相同的单词会给到同一个bolt2上。

 

Disruptor--Disruptor是由LAMX(欧洲顶级金融公司)设计和开源的大规模、高并发、低延迟的异步处理框架-Worker 内部通讯技术(Disruptor)

 

 

 

3)、Storm术语解释

 

Storm的术语包括StreamSpoutBoltTaskWorkerStream GroupingTopologyStream是被处理的数据。Sprout是数据源。Bolt处理数据。

 

Task是运行于SpoutBolt中的线程。Worker是运行这些线程的进程。Stream Grouping规定了Bolt接收什么东西做为输入数据。

 

数据能够随机分配(术语为Shuffle),或者根据字段值分配(术语为Fields),或者广播(术语为All),或者老是发给一个Task(术语为Global),也能够不关心该数据(术语为None),或者由自定义逻辑来决定(术语为Direct)。Topology是由Stream Grouping链接起来的SpoutBolt节点网络.

 

3.2 stream grouping分类

 

1. Shuffle Grouping: 随机分组, 随机派发stream里面的tuple,保证每一个bolt接收到的tuple数目相同.

 

2. Fields Grouping:按字段分组,好比按userid来分组,具备一样useridtuple会被分到相同的Bolts,而不一样的userid则会被分配到不一样的Bolts.

 

3. All Grouping:广播发送, 对于每个tuple,全部的Bolts都会收到.

 

4. Global Grouping: 全局分组,这个tuple被分配到storm中的一个bolt的其中一个task.再具体一点就是分配给id值最低的那个task.

 

5. Non Grouping: 不分组,意思是说stream不关心到底谁会收到它的tuple.目前他和Shuffle grouping是同样的效果,有点不一样的是storm会把这个bolt放到这个bolt的订阅者同一个线程去执行.

 

6. Direct Grouping: 直接分组,这是一种比较特别的分组方法,用这种分组意味着消息的发送者举鼎由消息接收者的哪一个task处理这个消息. 只有被声明为Direct Stream的消息流能够声明这种分组方法.并且这种消息tuple必须使用emitDirect方法来发射. 消息处理者能够经过TopologyContext来或者处理它的消息的taskid (OutputCollector.emit方法也会返回taskid)

 

4)、Storm组件 详细介绍

 

Storm集群主要由一个主节点和一群工做节点(worker node)组成,经过 Zookeeper进行协调。

 

3.3.1 主节点:

 

主节点一般运行一个后台程序 —— Nimbus,用于响应分布在集群中的节点,分配任务和监测故障。这个很相似于Hadoop中的Job Tracker

 

3.3.2工做节点:

 

工做节点一样会运行一个后台程序 —— Supervisor,用于收听工做指派并基于要求运行工做进程。每一个工做节点都是topology中一个子集的实现。

 

NimbusSupervisor之间的协调则经过Zookeeper系统或者集群。

 

3.3.3 Zookeeper

 

Zookeeper是完成SupervisorNimbus之间协调的服务。而应用程序实现实时的逻辑则被封装进Storm中的“topology”。 topology则是一组由Spouts(数据源)和Bolts(数据操做)经过Stream Groupings进行链接的图。下面对出现的术语进行更深入的解析。

 

3.3.4 Spout

 

简而言之,Spout历来源处读取数据并放入topologySpout分红可靠和不可靠两种;当Storm接收失败时,可靠的Spout会对tuple(元组,数据项组成的列表)进行重发;而不可靠的Spout不会考虑接收成功与否只发射一次。 而Spout中最主要的方法就是nextTuple(),该方法会发射一个新的tupletopology,若是没有新tuple发射则会简单的返回。

 

3.3.5 Bolt

 

Topology中全部的处理都由Bolt完成。Bolt能够完成任何事,好比:链接的过滤、聚合、访问文件/数据库、等等。BoltSpout中接收数据并进行处理,若是遇到复杂流的处理也可能将tuple发送给另外一个Bolt进行处理。而Bolt中最重要的方法是execute(),以新的tuple做为参数接收。无论是Spout仍是Bolt,若是将tuple发射成多个流,这些流均可以经过declareStream()来声明。

 

3.3.6Stream Groupings

 

Stream Grouping定义了一个流在Bolt任务间该如何被切分。这里有Storm提供的6Stream Grouping类型:

 

1. 随机分组(Shuffle grouping):随机分发tupleBolt的任务,保证每一个任务得到相等数量的tuple

 

2. 字段分组(Fields grouping):根据指定字段分割数据流,并分组。例如,根据“user-id”字段,相同“user-id”的元组老是分发到同一个任务,不一样“user-id”的元组可能分发到不一样的任务。

 

3. 所有分组(All grouping):tuple被复制到bolt的全部任务。这种类型须要谨慎使用。

 

4. 全局分组(Global grouping):所有流都分配到bolt的同一个任务。明确地说,是分配给ID最小的那个task

 

5. 无分组(None grouping):你不须要关心流是如何分组。目前,无分组等效于随机分组。但最终,Storm将把无分组的Bolts放到BoltsSpouts订阅它们的同一线程去执行(若是可能)。

 

6. 直接分组(Direct grouping):这是一个特别的分组类型。元组生产者决定tuple由哪一个元组处理者任务接收

 

5)、Worker进程间通讯原理

 

Worker进程间技术(NettyZeroMQ)

 

Worker 内部通讯技术(Disruptor)

 

6)、经常使用命令

 

启动Storm

 

 storm nimbus

 

 storm supervisor

 

 storm ui

 

storm jar

 

hadoop jar

 

storm kill 【拓扑名称】---杀死任务命令

 

 

一、Spark

1)、Scala

1)、Scala介绍

1.Spark中使用的是Sacla2.10

2.Scala官网6个特征。

1).Javascala能够混编

2).类型推测(自动推测类型)

3).并发和分布式(Actor

4).特质,特征(相似javainterfaces abstract结合)

5).模式匹配(相似java switch

6).高阶函数

2)、Scala安装使用

windows安装,配置环境变量

新建SCALA_HOME

 

上个步骤完成后,编辑Path变量,在后面追加以下:

;%SCALA_HOME%\bin;%SCALA_HOME%\jre\bin

 

  • 打开cmd,输入:scala  - version  看是否显示版本号,肯定是否安装成功

 

 

eclipse 配置scala插件

  • 下载插件(必定要对应eclipse版本下载)

http://scala-ide.org/download/prev-stable.html  

 

  • 下载好zip包后,解压以下:

 

  • featuresplugins两个文件夹拷贝到eclipse安装目录中的” dropins/scala”目录下。进入dropins,新建scala文件夹,将两个文件夹拷贝到“dropins/scala”下
scala ide

下载网址:http://scala-ide.org/download/sdk.html 

idea 中配置scala插件

  • 打开idea,close项目后,点击Configure->Plugins

 

  • 搜索scala,点击Install安装

 

  • 设置jdk,打开Project Structure,点击new 选择安装好的jdk路径

 

 

  • 建立scala项目,配置scala sdk(Software Development Kit)

 

 

 

点击第三步,弹出选择SDK,点击Browse选择本地安装的Scala目录。选择system.

3)、Scala基础

数据类型

 

 

 

变量和常量的声明
  • 定义变量或者常量的时候,也能够写上返回的类型,通常省略,如:val a:Int = 10
  • 常量不可再赋值

    /**

     * 定义变量和常量

     * 变量 :var 定义 ,可修改

     * 常量 :val 定义,不可修改

     */

    var name = "zhangsan"

    println(name)

    name ="lisi"

    println(name)

    val gender = "m"

//    gender = "m"//错误,不能给常量再赋值

 

类和对象
  • 建立类

class Person{

  val name = "zhangsan"

  val age = 18

  def sayName() = {

    "my name is "+ name

  }

}

 

  • 建立对象

object Lesson_Class {

   def main(args: Array[String]): Unit = {

    val person = new Person()

    println(person.age);

    println(person.sayName())

  }

}

 

  • 伴生类和伴生对象

class Person(xname :String , xage :Int){

  var name = Person.name

  val age = xage

  var gender = "m"

  def this(name:String,age:Int,g:String){

    this(name,age)

    gender = g

  }

  

  def sayName() = {

    "my name is "+ name

  }

 

}

 

object Person {

  val name = "zhangsanfeng"

  

  def main(args: Array[String]): Unit = {

    val person = new Person("wagnwu",10,"f")

    println(person.age);

    println(person.sayName())

    println(person.gender)

  }

}

 

注意点:

建议类名首字母大写 ,方法首字母小写,类和方法命名建议符合驼峰命名法。

l scala 中的object是单例对象,至关于java中的工具类,能够当作是定义静态的方法的类。object不能够传参数。另:Trait不能够传参数

l scala中的class类默承认以传参数,默认的传参数就是默认的构造函数。

重写构造函数的时候,必需要调用默认的构造函数。

l class 类属性自带getter setter方法。

使用object时,不用new,使用class时要new ,而且new的时候,class中除了方法不执行,其余都执行。

若是在同一个文件中,object对象和class类的名称相同,则这个对象就是这个类的伴生对象,这个类就是这个对象的伴生类。能够互相访问私有变量。

 

if else

    /**

     * if else

     */

    val age =18 

    if (age < 18 ){

     println("no allow")

    }else if (18<=age&&age<=20){

     println("allow with other")

    }else{

     println("allow self")

    }

 

for ,while,dowhile
  1. tountil 的用法(不带步长,带步长区别)

  /**

     * tountil

     * 例:

     * 1 to 10 返回110Range数组,包含10

     * 1 until 10 返回110 Range数组 ,不包含10

     */

    

    println(1 to 10 )//打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

    println(1.to(10))//与上面等价,打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

    

    println(1 to (10 ,2))//步长为2,从1开始打印 ,1,3,5,7,9

    println(1.to(10, 2))

    

    println(1 until 10 ) //不包含最后一个数,打印 1,2,3,4,5,6,7,8,9

    println(1.until(10))//与上面等价

    

println(1 until (10 ,3 ))//步长为2,从1开始打印,打印1,4,7

 

  1. 建立for循环

   /**

     * for 循环

     *

     */

    for( i <- 1 to 10 ){

      println(i)

    }

  1. 建立多层for循环

    //能够分号隔开,写入多个list赋值的变量,构成多层for循环

    //scala中 不能写count++ count-- 只能写count+

    var count = 0;

    for(i <- 1 to 10; j <- 1 until 10){

      println("i="+ i +", j="+j)

      count += 1

    }

    println(count);

    

    //例子: 打印小九九

    for(i <- 1 until 10 ;j <- 1 until 10){

      if(i>=j){

       print(i +" * " + j + " = "+ i*j+" ")

        

      }

      if(i==j ){

        println()

      }

      

    }

 

  1. for循环中能够加条件判断,能够使用分号隔开,也能够不使用分号

    //能够在for循环中加入条件判断

    for(i<- 1 to 10 ;if (i%2) == 0 ;if (i == 4) ){

      println(i)

}

 

  1. scala中不能使用count++count只能使用count = count+1 count += 1
  2. for循环用yield 关键字返回一个集合
  3. while循环,while(){}do {}while()

    

    //for中的符合条件的元素经过yield关键字返回成一个集合

    val list = for(i <- 1 to 10  ; if(i > 5 )) yield i 

    for( w <- list ){

      println(w)

}

 

   /**

     * while 循环

     */

    var index = 0 

    while(index < 100 ){

     println(""+index+"while 循环")

      index += 1 

    }

    index = 0 

    do{

     index +=1 

     println(""+index+"do while 循环")

}while(index <100 )

 

4)、Scala函数

Scala函数的定义

  • 有参函数
  • 无参函数

 

def fun (a: Int , b: Int ) : Unit = {

   println(a+b)

 }

fun(1,1)

    

def fun1 (a : Int , b : Int)= a+b

    println(fun1(1,2))  

注意点:

函数定义语法 def来定义

l 能够定义传入的参数,要指定传入参数的类型

l 方法能够写返回值的类型也能够不写,会自动推断,有时候不能省略,必须写,好比在递归函数中或者函数的返回值是函数类型的时候。

l scala中函数有返回值时,能够写return,也能够不写return,会把函数中最后一行当作结果返回。当写return时,必需要写函数的返回值。

若是返回值能够一行搞定,能够将{}省略不写

传递给方法的参数能够在方法中使用,而且scala规定方法的传过来的参数为val的,不是var的。

若是去掉方法体前面的等号,那么这个方法返回类型一定是Unit的。这种说法不管方法体里面什么逻辑都成立,scala能够把任意类型转换为Unit.假设,里面的逻辑最后返回了一个string,那么这个返回值会被转换成Unit,而且值会被丢弃。

递归函数

  /**

     * 递归函数

     * 5的阶乘

     */

    def fun2(num :Int) :Int= {

      if(num ==1)

        num

      else 

        num * fun2(num-1)

    }

    print(fun2(5))

包含参数默认值的函数

默认值的函数中,若是传入的参数个数与函数定义相同,则传入的数值会覆盖默认值

若是不想覆盖默认值,传入的参数个数小于定义的函数的参数,则须要指定参数名称

   /**

     * 包含默认参数值的函数

     * 注意:

     * 1.默认值的函数中,若是传入的参数个数与函数定义相同,则传入的数值会覆盖默认值

     * 2.若是不想覆盖默认值,传入的参数个数小于定义的函数的参数,则须要指定参数名称

     */

    def fun3(a :Int = 10,b:Int) = {

      println(a+b)

    }

    fun3(b=2)

 

可变参数个数的函数

l 多个参数用逗号分开

    /**

     * 可变参数个数的函数

     * 注意:多个参数逗号分开

     */

    def fun4(elements :Int*)={

      var sum = 0;

      for(elem <- elements){

        sum += elem

      }

      sum

    }

    println(fun4(1,2,3,4))

匿名函数

  1. 有参匿名函数
  2. 无参匿名函数
  3. 有返回值的匿名函数

能够将匿名函数返回给val定义的值

l 匿名函数不能显式声明函数的返回类型

    /**

     * 匿名函数

     * 1.有参数匿名函数

     * 2.无参数匿名函数

     * 3.有返回值的匿名函数

     * 注意:

     * 能够将匿名函数返回给定义的一个变量

     */

    //有参数匿名函数

    val value1 = (a : Int) => {

      println(a)

    }

    value1(1)

    //无参数匿名函数

    val value2 = ()=>{

      println("我爱尚学堂")

    }

    value2()

    //有返回值的匿名函数

    val value3 = (a:Int,b:Int) =>{

      a+b

    }

    println(value3(4,4)) 

嵌套函数

    /**

     * 嵌套函数

     * 例如:嵌套函数求5的阶乘

     */

    def fun5(num:Int)={

      def fun6(a:Int,b:Int):Int={

        if(a == 1){

          b

        }else{

          fun6(a-1,a*b)

        }

      }

      fun6(num,1)

    }

    println(fun5(5))

 

偏应用函数

偏应用函数是一种表达式,不须要提供函数须要的全部参数,只须要提供部分,或不提供所需参数。

    /**

     * 偏应用函数

     */

    def log(date :Date, s :String)= {

      println("date is "+ date +",log is "+ s)

    }

    

    val date = new Date()

    log(date ,"log1")

    log(date ,"log2")

    log(date ,"log3")

    

    //想要调用log,以上变化的是第二个参数,能够用偏应用函数处理

    val logWithDate = log(date,_:String)

    logWithDate("log11")

    logWithDate("log22")

    logWithDate("log33")

高阶函数

函数的参数是函数,或者函数的返回类型是函数,或者函数的参数和函数的返回类型是函数的函数。

l 函数的参数是函数

l 函数的返回是函数

l 函数的参数和函数的返回是函数

    /**

     * 高阶函数

     * 函数的参数是函数 或者函数的返回是函数 或者函数的参数和返回都是函数

     */

    

    //函数的参数是函数

    def hightFun(f : (Int,Int) =>Int, a:Int ) : Int = {

      f(a,100)

    }

    def f(v1 :Int,v2: Int):Int  = {

      v1+v2

    }

    

    println(hightFun(f, 1))

    

    //函数的返回是函数

    //12,3,4相加

    def hightFun2(a : Int,b:Int) : (Int,Int)=>Int = {

      def f2 (v1: Int,v2:Int) :Int = {

        v1+v2+a+b

      }

      f2

    }

    println(hightFun2(1,2)(3,4))

    

    //函数的参数是函数,函数的返回是函数

    def hightFun3(f : (Int ,Int) => Int) : (Int,Int) => Int = {

      f

    }

    println(hightFun3(f)(100,200))

    println(hightFun3((a,b) =>{a+b})(200,200))

    //以上这句话还能够写成这样

    //若是函数的参数在方法体中只使用了一次 那么能够写成_表示

    println(hightFun3(_+_)(200,200))

柯里化函数

l 能够理解为高阶函数的简化

   /**

     * 柯里化函数

     */

    def fun7(a :Int,b:Int)(c:Int,d:Int) = {

      a+b+c+d

    }

    println(fun7(1,2)(3,4))

5)、Scala字符串

  1. String
  2. StringBuilder 可变
  3. string操做方法举例
  • 比较:equals
  • 比较忽略大小写:equalsIgnoreCase
  • indexOf:若是字符串中有传入的assci码对应的值,返回下标

    /**

     * String && StringBuilder

     */

    val str = "abcd"

    val str1 = "ABCD"

    

    println(str.indexOf(97))

    println(str.indexOf("b"))

 

    println(str==str1)

    /**

     * compareToIgnoreCase

     *

     * 若是参数字符串等于此字符串,则返回值 0

     * 若是此字符串小于字符串参数,则返回一个小于 0 的值;

     * 若是此字符串大于字符串参数,则返回一个大于 0 的值。

     *

     */

    println(str.compareToIgnoreCase(str1))

    

    val strBuilder = new StringBuilder

    strBuilder.append("abc")

//    strBuilder.+('d')

    strBuilder+ 'd'

//    strBuilder.++=("efg")

    strBuilder++= "efg" 

//    strBuilder.+=('h')

    strBuilder+= 'h' 

    strBuilder.append(1.0)

    strBuilder.append(18f)

    println(strBuilder)

    

String方法:(见附件)

 

 

6)、集合

数组

  1. 建立数组
  • new Array[Int](10)

赋值:arr(0) = xxx

  • Array[String](“s1”,”s2”,”s3”)
  1. 数组遍历
  • for
  • foreach
  1. 建立一维数组和二维数组
  2. 数组中方法举例
  • Array.concate:合并数组
  • Array.fill(5)(“bjsxt”):建立初始值的定长数组

建立两种方式:

   /**

     * 建立数组两种方式:

     * 1.new Array[String](3)

     * 2.直接Array

     */

    

    //建立类型为Int 长度为3的数组

    val arr1 = new Array[Int](3)

    //建立String 类型的数组,直接赋值

    val arr2 = Array[String]("s100","s200","s300")

    //赋值

    arr1(0) = 100

    arr1(1) = 200

    arr1(2) = 300

遍历两种方式:

    /**

     * 遍历两种方式

     */

    for(i <- arr1){

       println(i)

    }

    arr1.foreach(i => {

      println(i)

    })

    

    for(s <- arr2){

      println(s)

    }

    arr2.foreach {

      x => println(x)

    }

    

建立二维数组

    /**

     * 建立二维数组和遍历

     */

    val arr3 = new Array[Array[String]](3)

    arr3(0)=Array("1","2","3")

    arr3(1)=Array("4","5","6")

    arr3(2)=Array("7","8","9")

    for(i <- 0 until arr3.length){

      for(j <- 0 until arr3(i).length){

        print(arr3(i)(j)+" ")

      }

      println()

    }

    

    var count = 0

    for(arr <- arr3 ;i <- arr){

      if(count%3 == 0){

        println()

      }

      print(i+" ")

      count +=1 

    }

    

    arr3.foreach { arr  => {

      arr.foreach { println }

    }}

    

    

    val arr4 = Array[Array[Int]](Array(1,2,3),Array(4,5,6))

    arr4.foreach { arr => {

      arr.foreach(i => {

        println(i)

      })

    }}

    println("-------")

    for(arr <- arr4;i <- arr){

      println(i)

    }

    

    

数组中的方法:

 

 

list

  1. 建立list

val list = List(1,2,3,4)

  • Nil长度为0list
  1. list遍历

foreach for

  1. list方法举例
  • filter:过滤元素
  • count:计算符合条件的元素个数
  • map:对元素操做
  • flatmap :压扁扁平,mapflat

 

 

    //建立

    val list = List(1,2,3,4,5)

    //遍历

    list.foreach { x => println(x)}

//    list.foreach { println}

    //filter

    val list1  = list.filter { x => x>3 }

    list1.foreach { println}

    

    //count

    val value = list1.count { x => x>3 }

    println(value)

    

    //map

    val nameList = List(

     "hello bjsxt",

     "hello xasxt",

     "hello shsxt"

        )

    val mapResult:List[Array[String]] = nameList.map{ x => x.split(" ") }

    mapResult.foreach{println}    

    

    //flatmap

    val flatMapResult : List[String] = nameList.flatMap{ x => x.split(" ") }

    flatMapResult.foreach { println }

  1. list方法总结
 

set

  1. 建立set

注意:set集合会自动去重

  1. set遍历

foreachfor

  1. set方法举例
  • 交集:intersect ,&
  • 差集: diff ,&~
  • 子集:subsetOf
  • 最大:max
  • 最小:min
  • 转成数组,toList
  • 转成字符串:mkString(“~”)
  1. set方法总结

    //建立

    val set1 = Set(1,2,3,4,4)

    val set2 = Set(1,2,5)

    //遍历

    //注意:set会自动去重

    set1.foreach { println}

   for(s <- set1){

      println(s)

    }

    println("*******")

   /**

    * 方法举例

    */

    

   //交集

   val set3 = set1.intersect(set2)

   set3.foreach{println}

   val set4 = set1.&(set2)

   set4.foreach{println}

   println("*******")

   //差集

   set1.diff(set2).foreach { println }

   set1.&~(set2).foreach { println }

   //子集

   set1.subsetOf(set2)

   

   //最大值

   println(set1.max)

   //最小值

   println(set1.min)

   println("****")

   

   //转成数组,list

   set1.toArray.foreach{println}

   println("****")

   set1.toList.foreach{println}

   

   //mkString

   println(set1.mkString)

   println(set1.mkString("\t"))

     

set方法总结

 

 

map

  1. map建立
  • Map1 –>”bjsxt’)
  • Map((1,”bjsxt”))

注意:建立map时,相同的key被后面的相同的key顶替掉,只保留一个

    val map = Map(

      "1" -> "bjsxt",

      2 -> "shsxt",

      (3,"xasxt")

    )

 

  1. 获取map的值
  • map.get(“1”).get
  • map.get(100).getOrElse(“no value”):若是map中没有对应项,赋值为getOrElse传的值。

    //获取值

    println(map.get("1").get)

    val result = map.get(8).getOrElse("no value")

    println(result)

  1. 遍历map
  • for,foreach

    //map遍历

    for(x <- map){

      println("====key:"+x._1+",value:"+x._2)

    }

    map.foreach(f => {

      println("key:"+ f._1+" ,value:"+f._2)

    })

  1. 遍历key
  • map.keys

    //遍历key

    val keyIterable = map.keys

    keyIterable.foreach { key => {

      println("key:"+key+", value:"+map.get(key).get)

    } }

    println("---------")

    

  1. 遍历value
  • map.values

    //遍历value

    val valueIterable = map.values

    valueIterable.foreach { value => {

      println("value: "+ value)

    } }

    

  1. 合并map
  • ++  例:map1.++(map2)  --map1中加入map2
  • ++:  例:map1.++:(map2) –map2中加入map1

注意:合并map会将map中的相同keyvalue替换

    //合并map

    val map1 = Map(

      (1,"a"),    

      (2,"b"),    

      (3,"c")    

    )

    val map2 = Map(

      (1,"aa"),

      (2,"bb"),

      (2,90),

      (4,22),

      (4,"dd")

    )

    map1.++:(map2).foreach(println)

    

  1. map中的方法举例
  • filter:过滤,留下符合条件的记录
  • count:统计符合条件的记录数
  • containsmap中是否包含某个key
  • exist:符合条件的记录存在不存在

    /**

     * map方法

     */

    //count

    val countResult  = map.count(p => {

      p._2.equals("shsxt")

    })

    println(countResult)

    

    //filter

    map.filter(_._2.equals("shsxt")).foreach(println)

    

    //contains

    println(map.contains(2))

   

    //exist

    println(map.exists(f =>{

      f._2.equals("xasxt")

      

    }))

    

Map方法总结

 

元组

  1. 元组定义

与列表同样,与列表不一样的是元组能够包含不一样类型的元素。元组的值是经过将单个的值包含在圆括号中构成的。

  1. 建立元组与取值
  • val  tuple = new Tuple1) 能够使用new
  • val tuple2  = Tuple1,2) 能够不使用new,也能够直接写成val tuple3 =1,2,3
  • 取值用”._XX” 能够获取元组中的值

注意:tuple最多支持22个参数

    //建立,最多支持22

    val tuple = new Tuple1(1)

    val tuple2 = Tuple2("zhangsan",2)

    val tuple3 = Tuple3(1,2,3)

    val tuple4 = (1,2,3,4)

    val tuple18 = Tuple18(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18)

    val tuple22 = new Tuple22(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)

 

    //使用

    println(tuple2._1 + "\t"+tuple2._2)

    val t = Tuple2((1,2),("zhangsan","lisi"))

    println(t._1._2)

 

  1. 元组的遍历

tuple.productIterator获得迭代器,进而遍历

    //遍历

    val tupleIterator = tuple22.productIterator

    while(tupleIterator.hasNext){

      println(tupleIterator.next())

    }

  1. swap,toString方法

注意:swap元素翻转,只针对二元组

    /**

     * 方法

     */

    //翻转,只针对二元组

    println(tuple2.swap)

    

    //toString

    println(tuple3.toString())

 

7)、trait特性

  1. 概念理解

Scala Trait(特征) 至关于 Java 的接口,实际上它比接口还功能强大。

与接口不一样的是,它还能够定义属性和方法的实现。

通常状况下Scala的类能够继承多个Trait,从结果来看就是实现了多重继承。Trait(特征) 定义的方式与类相似,但它使用的关键字是 trait

  1. 举例:trait中带属性带方法实现

注意:

  • 继承的多个trait中若是有同名的方法和属性,必需要在类中使用“override”从新定义。
  • trait中不能够传参数

trait Read {

  val readType = "Read"

  val gender = "m"

  def read(name:String){

println(name+" is reading")

  }

}

 

trait Listen {

  val listenType = "Listen"

  val gender = "m"

  def listen(name:String){

println(name + " is listenning")

  }

}

 

class Person() extends Read with Listen{

  override val gender = "f"

}

 

object test {

  def main(args: Array[String]): Unit = {

    val person = new Person()

    person.read("zhangsan")

    person.listen("lisi")

    println(person.listenType)

    println(person.readType)

    println(person.gender)

    

  }

}

 

  1. 举例:trait中带方法不实现

object Lesson_Trait2 {

  def main(args: Array[String]): Unit = {

    val p1 = new Point(1,2)

    val p2 = new Point(1,3)

    println(p1.isEqule(p2))

    println(p1.isNotEqule(p2))

  }

}

 

trait Equle{

  def isEqule(x:Any) :Boolean

  def isNotEqule(x : Any)  = {

    !isEqule(x)

  }

}

 

class Point(x:Int, y:Int) extends Equle {

  val xx = x

  val yy = y

 

  def isEqule(p:Any) = {

    p.isInstanceOf[Point] && p.asInstanceOf[Point].xx==xx

  }

  

}

8)、模式匹配match

  1. 概念理解:

Scala 提供了强大的模式匹配机制,应用也很是普遍。

一个模式匹配包含了一系列备选项,每一个都开始于关键字 case

每一个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。

  1. 代码及注意点
  • 模式匹配不只能够匹配值还能够匹配类型
  • 从上到下顺序匹配,若是匹配到则再也不往下匹配
  • 都匹配不上时,会匹配到case _ ,至关于default
  • match 的最外面的”{ }”能够去掉当作一个语句

object Lesson_Match {

  def main(args: Array[String]): Unit = {

    val tuple = Tuple6(1,2,3f,4,"abc",55d)

    val tupleIterator = tuple.productIterator

    while(tupleIterator.hasNext){

      matchTest(tupleIterator.next())

    }

    

  }

  /**

   * 注意点:

   * 1.模式匹配不只能够匹配值,还能够匹配类型

   * 2.模式匹配中,若是匹配到对应的类型或值,就再也不继续往下匹配

   * 3.模式匹配中,都匹配不上时,会匹配到 case _ ,至关于default

   */

  def matchTest(x:Any) ={

    x match {

      case x:Int=> println("type is Int")

      case 1 => println("result is 1")

      case 2 => println("result is 2")

      case 3=> println("result is 3")

      case 4 => println("result is 4")

      case x:String => println("type is String")

//      case x :Double => println("type is Double")

      case _ => println("no match")

    }

  }

  

}

9)、样例类(case classes)

  1. 概念理解

使用了case关键字的类定义就是样例类(case classes),样例类是种特殊的类。实现了类构造参数的getter方法(构造参数默认被声明为val),当构造参数是声明为var类型的,它将帮你实现settergetter方法。

  • 样例类默认帮你实现了toString,equalscopyhashCode等方法。
  • 样例类能够new, 也能够不用new
  1. 例子:结合模式匹配的代码

case class Person1(name:String,age:Int)

 

object Lesson_CaseClass {

  def main(args: Array[String]): Unit = {

    val p1 = new Person1("zhangsan",10)

    val p2 = Person1("lisi",20)

    val p3 = Person1("wangwu",30)

    

    val list = List(p1,p2,p3)

    list.foreach { x => {

      x match {

        case Person1("zhangsan",10) => println("zhangsan")

        case Person1("lisi",20) => println("lisi")

        case _ => println("no match")

      }

    } }

    

  }

}

10)、Actor Model

  1. 概念理解

Actor Model是用来编写并行计算或分布式系统的高层次抽象(相似java中的Thread)让程序员没必要为多线程模式下共享锁而烦恼,被用在Erlang 语言上, 高可用性99.9999999 % 一年只有31ms 宕机Actors将状态和行为封装在一个轻量的进程/线程中,可是不和其余Actors分享状态,每一个Actors有本身的世界观,当须要和其余Actors交互时,经过发送事件和消息,发送是异步的,非堵塞的(fire-andforget),发送消息后没必要等另外Actors回复,也没必要暂停,每一个Actors有本身的消息队列,进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,能够创建性能很好的事件驱动系统。

Actor的特征:

  • ActorModel是消息传递模型,基本特征就是消息传递
  • 消息发送是异步的,非阻塞的
  • 消息一旦发送成功,不能修改
  • Actor之间传递时,本身决定决定去检查消息,而不是一直等待,是异步非阻塞的

什么是Akka

Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Java Scala Actor 模型应用,底层实现就是Actor,Akka是一个开发库和运行环境,能够用于构建高并发、分布式、可容错、事件驱动的基于JVM的应用。使构建高并发的分布式应用更加容易。

spark1.6版本以前,spark分布式节点之间的消息传递使用的就是Akka,底层也就是actor实现的。1.6以后使用的netty传输。

  1. 例:Actor简单例子发送接收消息

import scala.actors.Actor

 

class myActor extends Actor{

  

  def act(){

    while(true){

      receive {

        case x:String => println("save String ="+ x)

        case x:Int => println("save Int")

        case _ => println("save default")

      }

    }

  }

}

 

object Lesson_Actor {

  def main(args: Array[String]): Unit = {

    

    //建立actor的消息接收和传递

    val actor =new myActor()

    //启动

    actor.start()

    //发送消息写法

    actor ! "i love you !"

 

  }

}

 

  1. 例:ActorActor之间通讯

case class Message(actor:Actor,msg:Any)

 

class Actor1 extends Actor{

  def act(){

    while(true){

      receive{

        case  msg :Message => {

          println("i sava msg! = "+ msg.msg)

          

          msg.actor!"i love you too !"

          }

        case msg :String => println(msg)

        case  _ => println("default msg!")

      }

    }

  }

}

 

class Actor2(actor :Actor) extends Actor{

  actor ! Message(this,"i love you !")

def act(){

while(true){

receive{

   case msg :String => {

     if(msg.equals("i love you too !")){

       println(msg)

      actor! "could we have a date !"

     }

   }

   case  _ => println("default msg!")

}

}

}

}

 

object Lesson_Actor2 {

  def main(args: Array[String]): Unit = {

    val actor1 = new Actor1()

    actor1.start()

    val actor2 = new Actor2(actor1)

    actor2.start()

  }

}

 

11)、WordCount

import org.apache.spark.SparkConf

import org.apache.spark.SparkContext

import org.apache.spark.rdd.RDD

import org.apache.spark.rdd.RDD.rddToPairRDDFunctions

 

object WordCount {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf()

    conf.setMaster("local").setAppName("WC")

    val sc = new SparkContext(conf)

    val lines :RDD[String] = sc.textFile("./words.txt")

    val word :RDD[String]  = lines.flatMap{lines => {

      lines.split(" ")

    }}

    val pairs : RDD[(String,Int)] = word.map{ x => (x,1) }

    val result = pairs.reduceByKey{(a,b)=> {a+b}}

    result.sortBy(_._2,false).foreach(println)

    

    //简化写法

    lines.flatMap { _.split(" ")}.map { (_,1)}.reduceByKey(_+_).foreach(println)

    

  }

}

2)、Spark

1)、Spark初始

什么是Spark

Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。SparkUC Berkeley AMP lab (加州大学伯克利分校的AMP实验室)所开源的类Hadoop MapReduce的通用并行计算框架,Spark拥有Hadoop MapReduce所具备的优势;但不一样于MapReduce的是Job中间输出结果能够保存在内存中,从而再也不须要读写HDFS,所以Spark能更好地适用于数据挖掘与机器学习等须要迭代的MapReduce的算法。

SparkScala编写,方便快速编程。

整体技术栈讲解

 

Spark演变历史

SparkMapReduce的区别

¬ 都是分布式计算框架,Spark基于内存,MR基于HDFSSpark处理数据的能力通常是MR的十倍以上,Spark中除了基于内存计算外,还有DAG有向无环图来切分任务的执行前后顺序。

Spark运行模式

  • Local

多用于本地测试,如在eclipseidea中写程序测试等。

  • Standalone

StandaloneSpark自带的一个资源调度框架,它支持彻底分布式。

  • Yarn

Hadoop生态圈里面的一个资源调度框架,Spark也是能够基于Yarn来计算的。

  • Mesos

资源调度框架。

¬ 要基于Yarn来进行资源调度,必须实现AppalicationMaster接口,Spark实现了这个接口,因此能够基于Yarn

2)、SparkCore

RDD

u 概念

RDD(Resilient Distributed Dateset),弹性分布式数据集。

u RDD的五大特性:

  1. RDD是由一系列的partition组成的。
  2. 函数是做用在每个partitionsplit)上的。
  3. RDD之间有一系列的依赖关系。
  4. 分区器是做用在K,V格式的RDD上。
  5. RDD提供一系列最佳的计算位置。

u RDD理解图:

 

u 注意:

¬ textFile方法底层封装的是读取MR读取文件的方式,读取文件以前先split,默认split大小是一个block大小。

¬ RDD实际上不存储数据,这里方便理解,暂时理解为存储数据。

¬ 什么是K,V格式的RDD?

  • 若是RDD里面存储的数据都是二元组对象,那么这个RDD咱们就叫作K,V格式的RDD

¬ 哪里体现RDD的弹性(容错)?

  • partition数量,大小没有限制,体现了RDD的弹性。
  • RDD之间依赖关系,能够基于上一个RDD从新计算出RDD

¬ 哪里体现RDD的分布式?

  • RDD是由Partition组成,partition是分布在不一样节点上的。

¬ RDD提供计算最佳位置,体现了数据本地化。体现了大数据中“计算移动数据不移动”的理念。

Spark任务执行原理

 

以上图中有四个机器节点,DriverWorker是启动在节点上的进程,运行在JVM中的进程。

  • Driver与集群节点之间有频繁的通讯。
  • Driver负责任务(tasks)的分发和结果的回收。任务的调度。若是task的计算结果很是大就不要回收了。会形成oom
  • WorkerStandalone资源调度框架里面资源管理的从节点。也是JVM进程。
  • MasterStandalone资源调度框架里面资源管理的主节点。也是JVM进程。

 

Spark代码流程

  1. 建立SparkConf对象
  • 能够设置Application name
  • 能够设置运行模式及资源需求。
  1. 建立SparkContext对象
  2. 基于Spark的上下文建立一个RDD,对RDD进行处理。
  3. 应用程序中要有Action类算子来触发Transformation类算子执行。
  4. 关闭Spark上下文对象SparkContext

Transformations转换算子

u 概念:

Transformations类算子是一类算子(函数)叫作转换算子,如map,flatMap,reduceByKey等。Transformations算子是延迟执行,也叫懒加载执行。

u Transformation类算子:

  • filter

过滤符合条件的记录数,true保留,false过滤掉。

 

  • map

将一个RDD中的每一个数据项,经过map中的函数映射变为一个新的元素。

特色:输入一条,输出一条数据。

 

  • flatMap

mapflat。与map相似,每一个输入项能够映射为0到多个输出项。

 

  • sample

随机抽样算子,根据传进去的小数按比例进行又放回或者无放回的抽样。

 

  • reduceByKey

将相同的Key根据相应的逻辑进行处理。

  • sortByKey/sortBy

做用在K,V格式的RDD上,对key进行升序或者降序排序。

Action行动算子

u 概念:

Action类算子也是一类算子(函数)叫作行动算子,如foreach,collectcount等。Transformations类算子是延迟执行,Action类算子是触发执行。一个application应用程序中有几个Action类算子执行,就有几个job运行。

u Action类算子

  • count

返回数据集中的元素数。会在结果计算完成后回收到Driver端。

 

  • take(n)

返回一个包含数据集前n个元素的集合。

 

  • first

first=take(1),返回数据集中的第一个元素。

 

  • foreach

循环遍历数据集中的每一个元素,运行相应的逻辑。

 

  • collect

将计算结果回收到Driver端。

 

ü 思考:一千万条数据量的文件,过滤掉出现次数多的记录,而且其他记录按照出现次数降序排序。

文件:

 

代码:

 

控制算子

u 概念:

控制算子有三种,cache,persist,checkpoint,以上算子均可以将RDD持久化,持久化的单位是partitioncachepersist都是懒执行的。必须有一个action类算子触发执行。checkpoint算子不只能将RDD持久化到磁盘,还能切断RDD之间的依赖关系。

u cache

默认将RDD的数据持久化到内存中。cache是懒执行。

¬ 注意:chche () = persist()=persist(StorageLevel.Memory_Only)

  • 测试cache文件:

文件:见“NASA_access_log_Aug95”文件。

测试代码:

 SparkConf conf = new SparkConf();

 conf.setMaster("local").setAppName("CacheTest");

 JavaSparkContext jsc = new JavaSparkContext(conf);

 JavaRDD<String> lines = jsc.textFile("./NASA_access_log_Aug95");

 

 lines = lines.cache();

 long startTime = System.currentTimeMillis();

 long count = lines.count();

 long endTime = System.currentTimeMillis();

 System.out.println(""+count+ "条数据,"+"初始化时间+cache时间+计算时间="+

          (endTime-startTime));

 

 long countStartTime = System.currentTimeMillis();

 long countrResult = lines.count();

 long countEndTime = System.currentTimeMillis();

 System.out.println(""+countrResult+ "条数据,"+"计算时间="+ (countEndTime-

           countStartTime));

 

 jsc.stop();

 

u persist

能够指定持久化的级别。最经常使用的是MEMORY_ONLYMEMORY_AND_DISK”_2”表示有副本数。

持久化级别以下:

 

¬ cachepersist的注意事项:

  1. cachepersist都是懒执行,必须有一个action类算子触发执行。
  2. cachepersist算子的返回值能够赋值给一个变量,在其余job中直接使用这个变量就是使用持久化的数据了。持久化的单位是partition
  3. cachepersist算子后不能当即紧跟action算子。

错误:rdd.cache().count() 返回的不是持久化的RDD,而是一个数值了。

u checkpoint

checkpointRDD持久化到磁盘,还能够切断RDD之间的依赖关系。

  • checkpoint 的执行原理:
  1. RDDjob执行完毕后,会从finalRDD从后往前回溯。
  2. 当回溯到某一个RDD调用了checkpoint方法,会对当前的RDD作一个标记。
  3. Spark框架会自动启动一个新的job,从新计算这个RDD的数据,将数据持久化到HDFS上。
  • 优化:对RDD执行checkpoint以前,最好对这个RDD先执行cache,这样新启动的job只须要将内存中的数据拷贝到HDFS上就能够,省去了从新计算这一步。
  • 使用:

 SparkConf conf = new SparkConf();

 conf.setMaster("local").setAppName("checkpoint");

 JavaSparkContext sc = new JavaSparkContext(conf);

 sc.setCheckpointDir("./checkpoint");

 JavaRDD<Integer> parallelize = sc.parallelize(Arrays.asList(1,2,3));

 parallelize.checkpoint();

 parallelize.count();

 sc.stop();

 

3)、集群搭建以及测试

搭建

  • Standalone

1).下载安装包,解压

 

 

2).更名

 

3).进入安装包的conf目录下,修改slaves.template文件,添加从节点。保存。

 

 

4).修改spark-env.sh

SPARK_MASTER_IP:masterip

SPARK_MASTER_PORT:提交任务的端口,默认是7077

SPARK_WORKER_CORES:每一个worker从节点可以支配的core的个数

SPARK_WORKER_MEMORY:每一个worker从节点可以支配的内存数

 

5).同步到其余节点上

 

 

6).启动集群

进入sbin目录下,执行当前目录下的./start-all.sh

 

7).搭建客户端

spark安装包原封不动的拷贝到一个新的节点上,而后,在新的节点上提交任务便可。

注意:

¬ 8080Spark WEBUI界面的端口,7077Spark任务提交的端口。

¬ 修改masterWEBUI端口:

ü 修改start-master.sh便可。

 

ü 也能够在Master节点上导入临时环境变量,只是做用于以后的程序,重启就无效了。

 

删除临时环境变量:

 

  • yarn

1). 123457步同standalone

2).在客户端中配置:

 

测试

PI案例:

 

Standalone提交命令:

./spark-submit

--master spark://node1:7077

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar 10000

 

YARN提交命令:

./spark-submit

--master yarn

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar 10000

4)、两种提交任务方式

Standalone模式两种提交任务方式

  1. Standalone-client提交任务方式
  • 提交命令

./spark-submit

--master spark://node1:7077

--class org.apache.spark.examples.SparkPi

../lib/spark-examples-1.6.0-hadoop2.6.0.jar

1000

或者

./spark-submit

--master spark://node1:7077

--deploy-mode client

--class org.apache.spark.examples.SparkPi

../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

  • 执行原理图解

 

  • 执行流程
  1. client模式提交任务后,会在客户端启动Driver进程。
  2. Driver会向Master申请启动Application启动的资源。
  3. 资源申请成功,Driver端将task发送到worker端执行。
  4. workertask执行结果返回到Driver端。
  • 总结

client模式适用于测试调试程序。Driver进程是在客户端启动的,这里的客户端就是指提交应用程序的当前节点。Driver端能够看到task执行的状况。生产环境下不能使用client模式,是由于:假设要提交100application到集群运行,Driver每次都会在client端启动,那么就会致使客户端100次网卡流量暴增的问题。

  1. Standalone-cluster提交任务方式
  • 提交命令

./spark-submit

--master spark://node1:7077

--deploy-mode cluster

--class org.apache.spark.examples.SparkPi

../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

  • 执行原理图解

 

  • 执行流程
  1. cluster模式提交应用程序后,会向Master请求启动Driver.
  2. Master接受请求,随机在集群一台节点启动Driver进程。
  3. Driver启动后为当前的应用程序申请资源。
  4. Driver端发送taskworker节点上执行。
  5. worker将执行状况和执行结果返回给Driver端。
  • 总结

Driver进程是在集群某一台Worker上启动的,在客户端是没法查看task的执行状况的。假设要提交100application到集群运行,每次Driver会随机在集群中某一台Worker上启动,那么这100次网卡流量暴增的问题就散布在集群上。

¬ 总结Standalone两种方式提交任务,Driver与集群的通讯包括:

1. Driver负责应用程序资源的申请

2. 任务的分发。

3. 结果的回收。

4. 监控task执行状况。

Yarn模式两种提交任务方式

  1. yarn-client提交任务方式
  • 提交命令

./spark-submit

--master yarn

 --class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

或者

./spark-submit

--master yarn–client

 --class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

或者

./spark-submit

--master yarn

--deploy-mode  client

 --class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

 

  • 执行原理图解

 

  • 执行流程
  1. 客户端提交一个Application,在客户端启动一个Driver进程。
  2. 应用程序启动后会向RS(ResourceManager)发送请求,启动AM(ApplicationMaster)的资源。
  3. RS收到请求,随机选择一台NM(NodeManager)启动AM这里的NM至关于Standalone中的Worker节点。
  4. AM启动后,会向RS请求一批container资源,用于启动Executor.
  5. RS会找到一批NM返回给AM,用于启动Executor
  6. AM会向NM发送命令启动Executor
  7. Executor启动后,会反向注册给DriverDriver发送taskExecutor,执行状况和结果返回给Driver端。
  • 总结

Yarn-client模式一样是适用于测试,由于Driver运行在本地,Driver会与yarn集群中的Executor进行大量的通讯,会形成客户机网卡流量的大量增长.

¬ ApplicationMaster的做用:

  1. 为当前的Application申请资源
  2. NameNode发送消息启动Executor

注意:ApplicationMasterlaunchExecutor和申请资源的功能,并无做业调度的功能。

  1. yarn-cluster提交任务方式
  • 提交命令

./spark-submit

--master yarn

--deploy-mode cluster 

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

或者

./spark-submit

--master yarn-cluster

--class org.apache.spark.examples.SparkPi ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

100

 

  • 执行原理图解

 

  • 执行流程
  1. 客户机提交Application应用程序,发送请求到RS(ResourceManager),请求启动AM(ApplicationMaster)
  2. RS收到请求后随机在一台NM(NodeManager)上启动AM(至关于Driver端)。
  3. AM启动,AM发送请求到RS,请求一批container用于启动Executor
  4. RS返回一批NM节点给AM
  5. AM链接到NM,发送请求到NM启动Executor
  6. Executor反向注册到AM所在的节点的DriverDriver发送taskExecutor
  • 总结

Yarn-Cluster主要用于生产环境中,由于Driver运行在Yarn集群中某一台nodeManager中,每次提交任务的Driver所在的机器都是随机的,不会产生某一台机器网卡流量激增的现象,缺点是任务提交后不能看到日志。只能经过yarn查看日志。

¬ ApplicationMaster的做用:

  1. 为当前的Application申请资源
  2. NameNode发送消息启动Excutor
  3. 任务调度。

¬ 中止集群任务命令:yarn application -kill applicationID

5)、补充部分算子

transformation

  • join,leftOuterJoin,rightOuterJoin,fullOuterJoin

做用在K,V格式的RDD上。根据K进行链接,对(K,Vjoin(K,W)返回(K,(V,W)

¬ join后的分区数与父RDD分区数多的那一个相同。

  • union

合并两个数据集。两个数据集的类型要一致。

¬ 返回新的RDD的分区数是合并RDD分区数的总和。

  • intersection

取两个数据集的交集

  • subtract

取两个数据集的差集

  • mapPartition

map相似,遍历的单位是每一个partition上的数据。

  • distinct(map+reduceByKey+map)
  • cogroup

当调用类型(K,V和(KW)的数据上时,返回一个数据集(K,(Iterable<V>,Iterable<W>))

  • mapPartitionWithIndex

相似于mapPartitions,除此以外还会携带分区的索引值。

  • repartition

增长或减小分区。会产生shuffle。(多个分区分到一个分区不会产生shuffle)

  • coalesce

coalesce经常使用来减小分区,第二个参数是减小分区的过程当中是否产生shuffle

true为产生shufflefalse不产生shuffle。默认是false

若是coalesce设置的分区数比原来的RDD的分区数还多的话,第二个参数设置为false不会起做用,若是设置成true,效果和repartition同样。repartition(numPartitions) = coalesce(numPartitions,true)

  • groupByKey

做用在KV格式的RDD上。根据Key进行分组。做用在KV,返回KIterable <V>

  • zip

将两个RDD中的元素(KV格式/KV格式变成一个KV格式的RDD,两个RDD的每一个分区元素个数必须相同。

  • zipWithIndex

该函数将RDD中的元素和这个元素在RDD中的索引号(从0开始)组合成K,V

action
  • foreachPartition

遍历的数据是每一个partition的数据。

  • countByKey

 做用到K,V格式的RDD上,根据Key计数相同Key的数据集元素。

  • countByValue

根据数据集每一个元素相同的内容来计数。返回相同内容的元素对应的条数。

  • reduce

根据聚合逻辑聚合数据集中的每一个元素。

 

6)、术语解释

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

7)、窄依赖和宽依赖

RDD之间有一系列的依赖关系,依赖关系又分为窄依赖和宽依赖。

  • 窄依赖

RDD和子RDD partition之间的关系是一对一的。或者父RDD一个partition只对应一个子RDDpartition状况下的父RDD和子RDD partition关系是多对一的。不会有shuffle的产生。

  • 宽依赖

RDD与子RDD partition之间的关系是一对多。会有shuffle的产生。

  • 宽窄依赖图理解

 

 

8)、Stage

Spark任务会根据RDD之间的依赖关系,造成一个DAG有向无环图,DAG会提交给DAGSchedulerDAGScheduler会把DAG划分相互依赖的多个stage,划分stage的依据就是RDD之间的宽窄依赖。遇到宽依赖就划分stage,每一个stage包含一个或多个task任务。而后将这些tasktaskSet的形式提交给TaskScheduler运行。

     stage是由一组并行的task组成。

  • stage切割规则

切割规则:从后往前,遇到宽依赖就切割stage

 

  • stage计算模式

pipeline管道计算模式,pipeline只是一种计算思想,模式。

 

¬ 数据一直在管道里面何时数据会落地?

  1. RDD进行持久化。
  2. shuffle write的时候。

¬ Stagetask并行度是由stage的最后一个RDD的分区数来决定的 。

¬ 如何改变RDD的分区数?

例如:reduceByKey(XXX,3),GroupByKey(4)

  • 测试验证pipeline计算模式

    val conf = new SparkConf()

    conf.setMaster("local").setAppName("pipeline");

    val sc = new SparkContext(conf)

    val rdd = sc.parallelize(Array(1,2,3,4))

    val rdd1 = rdd.map { x => {

      println("map--------"+x)

      x

    }}

    val rdd2 = rdd1.filter { x => {

      println("fliter********"+x)

      true

    } }

    rdd2.collect()

    sc.stop()

 

9)、Spark资源调度和任务调度

 

  • Spark资源调度和任务调度的流程:

启动集群后,Worker节点会向Master节点汇报资源状况,Master掌握了集群资源状况。当Spark提交一个Application后,根据RDD之间的依赖关系将Application造成一个DAG有向无环图。任务提交后,Spark会在Driver端建立两个对象:DAGSchedulerTaskSchedulerDAGScheduler是任务调度的高层调度器,是一个对象。DAGScheduler的主要做用就是将DAG根据RDD之间的宽窄依赖关系划分为一个个的Stage,而后将这些StageTaskSet的形式提交给TaskSchedulerTaskScheduler是任务调度的低层调度器,这里TaskSet其实就是一个集合,里面封装的就是一个个的task任务,也就是stage中的并行度task任务),TaskSchedule会遍历TaskSet集合,拿到每一个task后会将task发送到计算节点Executor中去执行(其实就是发送到Executor中的线程池ThreadPool去执行)。taskExecutor线程池中的运行状况会向TaskScheduler反馈,当task执行失败时,则由TaskScheduler负责重试,将task从新发送给Executor去执行,默认重试3次。若是重试3次依然失败,那么这个task所在的stage就失败了。stage失败了则由DAGScheduler来负责重试,从新发送TaskSetTaskSchdeulerStage默认重试4次。若是重试4次之后依然失败,那么这个job就失败了。job失败了,Application就失败了。

TaskScheduler不只能重试失败的task,还会重试straggling(落后,缓慢)task(也就是执行速度比其余task慢太多的task)。若是有运行缓慢的task那么TaskScheduler会启动一个新的task来与这个运行缓慢的task执行相同的处理逻辑。两个task哪一个先执行完,就以哪一个task的执行结果为准。这就是Spark的推测执行机制。在Spark中推测执行默认是关闭的。推测执行能够经过spark.speculation属性来配置。

注意:

¬ 对于ETL类型要入数据库的业务要关闭推测执行机制,这样就不会有重复的数据入库。

¬ 若是遇到数据倾斜的状况,开启推测执行则有可能致使一直会有task从新启动处理相同的逻辑,任务可能一直处于处理不完的状态。

  • 图解Spark资源调度和任务调度的流程

 

  • 粗粒度资源申请和细粒度资源申请

¬ 粗粒度资源申请(Spark

Application执行以前,将全部的资源申请完毕,当资源申请成功后,才会进行任务的调度,当全部的task执行完成后,才会释放这部分资源。

优势:Application执行以前,全部的资源都申请完毕,每个task直接使用资源就能够了,不须要task在执行前本身去申请资源,task启动就快了,task执行快了,stage执行就快了,job就快了,application执行就快了。

缺点:直到最后一个task执行完成才会释放资源,集群的资源没法充分利用。

¬ 细粒度资源申请(MapReduce

Application执行以前不须要先去申请资源,而是直接执行,让job中的每个task在执行前本身去申请资源,task执行完成就释放资源。

优势:集群的资源能够充分利用。

缺点:task本身去申请资源,task启动变慢,Application的运行就相应的变慢了。

 

10)、Spark-Submit提交参数

Options:

  • --master

 MASTER_URL, 能够是spark://host:port, mesos://host:port, yarn,  yarn-cluster,yarn-client, local

  • --deploy-mode

DEPLOY_MODE, Driver程序运行的地方,client或者cluster,默认是client

  • --class

CLASS_NAME, 主类名称,含包名

  • --jars

逗号分隔的本地JARS, Driverexecutor依赖的第三方jar

  • --files

用逗号隔开的文件列表,会放置在每一个executor工做目录

  • --conf

spark的配置属性

  • --driver-memory

Driver程序使用内存大小(例如:1000M5G),默认1024M

  • --executor-memory

每一个executor内存大小(如:1000M2G,默认1G

 

Spark standalone with cluster deploy mode only:

  • --driver-cores

Driver程序的使用core个数(默认为1,仅限于Spark standalone模式

Spark standalone or Mesos with cluster deploy mode only:

  • --supervise

失败后是否重启Driver,仅限于Spark  alone或者Mesos模式

Spark standalone and Mesos only:

  • --total-executor-cores

executor使用的总核数,仅限于SparkStandaloneSpark on Mesos模式

 

Spark standalone and YARN only:

  • --executor-cores

每一个executor使用的core数,Spark on Yarn默认为1standalone默认为worker上全部可用的core

 

YARN-only:

  • --driver-cores

driver使用的core,仅在cluster模式下,默认为1

  • --queue

QUEUE_NAME  指定资源队列的名称,默认:default

  • --num-executors

一共启动的executor数量,默认是2

11)、资源调度源码分析

  • 资源请求简单图

 

  • 资源调度Master路径:

 

路径:spark-1.6.0/core/src/main/scala/org.apache.spark/deploy/Master/Master.scala

  • 提交应用程序,submit的路径:

 

路径:spark-1.6.0/core/src/main/scala/org.apache.spark/ deploy/SparkSubmit.scala

  • 总结:
  1. Executor在集群中分散启动,有利于task计算的数据本地化。
  2. 默认状况下(提交任务的时候没有设置--executor-cores选项),每个Worker为当前的Application启动一个Executor,这个Executor会使用这个Worker的全部的cores1G内存。
  3. 若是想在Worker上启动多个Executor,提交Application的时候要加--executor-cores这个选项。
  4. 默认状况下没有设置--total-executor-cores,一个Application会使用Spark集群中全部的cores
  • 结论演示

使用Spark-submit提交任务演示。也能够使用spark-shell

  1. 默认状况每一个worker为当前的Application启动一个Executor,这个Executor使用集群中全部的cores1G内存。

./spark-submit

--master spark://node1:7077

 --class org.apache.spark.examples.SparkPi

 ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

10000

  1. workr上启动多个Executor,设置--executor-cores参数指定每一个executor使用的core数量。

./spark-submit

 --master  spark://node1:7077

 --executor-cores 1

 --class org.apache.spark.examples.SparkPi

../lib/spark-examples-1.6.0-hadoop2.6.0.jar

10000

  1. 内存不足的状况下启动core的状况。Spark启动是不只看core配置参数,也要看配置的core的内存是否够用。

./spark-submit

--master  spark://node1:7077

--executor-cores 1  

--executor-memory 3g

--class org.apache.spark.examples.SparkPi

 ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

10000

  1. --total-executor-cores集群中共使用多少cores

注意:一个进程不能让集群多个节点共同启动。

./spark-submit

--master  spark://node1:7077

--executor-cores 1  

--executor-memory 2g

--total-executor-cores 3

--class org.apache.spark.examples.SparkPi

 ../lib/spark-examples-1.6.0-hadoop2.6.0.jar

10000

12)、任务调度源码分析

  • Action算子开始分析

任务调度能够从一个Action类算子开始。由于Action类算子会触发一个job的执行。

  • 划分stage,taskSet形式提交任务

DAGScheduler 类中getMessingParentStages()方法是切割job划分stage。能够结合如下这张图来分析:

13)、二次排序

 

SparkConf sparkConf = new SparkConf()

.setMaster("local")

.setAppName("SecondarySortTest");

final JavaSparkContext sc = new JavaSparkContext(sparkConf);

 

JavaRDD<String> secondRDD = sc.textFile("secondSort.txt");

 

JavaPairRDD<SecondSortKey, String> pairSecondRDD = secondRDD.mapToPair(new PairFunction<String, SecondSortKey, String>() {

 

/**

 *

 */

private static final long serialVersionUID = 1L;

 

@Override

public Tuple2<SecondSortKey, String> call(String line) throws Exception {

           String[] splited = line.split(" ");

           int first = Integer.valueOf(splited[0]);

           int second = Integer.valueOf(splited[1]);

           SecondSortKey secondSortKey = new SecondSortKey(first,second);

           return new Tuple2<SecondSortKey, String>(secondSortKey,line);

}

});

 

pairSecondRDD.sortByKey(false).foreach(new  

               VoidFunction<Tuple2<SecondSortKey,String>>() {

 

/**

 *

 */

private static final long serialVersionUID = 1L;

 

@Override

public void call(Tuple2<SecondSortKey, String> tuple) throws Exception {

             System.out.println(tuple._2);

}

});

 

public class SecondSortKey  implements Serializable,Comparable<SecondSortKey>{

/**

 *

 */

private static final long serialVersionUID = 1L;

private int first;

private int second;

public int getFirst() {

return first;

}

public void setFirst(int first) {

this.first = first;

}

public int getSecond() {

return second;

}

public void setSecond(int second) {

this.second = second;

}

public SecondSortKey(int first, int second) {

super();

this.first = first;

this.second = second;

}

@Override

public int compareTo(SecondSortKey o1) {

if(getFirst() - o1.getFirst() ==0 ){

return getSecond() - o1.getSecond();

}else{

return getFirst() - o1.getFirst();

}

}

}

 

14)、分组取topNtopN

SparkConf conf = new SparkConf()

.setMaster("local")

.setAppName("TopOps");

JavaSparkContext sc = new JavaSparkContext(conf);

JavaRDD<String> linesRDD = sc.textFile("scores.txt");

 

JavaPairRDD<String, Integer> pairRDD = linesRDD.mapToPair(new PairFunction<String, String, Integer>() {

 

/**

 *

 */

private static final long serialVersionUID = 1L;

 

@Override

public Tuple2<String, Integer> call(String str) throws Exception {

String[] splited = str.split("\t");

String clazzName = splited[0];

Integer score = Integer.valueOf(splited[1]);

return new Tuple2<String, Integer> (clazzName,score);

        }

});

 

pairRDD.groupByKey().foreach(new 

            VoidFunction<Tuple2<String,Iterable<Integer>>>() {

 

    /**

     *

     */

    private static final long serialVersionUID = 1L;

 

    @Override

    public void call(Tuple2<String, Iterable<Integer>> tuple) throws Exception {

String clazzName = tuple._1;

Iterator<Integer> iterator = tuple._2.iterator();

 

Integer[] top3 = new Integer[3];

 

while (iterator.hasNext()) {

         Integer score = iterator.next();

 

           for (int i = 0; i < top3.length; i++) {

     if(top3[i] == null){

                top3[i] = score;

                break;

      }else if(score > top3[i]){

                 for (int j = 2; j > i; j--) {

            top3[j] = top3[j-1];

                 }

                top3[i] = score;

                break;

     }

       }

 }

 System.out.println("class Name:"+clazzName);

 for(Integer sscore : top3){

      System.out.println(sscore);

  }

}

});

15)、SparkShell的使用

  • 概念:

SparkShellSpark自带的一个快速原型开发工具,也能够说是Sparkscala REPL(Read-Eval-Print-Loop),即交互式shell。支持使用scala语言来进行Spark的交互式编程。

  • 使用:

启动Standalone集群,./start-all.sh

在客户端上启动spark-shell:

./spark-shell --master spark://node1:7077

启动hdfs,建立目录spark/test,上传文件wc.txt

启动hdfs集群:

    start-all.sh

建立目录:

    hdfs dfs -mkdir -p /spark/test

上传wc.txt

    hdfs dfs -put /root/test/wc.txt /spark/test/

wc附件:

 

运行wordcount

sc.textFile("hdfs://node1:9000/spark/test/wc.txt")

.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).foreach(println)

16)、广播变量和累加器

广播变量

  • 广播变量理解图

 

  • 广播变量使用

val conf = new SparkConf()

conf.setMaster("local").setAppName("brocast")

val sc = new SparkContext(conf)

val list = List("hello xasxt")

val broadCast = sc.broadcast(list)

val lineRDD = sc.textFile("./words.txt")

lineRDD.filter { x => broadCast.value.contains(x) }.foreach { println}

sc.stop()

  • 注意事项

¬ 能不能将一个RDD使用广播变量广播出去?

不能,由于RDD是不存储数据的。能够将RDD的结果广播出去。

¬ 广播变量只能在Driver端定义,不能在Executor端定义。

¬ Driver端能够修改广播变量的值,在Executor端没法修改广播变量的值。

累加器

  • 累加器理解图

 

  • 累加器的使用

val conf = new SparkConf()

conf.setMaster("local").setAppName("accumulator")

val sc = new SparkContext(conf)

val accumulator = sc.accumulator(0)

sc.textFile("./words.txt").foreach { x =>{accumulator.add(1)}}

println(accumulator.value)

sc.stop()

  • 注意事项

¬ 累加器在Driver端定义赋初始值,累加器只能在Driver端读取,在Excutor端更新。

3)、SparkUI

1)、SparkUI界面介绍

能够指定提交Application的名称

./spark-shell --master  spark://node1:7077 --name myapp

2)、配置historyServer

  • 临时配置,对本次提交的应用程序起做用

./spark-shell --master spark://node1:7077

--name myapp1

--conf spark.eventLog.enabled=true

 --conf spark.eventLog.dir=hdfs://node1:9000/spark/test

中止程序,在Web UiCompleted Applications对应的ApplicationID中能查看history

  • spark-default.conf配置文件中配置HistoryServer,对全部提交的Application都起做用

在客户端节点,进入../spark-1.6.0/conf/ spark-defaults.conf最后加入:

//开启记录事件日志的功能

spark.eventLog.enabled           true

//设置事件日志存储的目录

spark.eventLog.dir                    hdfs://node1:9000/spark/test

//设置HistoryServer加载事件日志的位置

spark.history.fs.logDirectory   hdfs://node1:9000/spark/test

//日志优化选项,压缩日志

spark.eventLog.compress         true

启动HistoryServer

./start-history-server.sh

访问HistoryServernode4:18080,以后全部提交的应用程序运行情况都会被记录。

4)、Master HA

1)、Master的高可用原理

Standalone集群只有一个Master,若是Master挂了就没法提交应用程序,须要给Master进行高可用配置,Master的高可用能够使用fileSystem(文件系统)zookeeper(分布式协调服务)。

fileSystem只有存储功能,能够存储Master的元数据信息,用fileSystem搭建的Master高可用,在Master失败时,须要咱们手动启动另外的备用Master,这种方式不推荐使用。

zookeeper有选举和存储功能,能够存储Master的元素据信息,使用zookeeper搭建的Master高可用,当Master挂掉时,备用的Master会自动切换,推荐使用这种方式搭建MasterHA

 

2)、Master高可用搭建

1) Spark Master节点上配置主Master,配置spark-env.sh

export SPARK_DAEMON_JAVA_OPTS="

-Dspark.deploy.recoveryMode=ZOOKEEPER

-Dspark.deploy.zookeeper.url=node3:2181,node4:2181,node5:2181

-Dspark.deploy.zookeeper.dir=/sparkmaster0821"

 

2) 发送到其余worker节点上

 

 

3) 找一台节点(非主Master节点)配置备用 Master,修改spark-env.sh配置节点上的MasterIP

 

4) 启动集群以前启动zookeeper集群:

../zkServer.sh start

5) 启动spark Standalone集群,启动备用Master

6) 打开主Master和备用Master WebUI页面,观察状态。

3)、注意点

  • 主备切换过程当中不能提交Application
  • 主备切换过程当中不影响已经在集群中运行的Application。由于Spark是粗粒度资源调度。

4)、测试验证

提交SparkPi程序,killMaster观察现象。

./spark-submit

--master spark://node1:7077,node2:7077

--class org.apache.spark.examples.SparkPi

../lib/spark-examples-1.6.0-hadoop2.6.0.jar

10000

 

5)、SparkShuffle

1)、SparkShuffle概念

reduceByKey会将上一个RDD中的每个key对应的全部value聚合成一个value,而后生成一个新的RDD,元素类型是<key,value>对的形式,这样每个key对应一个聚合起来的value

问题:聚合以前,每个key对应的value不必定都是在一个partition中,也不太可能在同一个节点上,由于RDD是分布式的弹性的数据集,RDDpartition极有可能分布在各个节点上。

如何聚合?

Shuffle Write上一个stage的每一个map task就必须保证将本身处理的当前分区的数据相同的key写入一个分区文件中,可能会写入多个不一样的分区文件中。

 – Shuffle Readreduce task就会从上一个stage的全部task所在的机器上寻找属于己的那些分区文件,这样就能够保证每个key所对应的value都会汇聚到同一个节点上去处理和聚合。

Spark中有两种Shuffle类型,HashShuffleSortShuffleSpark1.2以前是HashShuffle默认的分区器是HashPartitionerSpark1.2引入