Elasticsearch+Dubbo+Spring实践

1. 需求

因为公司一直用的是阿里的dubbo作为业务工程的RPC通信框架,我现在的任务是在mentor的指导下尝试重构公司的NLP服务,想把NLP的资源检索,单轮对话,多轮对话这些功能模块拆分成各自独立的服务。第一个尝试是优化问答系统中信息检索的过程,所以拿到的需求是调研用Elasticsearch来代替已有的资源检索性能会不会更好。这样也就产生了Elasticsearch+Dubbo+Spring这样奇葩的组合方式,毕竟Elasticsearch提倡的是用RESTful API交互来简化对数据的操作,而我要硬生生地用RPC的方式去调用。


Elasticsearch+Dubbo+Spring实践_第1张图片
想想都刺激

文章结构

  1. 需求
  2. Dubbo框架搭建
  3. Elasticsearch功能添加
  4. 源码

2. Dubbo框架搭建

环境准备

  • Zookeeper注册中心安装,下载。
解压
cd your_zookeeper_path/conf
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg

根据自己的需要修改配置文件,参见,这里Zookeeper的端口号采用默认配置2181。

  • Zookeeper启动:
cd your_zookeeper_path
./bin/zkServer.sh start
  • Zookeeper停止:
cd your_zookeeper_path
./bin/zkServer.sh stop

服务提供者Demo

  1. 在项目中添加Spring,Dubbo,Zookeeper的依赖:

        
        
            org.springframework.boot
            spring-boot-starter-web
            
                
                    org.springframework.boot
                    spring-boot-starter-logging
                
            
        
        
            net.sf.json-lib
            json-lib
            jdk15
            2.4
        
        
        
            com.alibaba
            dubbo
            2.5.3
            
                
                    spring
                    org.springframework
                
            
        
        
        
            com.github.sgroschupf
            zkclient
            0.1
        
        
        
            org.apache.zookeeper
            zookeeper
            3.3.6
        
    
  1. 新建服务提供者对外暴露的接口类:
public interface SearchService {
    public String sayHello(String name);
}
  1. 实现服务的接口:
public class SearchServiceImpl implements SearchService{
    public String sayHello(String name) {
        System.out.println("received from remote: "+name);
        return "Hello " + name;
    }
}
  1. 用 Spring 配置声明暴露服务:



    
    

    

    
    

    
    

    
    


  1. 启动服务提供者:
public class Starter {
    public static void main(String[] args) throws IOException {
        System.setProperty("java.net.preferIPv4Stack", "true");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"server.xml"});
        context.start();
        System.in.read(); 
    }
}

服务消费者Demo

  1. 在pom.xml中添加spring,dubbo,zookeeper的依赖:

        
        
            org.springframework.boot
            spring-boot-starter
            1.5.1.RELEASE
            
                
                    org.springframework.boot
                    spring-boot-starter-logging
                
            
        
        
            net.sf.json-lib
            json-lib
            jdk15
            2.4
        
        
        
            com.alibaba
            dubbo
            2.5.3
            
                
                    spring
                    org.springframework
                
            
        
        
        
            com.github.sgroschupf
            zkclient
            0.1
        
        
        
            org.apache.zookeeper
            zookeeper
            3.3.6
        
    

2.通过 Spring 配置引用远程服务:



    
    

    
    
    
    


  1. 加载Spring配置,并调用远程服务:
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {
    public static void main(String[] args) {
        String configName = "client.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{configName});
        context.start();
        SearchService searchService = (SearchService) context.getBean("demoService");

        while (true) {
            try {
                Thread.sleep(1000);
                // 执行远程方法
                String hello = searchService.sayHello("world");
                // 显示调用结果
                System.out.println( hello );
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }

    }
}

好啦,到这里用阿里的dubbo框架搭建的RPC通信的服务提供者和消费者已经完成,可以运行起来看看效果,记得先启动zookeeper注册中心。

3. Elasticsearch功能添加

下面我们往上面的Demo中添加操作Elasticsearch分布式搜索分析引擎的功能。

Elasticsearch安装

Elasticsearch是免安装的,下载后解压就好。关于下载版本的问题,我想发一个表情。

Elasticsearch+Dubbo+Spring实践_第2张图片

此处有好大一个坑。
Elasticsearch+Dubbo+Spring实践_第3张图片
sping data elasticsearch和elasticsearch的版本对应关系

因为一开始并不知道Spring data和elasticsearch之间有严格的版本对应关系,所以出了一堆莫名其妙的bug,这张对应表可以在 这里看,虽然我下面用的是spring boot,但是同样有这个问题。
建议下载Elasticsearch2.4.0,下载后解压就算是安装完成了。
修改配置文件:

cd your_Elasticsearch
vi config/elasticsearch.yml

默认的配置,elasticsearch使用的HTTP端口是9200,TCP端口是9300.
启动Elasticsearch:

cd your_Elasticsearch/bin
./elasticsearch

Server Demo

  1. pom.xml中添加elasticsearch的依赖
        
        
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
            2.0.1.RELEASE
        

这时会产生netty包的冲突,解决:

        
        
            com.alibaba
            dubbo
            2.5.3
            
                
                    spring
                    org.springframework
                
                
                    netty
                    org.jboss.netty
                
            
        
        
        
            org.apache.zookeeper
            zookeeper
            3.3.6
            
                
                    netty
                    io.netty
                
            
        
  1. 新建实体类:
@Document(indexName = "dialog", type = "qa", shards = 1, replicas = 0)
public class QA {
    @Id
    private int id;

    private String Q;

    @Field(type = FieldType.Nested)
    private List A;

    public QA() {
    }

    public QA(int id, String q, List a) {
        this.id = id;
        Q = q;
        A = a;
    }

    public int getId() {
        return id;
    }

    public String getQ() {
        return Q;
    }

    public void setQ(String q) {
        Q = q;
    }

    public List getA() {
        return A;
    }

    public void setA(List a) {
        A = a;
    }

    @Override
    public String toString() {
        return  " QA { " +
                "   id = " + id + "," +
                "   Q = '" + Q + "'," +
                "   A = " + A +
                '}';
    }
}
  1. 新建Repository类:
public interface QARepository extends ElasticsearchRepository {
}
  1. 修改server的接口类,添加操作elasticsearch的方法:
public interface SearchService {
    public String sayHello(String name);
    public String search(final String query);
}
  1. 修改service的实现,实现操作elasticsearch的方法:
@Service("demoService")
public class SearchServiceImpl implements SearchService {
    @Autowired
    private Client client;
    @Autowired
    private QARepository qARepository;
    public String sayHello(String name) {
        System.out.println("received from remote: "+name);
        return "Hello " + name;
    }

    /**
     * 插入一条数据
     * @param qa
     */
    public void addEntity(QA qa) {
        qARepository.save(qa);
    }

    /**
     * 查询
     * @param query
     * @return
     */
    public String search(final String query) {

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

        QueryBuilder queryBuilder = QueryBuilders.matchQuery("q", query);

        SearchResponse response = client.prepareSearch()
                .setQuery(queryBuilder)
                .addHighlightedField("Q")
                .execute().actionGet();

        SearchHit[] searchHitArr = response.getHits().getHits();
        /**
         * 遍历检索到的结果,这里可以自定义排序算法
         */
        for (int i = 0; i < searchHitArr.length; ++i) {
            SearchHit searchHit = searchHitArr[i];
            System.out.print("index:"+searchHit.index()+" type:"+searchHit.getType()+" id: "+searchHit.getId()+" q:"+(String)searchHit.getSource().get("q"));
            List l  = (List)searchHit.getSource().get("a");
            for (int j = 0;j0){
            SearchHit searchHit = searchHitArr[0];
            result = "index:"+searchHit.index()+" type:"+searchHit.getType()+" id: "+searchHit.getId()+" q:"+(String)searchHit.getSource().get("q");
            List l  = (List)searchHit.getSource().get("a");
            for (int j = 0;j

注意最上面的注解@service ,因为client和qARepository需要spring自动搜索注入。所以改成了注解的方式,不再在配置文件中显示的配置。

  1. spring配置文件的改变:



    
    
    
    
    
    
    
    
    
    
        
    
    
    
    
    
    
    


Client Demo

客户端需要改变的地方很小,只需要改变调用的远程方法即可。

public interface SearchService {
    public String sayHello(String name);
    public String search(final String query);
}
public class Client {
    public static void main(String[] args) {
        String configName = "client.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{configName});
        context.start();
        SearchService searchService = (SearchService) context.getBean("demoService");

        while (true) {
            try {
                Thread.sleep(1000);
                // 执行远程方法
                String hello = searchService.search("你怕黑吗?");
                // 显示调用结果
                System.out.println( hello );
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
}

记得测试之前先启动Elasticsearch。

4. 源码

Demo已经上传到github,点击ElasticsearchDemo查看。
这篇文章没有详细介绍导入数据的部分,可直接在命令行用Elasticsearch的语法插入数据,也可以先用SearchServiceImpl的addEntity方法插入数据,下一篇文章会介绍Elasticsearch如何批量插入数据。

你可能感兴趣的:(Elasticsearch+Dubbo+Spring实践)