网络拾遗

Kafka相关:

kafka性能调优:https://blog.csdn.net/vegetable_bird_001/article/details/51858915

kafka高性能揭秘:https://www.cnblogs.com/qcloud1001/p/8984590.html

SpringBoot:

程序员DD的教程:http://blog.didispace.com/categories/Spring-Boot/

SpringCloud:

程序员DD的教程:http://blog.didispace.com/categories/Spring-Cloud/

ElasticSearch:

与springboot集成: https://blog.csdn.net/li521wang/article/details/83792552

blog精华推荐

  1. 张强-纯洁的微笑
  2. 方志明-博客专栏
  3. 程序猿DD的博客
  4. Spring Cloud Zuul 的 route 运行机制分析-黑洞之谜
  5. 简书 二月_春风
  6. 芋道源码解析
  7. Aoho的博客(security,权限服务整合)
  8. 知秋大佬的视频

kafka删除topic

转:https://www.jianshu.com/p/11dd353d0057

删除kafka topic及其数据,严格来说并不是很难的操作。但是,往往给kafka 使用者带来诸多问题,发现都会偶然出现无法彻底删除kafka的情况。
前提:kafka必须配置参数delete.topic.enable=true

  1. 如果需要被删除topic 此时正在被程序 produce和consume,则这些生产和消费程序需要停止
  2. 执行删除命令
    ./bin/kafka-topics --delete --zookeeper xxxx:2181 --topic topicName
  3. 执行查询命令
    ./bin/kafka-topics.sh --list --zookeeper xxxx:2181
    如果topicName正常被删除,那说明正常删除,如果被标记为--marked for deletion,说明还有对这个topic的引用

另外如果topic被标记为--marked for deletion,这时如果去zookeeper的/brokers/topics/节点下删除这个topic(不建议这样做),虽然真的在kafka中查不到这个topic了,但是可能会引出很多意想不到的异常,例如常见的:

WARN Error while fetching metadata with correlation id 0 : {test=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)

上面这个错误就是异常topic引起的,当producer或consumer向这个topic发送数据/读取数据时不会报错,也什么都不提示,只有打印log4j日志才可以看到上面的错误信息,此时最简单的方法就是重建topic。

还有种方法是重启zookeeper和kafka,这时topic应该就恢复正常了,可是线上的kafka和zk怎么会让你随便重启呢,所以最好的方法还是重建topic,避免这个问题的方法就是要正确删除topic。

重点

  • 不到万不得已,千万不要动zk里的数据,因为我在实际线上中发现有时即使删了zk里的topic数据,但kafka内存中还是存在那个topic的,这是可能会引发意想不到的错误,严重的可能导致kafka集群挂掉,慎重!!
  • 用命令删除后就别再动了(标记状态),等kafka真正检测到这个topic不用时(比如数据彻底过了保质期)kafka会自行删除这个topic
  • 还有一种情况也是不能删除topic的,就是假如要删除的topic有分区或副本分别分布在broker0、1、2上,当broker0宕机,此时broker0上有这个topic的分区和副本,这时是删不掉的,只有把broker0启动,topic会自动删除

Linux shell脚本提示:找不到/bin/bash

写了一个shell脚本:test.sh

#指定脚本运行环境
#!/usr/bin/bash 

/usr/local/kafka/stop.sh
sleep 1
/usr/local/kafka/start.sh.....

结果运行test.sh, 提示: 找不到目录/usr/bin/bash, 但是系统确实有这个目录。后来才发现是文件格式的问题,解决如下:

#编辑脚本
> vim test.sh
:set ff

formatff=doc
#看到文件格式是doc, 修改为unix
:set ff=unix
#保存
:wq
#然后运行test.sh,一切正常了!

如果是ubuntu系统,可能是32位lib库的问题,解决方案:安装lib库

$sudo apt-get install ia32-libs

如果它说没有可用的软件包,那么安装提示中替代的包即可,比如:
lib32ncurses5 lib32z1

$sudo apt-get install lib32z1

JAVA NIO基本使用

NIO中有几个核心对象:缓冲区(Buffer)、通道(Channel)、选择器(Selector)

缓冲区Buffer

缓冲区实际上是一个容器对象,更直接的说,其实就是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。

import java.nio.IntBuffer;
 
public class TestIntBuffer {
	public static void main(String[] args) {
		// 分配新的int缓冲区,参数为缓冲区容量
		// 新缓冲区的当前位置将为零,其界限(限制位置)将为其容量。它将具有一个底层实现数组,其数组偏移量将为零。
		IntBuffer buffer = IntBuffer.allocate(8);
 
		for (int i = 0; i < buffer.capacity(); ++i) {
			int j = 2 * (i + 1);
			// 将给定整数写入此缓冲区的当前位置,当前位置递增
			buffer.put(j);
		}
 
		// 重设此缓冲区,将限制设置为当前位置,然后将当前位置设置为0
		buffer.flip();
 
		// 查看在当前位置和限制位置之间是否有元素
		while (buffer.hasRemaining()) {
			// 读取此缓冲区当前位置的整数,然后当前位置递增
			int j = buffer.get();
			System.out.print(j + "  ");
		}
 
	}
 
}

通道Channel

通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

使用NIO读取数据

在前面我们说过,任何时候读取数据,都不是直接从通道读取,而是从通道读取到缓冲区。所以使用NIO读取数据可以分为下面三个步骤: 
1. 从FileInputStream获取Channel 
2. 创建Buffer 
3. 将数据从Channel读取到Buffer中

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
 
public class Program {
    static public void main( String args[] ) throws Exception {
        FileInputStream fin = new FileInputStream("c:\\test.txt");
        
        // 获取通道
        FileChannel fc = fin.getChannel();
        
        // 创建缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        
        // 读取数据到缓冲区
        fc.read(buffer);
        
        buffer.flip();
        
        while (buffer.remaining()>0) {
            byte b = buffer.get();
            System.out.print(((char)b));
        }
        
        fin.close();
    }
}

使用NIO写入数据

使用NIO写入数据与读取数据的过程类似,同样数据不是直接写入通道,而是写入缓冲区,可以分为下面三个步骤: 
1. 从FileInputStream获取Channel 
2. 创建Buffer 
3. 将数据从Channel写入到Buffer中

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
 
public class Program {
    static private final byte message[] = { 83, 111, 109, 101, 32,
        98, 121, 116, 101, 115, 46 };
 
    static public void main( String args[] ) throws Exception {
        FileOutputStream fout = new FileOutputStream( "c:\\test.txt" );
        
        FileChannel fc = fout.getChannel();
        
        ByteBuffer buffer = ByteBuffer.allocate( 1024 );
        
        for (int i=0; i<message.length; ++i) {
            buffer.put( message[i] );
        }
        
        buffer.flip();
        
        fc.write( buffer );
        
        fout.close();
    }

选择器Selector注册和处理事件

NIO中实现非阻塞I/O的核心对象就是Selector,Selector就是注册各种I/O事件地 方,而且当那些事件发生时,就是这个对象告诉我们所发生的事件. 当有读或写等任何注册的事件发生时,可以从Selector中获得相应的SelectionKey,同时从 SelectionKey中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据

使用NIO中非阻塞I/O编写服务器处理程序,大体上可以分为下面三个步骤:

1. 向Selector对象注册感兴趣的事件 
2. 从Selector中获取感兴趣的事件 
3. 根据不同的事件进行相应的处理

/*
 * 注册事件
 * */
protected Selector getSelector() throws IOException {
    // 创建Selector对象
    Selector sel = Selector.open();
    
    // 创建可选择通道,并配置为非阻塞模式
    ServerSocketChannel server = ServerSocketChannel.open();
    server.configureBlocking(false);
    
    // 绑定通道到指定端口
    ServerSocket socket = server.socket();
    InetSocketAddress address = new InetSocketAddress(port);
    socket.bind(address);
    
    // 向Selector中注册感兴趣的事件
    server.register(sel, SelectionKey.OP_ACCEPT); 
    return sel;
}

/*
 * 开始监听
 * */ 
public void listen() { 
    System.out.println("listen on " + port);
    try { 
        while(true) { 
            // 该调用会阻塞,直到至少有一个事件发生
            selector.select(); 
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iter = keys.iterator();
            while (iter.hasNext()) { 
                SelectionKey key = (SelectionKey) iter.next(); 
                iter.remove(); 
                process(key); 
            } 
        } 
    } catch (IOException e) { 
        e.printStackTrace();
    } 
}

/*
 * 根据不同的事件做处理
 * */
protected void process(SelectionKey key) throws IOException{
    // 接收请求
    if (key.isAcceptable()) {
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        SocketChannel channel = server.accept();
        channel.configureBlocking(false);
        channel.register(selector, SelectionKey.OP_READ);
    }
    // 读信息
    else if (key.isReadable()) {
        SocketChannel channel = (SocketChannel) key.channel(); 
        int count = channel.read(buffer); 
        if (count > 0) { 
            buffer.flip(); 
            CharBuffer charBuffer = decoder.decode(buffer); 
            name = charBuffer.toString(); 
            SelectionKey sKey = channel.register(selector, SelectionKey.OP_WRITE); 
            sKey.attach(name); 
        } else { 
            channel.close(); 
        } 
        buffer.clear(); 
    }
    // 写事件
    else if (key.isWritable()) {
        SocketChannel channel = (SocketChannel) key.channel(); 
        String name = (String) key.attachment(); 
        
        ByteBuffer block = encoder.encode(CharBuffer.wrap("Hello " + name)); 
        if(block != null)
        {
            channel.write(block);
        }
        else
        {
            channel.close();
        }
 
     }
}

JAVA NIO的selector的实现原理 epoll

Java NIO的核心类库多路复用器Selector就是基于epoll的多路复用技术实现的 

相比select、poll系统调用,epoll有如下优点:

1.支持一个进程打开的socket描述符(FD)不受限制,仅受限于操作系统的最大文件句柄数。
select最大的缺陷是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024。可以选择修改这个宏后重新编译内核,但这对带来网络效率的下降。也可以选择多进程的方案(传统的Apache方案)来解决这个问题,但进程的创建成本,以及进程间的数据交换需要进行考虑。
epoll具体支持的FD上线值可以通过cat /proc/sys/fs/file-max查看,这个值和系统的内存关系比较大。

2.I/O效率不会随着FD数目的增加而线性下降。
当拥有一个很大的socket集合时,由于网络延时或者链路空闲,任一时刻只有少部分的socket是“活跃”的,但select/poll每次调用都会线性扫描全部的集合,导致效率线性下降。而epoll只会对活跃的socket进行操作-只有活跃的socket才会主动调用callback函数,所以只需要遍历那些被内核I/O事件异步唤醒而加入Ready队列的FD集合。

3.使用mmap加速内核与用户空间的消息传递
epoll通过内核和用户空间mmap同一块内存来实现的。mmap()系统调用允许存放在块设备上的文件或信息的一部分映射到进程的部分地址空间。

看到这里,或许会产生一个疑问,为什么内核要和用户空间进行消息传递呢?

下面我来进行深入解析:

首先,操作系统必须完成的两个主要目标:

1,与硬件部分交互

2,为运行在计算机系统上的应用程序(即所谓的用户程序)提供执行环境。

而现代操作系统是禁止用户程序直接与底层硬件进行交互的,或者禁止直接访问任意的物理地址。为此,硬件为CPU引入了至少两种不同的执行模式:用户程序的非特权模式和内核的特权模式。Unix把它们分别称为用户态(User Mode)和内核态(Kernel Mode)。所以,每个实际的文件I/O操作必须在内核态下进行。

cpu既可以运行在用户态,也可以运行在内核态。一些CPU可以有两种以上的执行状态,如Intel80x86微处理器有四种不同的执行状态。但是,所有标准的Unix内核都仅仅利用了内核态和用户态。

一个程序执行时,大部分时间都处在用户态,只有需要内核所提供的服务时才切换到内核态。当内核满足了用户程序的请求后,它让程序又回到用户态下。

然后,访问文件的模式有多种:

1,规范模式

规范模式下文件打开后,标志O_SYNC与O_DIRECT清0,而且它的内容是由系统调用read()和write()来存取。系统调用read()将阻塞调用进程,直到数据被拷贝进用户态地址空间。但系统调用write()不同,它在数据被拷贝到页高速缓存(延迟写)后就马上结束。

2,同步模式

同步模式下文件打开后,标志O_SYNC置1。这个标志只影响写操作(读操作总是会阻塞),它将阻塞调用进程,直到数据被有效的写入磁盘。

3,内存映射模式

内存映射模式下文件打开后,应用程序发出系统调用mmap()将文件映射到内存中。因此,文件就称为RAM中的一个字节数组,应用程序就可以直接访问数组元素,而不需用系统调用read()、write()或lseek()。

4,直接I/O模式

直接I/O模式下文件打开后,标志O_DIRECT置1。任何读写操作都将数据在用户态地址空间与磁盘间直接传送而不通过页高速缓存。

5,异步模式

异步模式下,文件的访问可以有两种方法,即通过一组POSIX API或Linux特有的系统调用来实现。所谓的异步模式就是数据的传输请求不阻塞调用进程,而是在后台执行,同时应用程序继续它的正常运行。

说道内存映射,就要谈谈操作系统的内存管理了。

以80X86微处理器为例,我们必须区分以下三种不同的地址:

逻辑地址,包含在机器语言指令中用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段(segment)和偏移量(offset)组成。

线性地址,也称虚拟地址,是一个32位无符号整数,可以用来表示高达4GB的地址。

物理地址,用于内存芯片级内存单元寻址。

内存控制单元(MMU)通过一种称为分段单元的硬件电路把一个逻辑地址转换成线性地址;接着,第二个称为分页单元的硬件电路把线性地址转换成一个物理地址。

注:
Linux采用4KB页框大小作为标准的内存分配单元。

内存碎片的产生主要是由于请求内存的大小与分配给它的大小不匹配而造成的。一种典型的解决办法是提供按照几何分布的内存区大小,即内存区大小取决于2的幂而不取决于所存放的数据大小。这样,不管请求内存的大小是多少,都可以保证内存碎片小于50%。

内核使用一种新的资源-线性区,实现了对进程动态内存的推迟分配。当用户态进程请求动态内存时,并没有获得请求的页框,而仅仅获得对一个新的线性地址区间的使用权,而这一线性地址区间就成为进程地址空间的一部分。这一区间叫“线性区”。

进程的地址空间由允许进程使用的全部线性地址组成。内核通过所谓线性区的资源来表示线性地址区间,线性区是有起始线性地址、长度和一些访问权限来描述的。

内存映射,内核给这个进程分配一个新的线性区来映射这个文件。一个新建立的内存映射就是一个不包含任何页的线性区,当进程引用线性区中的一个地址时,缺页异常发生。

与进程地址空间有关的全部信息都包含在一个叫做内存描述符的数据结构中,类型为mm_struct。

Linux通过类型为vm_area_struct的对象实现线性区

进程所拥有的所有线性区是通过一个简单的链表按照内存地址升序链接在一起的,其中mmap字段指向链表中的第一个线性区描述符。当进程中的线性区很多时,Linux2.6使用红黑树来存储内存描述符。

进程的地址空间、它的内存描述符以及线性区链表三者之间的关系,如下图所示:

每个Unix进程都拥有一个特殊的线性区-堆,堆用于满足进程的动态内存请求。

For most operating systems, mapping a file into memory is more expensive than reading or writing a few tens of kilobytes of data via the usual read and write methods. From the standpoint of performance it is generally only worth mapping relatively large files into memory.

JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0。

NIO2.0正式提供了异步文件通道和异步套接字通道的实现。对应于Unix网络编程中的事件驱动I/O – AIO。它不需要通过多路复用器-Selector对注册的通道进行轮询即可实现异步读写,从而简化了NIO的编程模型。

转自https://blog.csdn.net/donsonzhang/article/details/46626273

ElasticSearch 笔记 – 单机伪集群搭建 7.0.0

网上看了一下, 大都需要把elasticsearch程序拷贝多份, 然后修改配置.

程序有500多m, 蛮大的, 所以我想有没有只修改配置的方式…..

主要是三个地方:

  1. bin/elasticsearch.bat
  2. bin/elasticsearch-env.bat
  3. config文件夹

1.config文件夹(这里只做3个节点)

拷贝config文件夹, 命名为config-1, config-2, config-3
修改config/elasticsearch.yml
----------------------------------------------------------
#统一集群的名称(3个配置一样)
cluster.name: my-app
#当前节点名称
node.name: node-1
#当前节点数据
path.data: /data/elastic/data/1
#当前节点日志目录
path.data: /data/elastic/log/1
#当前节点端口(3个节点不同端口)
http.port: 9201
#指定初始的主节点(这里默认第一个)
cluster.initial_master_nodes: ["node-1"]
----------------------------------------------------------
其他两个文件夹做类似的修改

2.bin/elasticsearch-env.bat

找到设置"配置文件"的代码:
......
set HOSTNAME=%COMPUTERNAME%
if not defined ES_PATH_CONF (
  set ES_PATH_CONF=!ES_HOME!\config
)
......
把"set ES_PATH_CONF=!ES_HOME!\config" 修改为"set ES_PATH_CONF=!ES_HOME!\config-1", 另存为: bin/elasticsearch-env-1.bat
其他两个类似

3.bin/elasticsearch.bat

找到设置"环境变量"的代码:
......
CALL "%~dp0elasticsearch-env.bat" || exit /b 1
......
把"elasticsearch-env.bat" 修改为"elasticsearch-env-1.bat", 另存为: bin/elasticsearch-1.bat
其他两个类似

这样, 启动elasticsearch-1.bat/ elasticsearch-2.bat / elasticsearch-3.bat

就能建立单机集群.

6.X版本的参考: https://blog.csdn.net/qq_34021712/article/details/79330028

Spring–AOP原理及拦截器

原理

AOP(Aspect Oriented Programming),也就是面向方面编程的技术。AOP基于IoC基础,是对OOP的有益补充。

  AOP将应用系统分为两部分,核心业务逻辑(Core businessconcerns)及横向的通用逻辑,也就是所谓的方面Crosscutting enterprise concerns,例如,所有大中型应用都要涉及到的持久化管理(Persistent)、事务管理(TransactionManagement)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。

  AOP正在成为软件开发的下一个光环。使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect。AOP可以防止代码混乱。

  Springframework是很有前途的AOP技术。作为一种非侵略性的、轻型的AOP framework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。这意味着开发团队里只需一人要对付AOP framework,其他人还是像往常一样编程。

  AOP概念

  让我们从定义一些重要的AOP概念开始。

  — 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。

  — 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

  — 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

  — 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。

  — 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。

  — 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。

  — AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

  — 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

  各种通知类型包括:

  —  Around通知:包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为,它们负责选择继续执行连接点或通过返回它们自己的返回值或抛出异常来短路执行。

  —  Before通知:在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

  —  Throws通知:在方法抛出异常时执行的通知。Spring提供强制类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable或Exception强制类型转换。

  —  After returning通知:在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。

  Around通知是最通用的通知类型。大部分基于拦截的AOP框架(如Nanning和Jboss 4)只提供Around通知。

  如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需要的行为。例如,如果只是需要用一个方法的返回值来更新缓存,你最好实现一个after returning通知,而不是around通知,虽然around通知也能完成同样的事情。使用最合适的通知类型使编程模型变得简单,并能减少潜在错误。例如,你不需要调用在around通知中所需使用的MethodInvocation的proceed()方法,因此就调用失败。

  切入点的概念是AOP的关键,它使AOP区别于其他使用拦截的技术。切入点使通知独立于OO的层次选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。 因此切入点构成了AOP的结构要素。

 拦截器(也称拦截机)

    拦截机 (Interceptor), 是 AOP (Aspect-Oriented Programming) 的另一种叫法。AOP本身是一门语言,只不过我们使用的是基于JAVA的集成到Spring 中的 SpringAOP。同样,我们将通过我们的例子来理解陌生的概念。

   接口类

 

package com.test.TestSpring3; 

 

public interface UserService // 被拦截的接口 

...{ 

   public void printUser(String user); 

} 

 实现类

 

package com.test.TestSpring3; 

 

public class UserServiceImp implements UserService// 实现UserService接口 

...{ 

   public void printUser(String user) ...{ 

       System.out.println("printUser user:" + user);// 显示user 

   } 

} 

  

 AOP拦截器

package com.test.TestSpring3; 

 

import org.aopalliance.intercept.MethodInterceptor; 

importorg.aopalliance.intercept.MethodInvocation; 

 

public class UserInterceptor implementsMethodInterceptor 

// AOP方法拦截器 

...{ 

 

   public Object invoke(MethodInvocation arg0) throws Throwable ...{ 

 

       try ...{ 

 

           if(arg0.getMethod().getName().equals("printUser")) 

           // 拦截方法是否是UserService接口的printUser方法 

           ...{ 

                Object[] args =arg0.getArguments();// 被拦截的参数 

               System.out.println("user:" + args[0]); 

               arg0.getArguments()[0] ="hello!";// 修改被拦截的参数 

 

           } 

 

           System.out.println(arg0.getMethod().getName() + "---!"); 

           return arg0.proceed();// 运行UserService接口的printUser方法 

 

       } catch (Exception e) ...{  

           throw e; 

       } 

   } 

}

 

 测试类

 

package com.test.TestSpring3; 

 

importorg.springframework.beans.factory.BeanFactory; 

 

importorg.springframework.beans.factory.xml.XmlBeanFactory; 

import org.springframework.context.ApplicationContext; 

importorg.springframework.context.support.ClassPathXmlApplicationContext; 

importorg.springframework.context.support.FileSystemXmlApplicationContext; 

importorg.springframework.core.io.ClassPathResource; 

import org.springframework.core.io.Resource; 

importorg.springframework.web.context.support.WebApplicationContextUtils; 

 

public class TestInterceptor ...{ 

 

   public static void main(String[] args) ...{ 

       ApplicationContext ctx = new FileSystemXmlApplicationContext( 

               "classpath:applicationContext.xml"); 

//       ApplicationContext ctx = newClassPathXmlApplicationContext("applicationContext.xml");     

         

       UserService us = (UserService)ctx.getBean("userService"); 

       us.printUser("shawn"); 

 

   } 

}

 

 配置文件

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

<!DOCTYPE beans PUBLIC"-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd"> 

<beans> 

   <bean id="userServiceImp" 

       class="com.test.TestSpring3.UserServiceImp" /> 

 

   <bean id="userInterceptor"class="com.test.TestSpring3.UserInterceptor" /> 

 

   <bean id="userService" 

       class="org.springframework.aop.framework.ProxyFactoryBean"> 

     <!-- 代理接口--> 

        <propertyname="proxyInterfaces"> 

           <value>com.test.TestSpring3.UserService</value> 

       </property> 

      <!-- 目标实现类--> 

       <property name="target"> 

           <ref local="userServiceImp" />  

     </property> 

        <!-- 拦截器 --> 

       <property name="interceptorNames"> 

           <list> 

               <value>userInterceptor</value> 

           </list> 

       </property> 

   </bean> 

 

</beans>

  

 输出:

 user:shawn

  printUser—!

 printUser user:hello!

 

结论:调用方法的时候 传入的值被拦截修改了.

拦截器中的事务管理(事务拦截机)

 如果不采用拦截机的机制时,在使用JDBC进行数据库访问时,存在两种情况:

自动提交        这是JDBC驱动默认的模式,每次数据库操作(CRUD)成功完成后,都作为一个单独的事务自动提交,如果未成功完成,即抛出了 SQLException 的话,仅最近的一个操作将回滚。

非自动提交    这是想更好的控制事务时需要程序地方式进行控制:

在进行该事务单元的任何操作之前 setAutoCommit(false)

在成功完成事务单元后commit()

在异常发生后rollback()

自动提交模式是不被推荐的,因为每个操作都将产生一个事务点,这对于大的应用来说性能将受到影响;再有,对于常见的业务逻辑,这种模式显得无能为力。比如:

转帐,从A帐户取出100元,将其存入B帐户;如果在这两个操作之间发生了错误,那么用户A将损失了100元,而本来应该给帐户B的,却因为失败给了银行。

所以,建议在所有的应用中,如果使用 JDBC 都将不得不采用非自动提交模式(你们要能发现了在我们的 JDBC 那个例子中,我们采用的就是自动提交模式,我们是为了把精力放在JDBC上,而不是事务处理上),即我们不得不在每个方法中:

try {     

 // 在获得连接后,立即通过调用 setAutoCommit(false) 将事务处理置为非自动提交模式  // Prepare Query to fetch the userInformation        

    pst = conn.prepareStatement(findByName);                  

      // ...           conn.commit();        

 } catch(Exception ex) {        

    conn.rollback();       

     throw ex;        

 }finally {    

        try {     

          // Close Result Set and Statement   

         if (rset != null) rset.close();                

        if (pst != null) pst.close();                         

    }catch (Exception ex) {                

      ex.printStackTrace();                 

      throw new Exception("SQL Error while closing objects = " +ex.toString());             

}   

}

  

 这样代码在AOP的倡导者看来是“肮脏”的代码。他们认为,所有的与事务有关的方法都应当可以集中配置(见声明性事务控制),并自动拦截,程序应当关心他们的主要任务,即商业逻辑,而不应和事务处理的代码搅和在一起。

 

我先看看 Spring 是怎么做到拦截的:

Spring 内置支持的事务处理拦截机

这里因为要用到JpetStore项目中的代码,我们将 applicationContext.xml 全部内容列出:

 

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

<!--

  -Application context definition for JPetStore's business layer.

  -Contains bean references to the transaction manager and to the DAOs in

  -dataAccessContext-local/jta.xml (see web.xml's"contextConfigLocation").
   Jpetstore 的应用上下文定义,包含事务管理和引用了在 dataAccessContext-local/jta.xml(具体使用了哪个要看 web.xml 中的 'contextConfigLocation' 的配置)中注册的DAO

-->

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

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

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

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

      xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd

      http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd

http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

   <!-- ========================= GENERAL DEFINITIONS========================= -->

 

   <!-- Configurer that replaces ${...} placeholders with values fromproperties files

        占位符的值将从列出的属性文件中抽取出来

    -->

   <!-- (in this case, mail and JDBC related properties) -->

   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

       <property name="locations">

           <list>

               <value>WEB-INF/mail.properties</value>

               <value>WEB-INF/jdbc.properties</value>

           </list>

       </property>

   </bean>

   <!-- MailSender used by EmailAdvice

        指定用于发送邮件的javamail 实现者,这里使用了spring 自带的实现。此 bean 将被 emailAdvice 使用
    -->

   <bean id="mailSender"class="org.springframework.mail.javamail.JavaMailSenderImpl">

       <property name="host" value="${mail.host}"/>

   </bean>
 

   <!-- ========================= BUSINESS OBJECT DEFINITIONS======================== -->

 

   <!-- 不需要,因为被SpringMVC 的实现使用 Genericvalidator for Account objects, to be used for example by the Spring web tier-->

   <bean id="accountValidator"class="org.springframework.samples.jpetstore.domain.logic.AccountValidator"/>
  

   <!-- 不需要,因为被SpringMVC 的实现使用 Genericvalidator for Order objects, to be used for example by the Spring web tier-->

   <bean id="orderValidator"class="org.springframework.samples.jpetstore.domain.logic.OrderValidator"/>
   

   <!--

       主要的商业逻辑对象,即我们所说的门面对象

       注入了所有的DAO,这些DAO是引用了 dataAccessContext-xxx.xml 中定义的DAO

       门面对象中的所有方法的事务控制将通过下面的 aop:config 来加以控制

     - JPetStore primary business object (default implementation).

     - Transaction advice gets applied through the AOP configuration below.

  -->

   <bean id="petStore"class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">

       <property name="accountDao" ref="accountDao"/>

       <property name="categoryDao"ref="categoryDao"/>

       <property name="productDao" ref="productDao"/>

       <property name="itemDao" ref="itemDao"/>

       <property name="orderDao" ref="orderDao"/>

   </bean>
  

   <!-- ========================= ASPECT CONFIGURATION======================== -->

   <!-- AOP配置,用来控制哪些方法将需要进行事务处理,采用了AspectJ 的语法 -->

   <aop:config>

       <!--

       This definition creates auto-proxy infrastructure based on the givenpointcut,

       expressed in AspectJ pointcut language. Here: applying the advice named

       "txAdvice" to all methods on classes named PetStoreImpl.
   -->

       <!-- 指出在PetStoreFacade 的所有方法都将采用txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace, 但其暗示着其所有的实现类也将

        同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。 -->

       <aop:advisor pointcut="execution(**..PetStoreFacade.*(..))" advice-ref="txAdvice"/>

       

       <!--

           This definition creates auto-proxy infrastructure based on the givenpointcut,

           expressed in AspectJ pointcut language. Here: applying the advice named

           "emailAdvice" to insertOrder(Order) method of PetStoreImpl

   -->

       <!-- 当执行PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,

        并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)-->

       <aop:advisor pointcut="execution(**..PetStoreFacade.insertOrder(*..Order))" advice-ref="emailAdvice"/>
   </aop:config>

   <!--
  事务方针声明,用于控制采用什么样的事务策略

       Transaction advice definition, based on method name patterns.

       Defaults to PROPAGATION_REQUIRED for all methods whose name starts with

       "insert" or "update", and to PROPAGATION_REQUIREDwith read-only hint

       for all other methods.
   -->

   <tx:advice id="txAdvice">

       <tx:attributes>

           <tx:method name="insert*"/>

           <tx:method name="update*"/>

           <tx:method name="*" read-only="true"/>

       </tx:attributes>

   </tx:advice>


   <!-- 拦截机,用于在适当的时机(通过AOP配置,如上面)在方法执行成功后发送邮件

     AOP advice used to send confirmation email after order has beensubmitted -->

   <!-- -->

   <bean id="emailAdvice" class="org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice">

       <property name="mailSender" ref="mailSender"/>

   </bean>

   <!-- ========================= 忽略 REMOTE EXPORTER DEFINITIONS ======================== -->

</beans>

 

这个配置比想象的要简单的多:

<aop:config>        

 <!-- This definition creates auto-proxyinfrastructure based on the given pointcut, expressed in AspectJ pointcutlanguage.  

Here: applying the advice named        "txAdvice" to all methods onclasses named PetStoreImpl. 指出在 PetStoreFacade  

的所有方法都将采用txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace,         

 但其暗示着其所有的实现类也将同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。    -->    

      <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))"advice-ref="txAdvice"/>                

 <!-- 其它拦截机-->   

</aop:config>

 

1. 所有的拦截机配置都放在 <aop:config> 配置元素中.

2. 下面还是需要理解一下几个有关AOP的专用名词,不过,是挺抽象的,最好能会意出其的用意

pointcut 切入点,比如:updateAccount 方法需要进行事务管理,则这个切入点就是“执行方法体”(execution)。Spring 所有支持的切入点类型在都在 Spring reference: 6.2.3.1. Supported Pointcut Designators 中列出了。

advice   要对这个切入点进行什么操作,比如事务控制

advisor  Spring 特有的概念,将上两个概念合到一个概念中来,即一个 advisor 包含了一个切入点及对这个切入点所实施的操作。

因为 方法执行切入点 execution 为最常见的切入点类型,我们着重介绍一下,execution 的完全形式为:

execution(modifiers-pattern?ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

这是一个正则表达式,其中由’?’ 结尾的部分是可选的。翻译过来就是:

执行(方法访问修饰符? 方法返回类型 声明类型? 方法名(方法参数类型) 抛出异常?)

所有的这些都是用来定义执行切入点,即那些方法应该被侯选为切入点:

方法访问修饰符   即 public, private 等等

方法返回类型       即方法返回的类型,如 void, String 等等

声明类型                1.5的语法,现在可以先忽略它

方法名                    方法的名字

方法参数类型       方法的参数类型

抛出异常                方法声明的抛出的异常

 

例如,所有dao代码被定义在包 com.xyz.dao 及子包 com.xyz.dao.hibernate, 或者其它,如果还有的话,子包中, 里面定义的是提供DAO功能的接口或类,那么表达式:

execution(* com.xyz.dao..*.*(..))

表示切入点为:执行定义在包com.xyz.dao 及其子包(因为 .. 所致) 中的任何方法

 

详细情况可以参见Spring refernce: 6.2.3.4. Examples

 

因此这个表达式为执行定义在类PetStoreFacade 及其实现类中的所有方法,采取的动作定义在 txAdvice 中. 关于该 advice 的定义,(见声明性事务控制)一节

<aop:advisor pointcut=”execution(**..PetStoreFacade.*(..))” advice-ref=”txAdvice”/>

 

Spring 自定拦截机

来为了进行事务控制,我们只需简单地配置几下,所有的工作都由 Spring 来做。这样固然很好,但有时我们需要有我们特有的控制逻辑。因为Spring 不可能包含所有人需要的所有拦截机。所以它提供了通过程序的方式加以定制的方式。我们的项目中就有这么一个拦截机,在用户确认付款后,将定单信息通过 email 的方式发送给注册用户的邮箱中。

 

<aop:config>

  ...

 

       <!-- 当执行 PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,

        并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)-->

       <aop:advisor pointcut="execution(**..PetStoreFacade.insertOrder(*..Order))"advice-ref="emailAdvice"/>

       

   </aop:config>

红色的注释已经说的很清楚这个Advisor 了,它的切入点(pointcut)为PetStoreFacade 的 voidinsertOrder(Order order) 方法,采取的动作为引用的 emailAdvice, 下面我们就来看看 emailAdvice:

 

   <bean id=”emailAdvice”class=”org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice”>

       <property name=”mailSender” ref=”mailSender”/>

   </bean>

它给了这个 advice 的实现类为 logic 包中 SendOrderConfirmationEmailAdvice,该Bean 引用了我们前面定义的邮件发送器(一个 Spring 内置的邮件发送器).

 

下面看看这个实现类:

 

public classSendOrderConfirmationEmailAdvice implements AfterReturningAdvice,InitializingBean {

   // user jes on localhost

   private static final String DEFAULT_MAIL_FROM ="test@pprun.org";

   

   private static final String DEFAULT_SUBJECT = "Thank you for yourorder!";

   

   private final Log logger = LogFactory.getLog(getClass());

   

   private MailSender mailSender;

   

   private String mailFrom = DEFAULT_MAIL_FROM;

   

   private String subject = DEFAULT_SUBJECT;

   

   public void setMailSender(MailSender mailSender) {

       this.mailSender = mailSender;

    }

   

   public void setMailFrom(String mailFrom) {

       this.mailFrom = mailFrom;

    }

   

   public void setSubject(String subject) {

       this.subject = subject;

    }

    

   public void throws Exception {

       if (this.mailSender == null) {

           throw new IllegalStateException("mailSender is required");

       }

    }

   

   /**

    *

    * @param returnValue 被拦截的方法的返回值

    * @param m 被拦截的方法的所有信息(Method类封装了这些信息)

    * @param args 被拦截的方法的所有参数组成的数组

    * @param target 目标对象,对于方法执行来说,即是方法所在的类的实例(与 this 同,批当前对象)

    * @throws java.lang.Throwable

    */

   public void afterReturning(Object returnValue, Method m, Object[] args,Object target) throws Throwable {

       // 我们被拦截的方法为 voidinsertOrder(Order order),方法只有一个参数,所以可知数据的第1个元素即是被传进的 order对象

       // 得到了order 对象,就可以将 order 对应的帐户名及帐单号发送到邮件中,以便确认无误。

       Order order = (Order) args[0];

       Account account = ((PetStoreFacade) target).getAccount(order.getUser().getUsername());

       

       // don't do anything if email address is not set

       if (account.getEmail() == null || account.getEmail().length() == 0) {

           return;

       }

       

       StringBuffer text = new StringBuffer();

       text.append("Dear ").append(account.getFirstname()).

                append('').append(account.getLastname());

       text.append(", thank your for your order from JPetStore. " +

                "Please note that yourorder number is ");

        text.append(order.getId());

       

       SimpleMailMessage mailMessage = new SimpleMailMessage();

       mailMessage.setTo(account.getEmail());

       mailMessage.setFrom(this.mailFrom);

       mailMessage.setSubject(this.subject);

       mailMessage.setText(text.toString());

       try {

           this.mailSender.send(mailMessage);

       } catch (MailException ex) {

           // just log it and go on

           logger.warn("An exception occured when trying to send email",ex);

       }

    }

   

}

 

1. 红色的内容即为反向注入的 mailSender 属性

 

2. 蓝色的内容为 Spring Bean 的一个通用的接口 InitializingBean ,实现类需要实现该接口定义的方法 afterPropertiesSet() ,该方法中一般是在Bean 被初始化后并设置了所有的setter 注入后调用的。所以这里是保证邮件发送器配置正确。因为如果没有配置正确,下面的工作是无法进行的,所以与其等那时抛出异常,还不如早早地在部署时就告知(通过抛出 IllegalStateException 来提示)

 

3. 绿色的内容为这个 Advise 的核心,即在切入点被切入后将采用的动作。因为 Advise 也同样有多种类型,比如我们这里的“方法正常返回”,“方法执行前”,“方法执行后”,“环绕在方法执行前后”,“方法抛出异常时”等等(详情参见 SpringReference: 6.2.4. Declaring advice)。但是我们的逻辑为在用户确认定单并且执行成功(所谓的成功是指将这一定单插入到了表 Order 中了)后,将发送一确认信。所以”方法正常返回“完全符合我们的要求。

接口AfterReturningAdvice即是 Spring中表示”方法正常返回“ 这一语义的 Advice, 所以我们实现这个接口及其必须的方法 afterReturning.

方法代码的工作其实并不重要,只要我们理解这些“魔法”一样的技术后,实现代码是很简单的。值得提及的是这个方法的参数,这些参数是封装了切入点的所有信息,请见上面的注释。在我们的实现中只使用了被拦截方法的参数,在复杂的 Advice 实现中可能会用到切入点所有信息。

关于一次JVM宕机的问题排查

描述

情况是这样的,tomcat服务刚启动访问正常, 但是过不了一会儿,访问就N慢,到最后服务直接宕机.

处理

1. 先打印tomcat日志,看看什么情况: tail -200f logs/proj.log

   堆内存溢出,JVM荣幸挂了.
2.接下来: 看看内存的使用情况
    先找到java线程pid, 然后执行: jmap -heap [pid]

    JVM总共分配了3G内存, 怎么一下子就耗光了, Eden space/Old Generation都是满的.
    所以断定: 再往上加内存是没用了, 肯定是代码问题, 某个方法一次性加载了大量的数据导致.
    于是,我们就要做dump分析了
3.DUMP内存情况
    >jmap -dump:live,format=b,file=/tmp/dump20170603.bin [pid]
    dump文件一共3G,还好不大.
4.分析dump文件
    我用MAT(memory analyzer tool)分析工具: 先设置-Xmx4g,要不然撑不住.
   
    DubboServerHandler占了1.0G
    JDBC4ResultSet占了1.4G
    看了一下DubboServerHandler,  里面最大的对象也是JDBC4ResultSet.
    所以, 这是典型的SQL结果集太大, 没做分页处理.  到底是那个SQL???
    
    再分析ResultSet所属的Statement对象,终于找到了这个罪犯: select * from 用户表;
    后面居然没有加任何条件,没有加!!! 把所有用户都查出来…..顿时吴宇森!
    之后,在分析这个线程具体调用的堆栈信息,发现是: 查询别的业务时,附带查询的用户信息.
    
    用户查询本身有加条件做为输入参数,但是方法里面, 并没有为条件做空判断.
    导致: 条件为空时,便查询了所有用户.  

总结

缺少一个条件判断,导致整个服务宕机. 多么狗血的事实.

常用SQL

1. 行列转换--普通

假设有张学生成绩表(CJ)如下
Name Subject Result
张三 语文 80
张三 数学 90
张三 物理 85
李四 语文 85
李四 数学 92
李四 物理 82

想变成
姓名 语文 数学 物理
张三 80 90 85
李四 85 92 82

declare @sql varchar(4000)
set @sql = 'select Name'
select @sql = @sql + ',sum(case Subject when '''+Subject+''' then Result end) ['+Subject+']'
from (select distinct Subject from CJ) as a
select @sql = @sql+' from test group by name'
exec(@sql)

2. 行列转换--合并

有表A,
id pid
1 1
1 2
1 3
2 1
2 2
3 1
如何化成表B:
id pid
1 1,2,3
2 1,2
3 1

创建一个合并的函数
create function fmerg(@id int)
returns varchar(8000)
as
begin
declare @str varchar(8000)
set @str=''
select @str=@str+','+cast(pid as varchar) from 表A where id=@id set @str=right(@str,len(@str)-1)
return(@str)
End
go

--调用自定义函数得到结果
select distinct id,dbo.fmerg(id) from 表A

3. 如何取得一个数据表的所有列名

方法如下:先从SYSTEMOBJECT系统表中取得数据表的SYSTEMID,然后再SYSCOLUMN表中取得该数据表的所有列名。
SQL语句如下:
declare @objid int,@objname char(40)
set @objname = 'tablename'
select @objid = id from sysobjects where id = object_id(@objname)
select 'Column_name' = name from syscolumns where id = @objid order by colid

是不是太简单了? 呵呵 不过经常用阿.

4. 通过SQL语句来更改用户的密码

修改别人的,需要sysadmin role
EXEC sp_password NULL, 'newpassword', 'User'

如果帐号为SA执行EXEC sp_password NULL, 'newpassword', sa

5. 怎么判断出一个表的哪些字段不允许为空?

select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where IS_NULLABLE='NO' and TABLE_NAME=tablename

6. 如何在数据库里找到含有相同字段的表?
a. 查已知列名的情况
SELECT b.name as TableName,a.name as columnname
From syscolumns a INNER JOIN sysobjects b
ON a.id=b.id
AND b.type='U'
AND a.name='你的字段名字'

b. 未知列名查所有在不同表出现过的列名
Select o.name As tablename,s1.name As columnname
From syscolumns s1, sysobjects o
Where s1.id = o.id
And o.type = 'U'
And Exists (
Select 1 From syscolumns s2
Where s1.name = s2.name
And s1.id <> s2.id
)

7. 查询第xxx行数据

假设id是主键:
select *
from (select top xxx * from yourtable) aa
where not exists(select 1 from (select top xxx-1 * from yourtable) bb where aa.id=bb.id)

如果使用游标也是可以的
fetch absolute [number] from [cursor_name]
行数为绝对行数

8. SQL Server日期计算
a. 一个月的第一天
SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0)
b. 本周的星期一
SELECT DATEADD(wk, DATEDIFF(wk,0,getdate()), 0)
c. 一年的第一天
SELECT DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)
d. 季度的第一天
SELECT DATEADD(qq, DATEDIFF(qq,0,getdate()), 0)
e. 上个月的最后一天
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(mm,0,getdate()), 0))
f. 去年的最后一天
SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))
g. 本月的最后一天
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0))
h. 本月的第一个星期一
select DATEADD(wk, DATEDIFF(wk,0,
dateadd(dd,6-datepart(day,getdate()),getdate())
), 0)
i. 本年的最后一天
SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0))。
9.获取行号
mysql:
SELECT @rowno:=@rowno + 1 AS rowno,a.* FROM tableName a,(SELECT @rowno:=0) b
oracle:
SELECT rownum,a.* FROM tableName a

感谢那些网上提供相关SQL的作者

ElasticSearch 笔记 – 高级查询API

通用查询: match/multi_match
无论是全文搜索还是精确查询 它将用正确的分析器去分析查询字符串

范围查询: range

{
    "range": {
        "字段": {
            "gt":  20,
            "lt":   30
        }
    }
}

精确查询 term/terms

{ "term": { "age":    26 }}
term 查询对于输入的文本不分析 ,所以它将给定的值进行精确查询(包括在大小写、重音、空格等方面的差异)。

非空和空查询: exists/missing

{
    "exists":   {
        "field":    "字段"
    }
}
这与SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本质上具有共性

组合查询: bool

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ],
        "filter": {
          "range": { "date": { "lt": "2014-08-01" }} 
        }
    }
}
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。

constant_score 查询

{
    "constant_score":   {
        "filter": {
            "term": { "category": "ebooks" } 
        }
    }
}
它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下

查询分析: 在查询URL后加上explain

GET /_validate/query?explain
{
   "query": {
      "match" : {
         "tweet" : "really powerful"
      }
   }
}

如何选择查询与过滤编辑

通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。