docker搭建elasticsearch、kibana,并集成至spring boot

步骤如下:

一、基于docker搭建elasticsearch环境

1、拉取镜像

docker pull elasticsearch

2、制作elasticsearch的配置文件

master配置

http.host: 0.0.0.0
#集群名称 所有节点要相同
cluster.name: "estest"
#本节点名称
node.name: master
#作为master节点
node.master: true
#是否存储数据
node.data: true
bootstrap.system_call_filter: false
transport.host: 0.0.0.0  
discovery.zen.minimum_master_nodes: 1

salve配置

http.host: 0.0.0.0
#集群名称 所有节点要相同 
cluster.name: "estest"
#子节点名称
node.name: salve1
#不作为master节点
node.master: false
node.data: true
bootstrap.system_call_filter: false
transport.host: 0.0.0.0  
discovery.zen.minimum_master_nodes: 1  
discovery.zen.ping.unicast.hosts: ["esmaster:9300"]

注意salve节点的discovery.zen.ping.unicast.hosts设置为esmaster:9300,这里的esmaster是master节点的docker容器名字

3、启动容器

master容器

docker run -d --name esmaster --ulimit nofile=65536:131072 -p 9200:9200 -p 9300:9300 -v C:/work/docker_volume/elasticsearch/elasticsearchmaster.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v C:/work/docker_volume/elasticsearch/data/master:/usr/share/elasticsearch/data -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" elasticsearch

说明一下:

--name esmaster

 指定容器名称为esmater,salve节点配置文件中的discovery.zen.ping.unicast.hosts要和这里对应,因为salve容器和master容器之间的是通过 --linkM指令来通信的。

--ulimit nofile=65536:131072

避免容器启动时会报bootstrap checks failed异常

 -p 9200:9200 -p 9300:9300

暴露出容器的9200,9300端口到宿主机的9200,9300端口

-v C:/work/docker_volume/elasticsearch/elasticsearchmaster.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v C:/work/docker_volume/elasticsearch/data/master:/usr/share/elasticsearch/data

 挂载卷到容器中,其实就是设置容器配置文件关联上面写好的配置文件,容器存储数据关联到宿主机的外部文件中

 

-e "ES_JAVA_OPTS=-Xms512m -Xmx512m"

设置容器内Java虚拟机的内存大小

salve容器

docker run -d --name essalve1 --ulimit nofile=65536:131072 --link esmaster:esmaster -v C:/work/docker_volume/elasticsearch/elasticsearchsalve1.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v C:/work/docker_volume/elasticsearch/data/salve1:/usr/share/elasticsearch/data -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" elasticsearch

主要是使用--link esmaster:esmaster指令,来连接master容器,别的和master容器启动参数基本一样。

浏览器输入localhost:9200验证一下

说明elasticsearch已经启动成功

二、kibana

1、拉取镜像

docker pull kibana:5.6.14

2、启动镜像

docker run -it -d -e ELASTICSEARCH_URL=http://172.17.0.2:9200 -p 5601:5601 --name kibana  kibana:5.6.14

注意ELASTICSEARCH_URL中配的是es容器的docker内部ip,可以通过如下方式查看

docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container-ID>

3、http://localhost:5601访问即可

三、es与springboot集成

1、pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
    <groupId>io.searchbox</groupId>
    <artifactId>jest</artifactId>
</dependency>
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
</dependency>

2、工具类

@Slf4j
@Component
@ConditionalOnProperty(name = "elasticsearch.enabled")
public class ElasticUtil {

    private static JestClient jestClient;

    /**
     * 向elasticsearch中插入一条记录
     *
     * @param t     数据
     * @param index 索引名
     * @param type  类型名
     * @param <T>
     * @return 插入结果
     */
    public static <T> Result save(T t, String index, String type) {
        if (t == null || StringUtils.isBlank(index) || StringUtils.isBlank(type)) {
            return Result.getResult(Constant.FAIL, "缺少参数");
        }
        Index indexBuilder = new Index.Builder(t).index(index).type(type).build();
        try {
            DocumentResult execute = jestClient.execute(indexBuilder);
            return Result.getResult(Constant.SUCCESS, "插入elasticsearch成功", execute);
        } catch (IOException e) {
            log.error("插入elasticsearch异常", e);
            return Result.getResult(Constant.ERROR, "插入elasticsearch异常");
        }
    }

    /**
     * 向elasticsearch中批量插入多条记录
     *
     * @param list  数据
     * @param index 索引名
     * @param type  类型名
     * @param <T>
     * @return 插入结果
     */
    public static <T> Result saveAll(List<T> list, String index, String type) {
        if (list == null || list.size() == 0 || StringUtils.isBlank(index) || StringUtils.isBlank(type)) {
            return Result.getResult(Constant.FAIL, "缺少参数");
        }
        Bulk.Builder bulkBuilder = new Bulk.Builder();
        /*list.stream().forEach(data -> {
            Index indexBuilder = new Index.Builder(data).index(index).type(type).build();
            bulkBuilder.addAction(indexBuilder);
        });*/
        List<Index> indexList = list.stream().map(data -> new Index.Builder(data).index(index).type(type).build()).collect(toList());
        bulkBuilder.addAction(indexList);
        try {
            jestClient.execute(bulkBuilder.build());
            return Result.getResult(Constant.SUCCESS, "批量插入elasticsearch成功");
        } catch (IOException e) {
            log.error("批量插入elasticsearch异常", e);
            return Result.getResult(Constant.ERROR, "批量插入elasticsearch异常");
        }
    }

    /**
     * 根据id修改文档
     *
     * @param t     修改数据
     * @param index 索引
     * @param type  类型
     * @param id    主键
     * @param <T>
     * @return 修改结果
     */
    public static <T> Result updateById(T t, String index, String type, String id) {
        if (t == null || StringUtils.isBlank(index) || StringUtils.isBlank(type) || StringUtils.isBlank(id)) {
            return Result.getResult(Constant.FAIL, "缺少参数");
        }
        try {
            String doc = XContentFactory.jsonBuilder().startObject().field("doc", JSONObject.toJSON(t)).endObject().string();
            Update updateBuilder = new Update.Builder(doc).index(index).type(type).id(id).build();
            DocumentResult execute = jestClient.execute(updateBuilder);
            return Result.getResult(Constant.SUCCESS, "修改elasticsearch文档成功", execute);
        } catch (IOException e) {
            log.error("XContentFactory构造JSON异常", e);
            return Result.getResult(Constant.ERROR, "XContentFactory构造JSON异常");
        } catch (Exception e) {
            log.error("修改elasticsearch文档异常", e);
            return Result.getResult(Constant.ERROR, "修改elasticsearch文档异常");
        }
    }

    /**
     * 根据主键删除文档
     *
     * @param index 索引
     * @param type  类型
     * @param id    主键
     * @return
     */
    public static Result deleteById(String index, String type, String id) {
        if (StringUtils.isBlank(index) || StringUtils.isBlank(type) || StringUtils.isBlank(id)) {
            return Result.getResult(Constant.FAIL, "缺少参数");
        }
        try {
            Delete deleteBuilder = new Delete.Builder(id).index(index).type(type).build();
            DocumentResult execute = jestClient.execute(deleteBuilder);
            return Result.getResult(Constant.SUCCESS, "删除elasticsearch文档成功", execute);
        } catch (Exception e) {
            log.error("删除elasticsearch文档异常", e);
            return Result.getResult(Constant.ERROR, "删除elasticsearch文档异常");
        }
    }

    /**
     * 分页查询
     *
     * @param clazz 数据类型
     * @param elasticSearchBean 查询对象
     * @param <T>
     * @return 查询结果
     */
    public static <T> Result get(Class<T> clazz, ElasticSearchBean elasticSearchBean) {
        try {
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            AbstractQueryBuilder queryStringQueryBuilder = null;
            if(elasticSearchBean.isFullTextMatching()){
                queryStringQueryBuilder = QueryBuilders.queryStringQuery(elasticSearchBean.getKeyword());
            }else {
                queryStringQueryBuilder = QueryBuilders.multiMatchQuery(elasticSearchBean.getKeyword(), (String[]) elasticSearchBean.getMatchingFields().toArray());
            }
            searchSourceBuilder.query(queryStringQueryBuilder);
            if(null != elasticSearchBean.getHighLightsFields()){
                HighlightBuilder highlightBuilder = new HighlightBuilder();
                elasticSearchBean.getHighLightsFields().stream().forEach(field -> highlightBuilder.field(field));
//                HighlightBuilder highlightBuilder = new HighlightBuilder().field("*").requireFieldMatch(false);
                highlightBuilder.requireFieldMatch(false);
                //高亮标签
                highlightBuilder.preTags("<em>").postTags("</em>");
                //高亮内容长度
                highlightBuilder.fragmentSize(200);
                searchSourceBuilder.highlighter(highlightBuilder);
            }
            Search.Builder searchBuilder = new Search.Builder(
                    searchSourceBuilder.toString())
                    .addIndex(elasticSearchBean.getIndex())
                    .addType(elasticSearchBean.getType());
            if(null != elasticSearchBean.getFrom()){
                searchBuilder.setParameter("from", elasticSearchBean.getFrom());
            }
            if(null != elasticSearchBean.getSize()){
                searchBuilder.setParameter("size", elasticSearchBean.getSize());
            }

            if(null != elasticSearchBean.getSortFields() && elasticSearchBean.getSortFields().size() > 0){
                searchBuilder.addSort(elasticSearchBean.getSortFields());
            }
//            .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)  //设置查询类型:1.SearchType.DFS_QUERY_THEN_FETCH 精确查询; 2.SearchType.SCAN 扫描查询,无序
            Search search = searchBuilder.build();
            SearchResult searchResult = jestClient.execute(search);
            if(!searchResult.isSucceeded()){
                return Result.getResult(Constant.FAIL,searchResult.getErrorMessage());
            }
            List<JSONObject> data = searchResult.getHits(clazz).stream().map(hit -> {
                JSONObject json = (JSONObject) JSONObject.toJSON(hit.source);
                json.put("esId", hit.id);
                Map<String, List<String>> highlight = hit.highlight;
                for (String key : highlight.keySet()) {
                    json.put(key, highlight.get(key).get(0));
                }
                return json;
            }).collect(toList());

            return Result.getResult(Constant.SUCCESS, "elasticsearch查询成功", data, searchResult.getTotal().intValue());
        } catch (IOException e) {
            log.error("elasticsearch查询异常", e);
            return Result.getResult(Constant.ERROR, "elasticsearch查询异常");
        }
    }


    @Resource
    public void setJestClient(JestClient jestClient) {
        ElasticUtil.jestClient = jestClient;
    }
}

 

3、抽象出一个类,封装查询时要传递的各种参数

 

@Data
public class ElasticSearchBean implements Serializable {
    private static final long serialVersionUID = -5188975204258390540L;
    /**
     * 索引
     */
    private String index;
    /**
     * 类型
     */
    private String type;
    /**
     * 要查询关键字
     */
    private String keyword;
    /**
     * 从第几行开始
     */
    private Integer from;
    /**
     * 查询几条数据
     */
    private Integer size;
    /**
     * 高亮字段
     */
    private List<String> highLightsFields;
    /**
     * 排序字段
     */
    private List<Sort> sortFields;
    /**
     * 是否是全文匹配,true:是;false;不是
     */
    private boolean isFullTextMatching;
    /**
     * 要匹配的字段
     */
    private List<String> MatchingFields;

}

4、测试

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = SswApplication.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class ElasticSearchTest {
    @Test
    public void saveTest() {
        User user = new User();
        user.setRealName("王五");
        user.setLoginName("wangwu");
        user.setAge(19);
        user.setGender(true);
        user.setStatus(0);
        Result result = ElasticUtil.save(user, "user", "user");
        System.out.println(result.getFlag());
        System.out.println(result.getMsg());
    }
    @Test
    public void updateTest() {
        User user = new User();
        user.setRealName("王五2号");
        user.setLoginName("wangwu");
        user.setAge(19);
        user.setGender(true);
        user.setStatus(0);
        Result result = ElasticUtil.updateById(user, "user", "user","AWLxcPgxbdesqcfPx6TL");
        System.out.println(result.getFlag());
        System.out.println(result.getMsg());
    }
    @Test
    public void deleteTest() {
        Result result = ElasticUtil.deleteById("user", "user","AWLxcPgxbdesqcfPx6TL");
        System.out.println(result.getFlag());
        System.out.println(result.getMsg());
    }
    @Test
    public void searchTest() {
        Class clazz = User.class;
        String index = "user";
        String type = "user";
        String keyword = "王";
        int from = 0;
        int size = 10;
        List<String> highLightsFields = Arrays.asList(new String[]{"realName","loginName"});
        List<Sort> sortFields = Arrays.asList(new Sort[]{new Sort("realName",Sort.Sorting.DESC),new Sort("loginName",Sort.Sorting.ASC)});
        List<String> matchingFields = Arrays.asList(new String[]{"password","email"});
        ElasticSearchBean elasticSearchBean = new ElasticSearchBean();
        elasticSearchBean.setIndex(index);
        elasticSearchBean.setType(type);
        elasticSearchBean.setKeyword(keyword);
        elasticSearchBean.setFrom(from);
        elasticSearchBean.setSize(size);
        elasticSearchBean.setHighLightsFields(highLightsFields);
        elasticSearchBean.setFullTextMatching(true);
        elasticSearchBean.setSortFields(sortFields);
        elasticSearchBean.setMatchingFields(matchingFields);


        Result result = ElasticUtil.get(clazz, elasticSearchBean);
        System.out.println(result.getFlag());
        System.out.println(result.getMsg());
        System.out.println(result.getTotal());
        System.out.println(result.getData());
    }
}

注意,在排序的时候,有时候会报异常

Fielddata is disabled on text fields by default. Set fielddata=true on [realName] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory.

这是因为elasticsearch对于text类型的字段,默认是不支持排序的,需要设置,方法如下:

发一个put请求:

http://localhost:9200/index/_mapping/type

请求体为

{
	"properties": {
		"realName": {
			"type": "text",
			"fielddata": true
		}
	}
}

如果返回

{"acknowledged":true}

说明设置成功,再排序就没有问题了

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
核心功能 文章/图片/视频发布、喜欢、统计阅读次数。 文章标签tag功能、支持按tag分类 文章支持ueditor/markdown编辑器切换(后台配置) 评论功能,支持回复,支持表情。 第三方(微博、QQ)登录。 lucene实现的站内搜索。 响应式布局 支持用户订阅 先看效果图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) http://localhost:8080/admin/group/list SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)SpringBoot开发非常美观的java博客系统(包含后台管理功能) 技术选型: JDK8 数据库MySQL 主框架 (Spring-bootSpring-data-jpa) 安全权限 Shiro 搜索工具 Lucene 缓存 Ehcache 视图模板 Freemarker 其它 Jsoup、fastjson jQuery、Seajs Bootstrap 前端框架 UEditor/Markdown编辑器 font-Awesome 字体/图标 准备工作(sql文件在项目里面) 安装 Jdk8 安装 Maven 准备 IDE (如果你不看源码,可以忽略下面的步骤,直接通过Maven编译war包:mvn clean package -DskipTests) IDE 需要配置的东西 编码方式设为UTF-8 配置Maven 设置Jdk8 关于这些配置,网上有一大把的资料,所以此处不再重复。 获取代码导入到IDE 下载代码 导入到IDE的时候请选择以Maven的方式导入 项目配置参考 系统配置手册 配置完毕 启动项目,在控制台看到Mblog加载完毕的信息后,表示启动成功 打开浏览器输入:http//localhost/mblog/ (此处仅是示例,具体具体端口因人而异),访问成功即部署完毕 后台管理的地址是 /admin, 如果你是管理员账号点导航栏的头像会看到"后台管理" 启动成功后,你应该去后台的系统配置里配置你的网站信息等。 常见问题总结 进入系统后, 菜单加载不出来, 那应该是你没有导 db_init.sql 点标签显示乱码, 请设置Tomcat的 URIEncoding 为 UTF-8 项目截图 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 转自:https://gitee.com/mtons/mblog SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注意: 一、java main方式运行mblog-web下的BootApplication.java时抛出异常的解决方案 Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean. SpringBoot开发非常美观的java博客系统(包含后台管理功能) 注释掉后下面图片的这段后,记得maven要重新reimport SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 否则maven依赖不生效还是会抛出以上的异常 二、第三方登录点击后无响应,那是因为第三方开放平台回调的url失效导致,需要你去对应的第三方开放平台注册app后获取对应的oauth帐号 SpringBoot开发非常美观的java博客系统(包含后台管理功能) 三、idea以maven项目导入该项目后,发现没有maven的依赖包时,需要对每个maven module进行clear和install,并且注意maven的依赖顺序 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能) 四、访问地址是http://localhost:8080 登录时,帐号,密码只要自己找个密码,然后md5下在更新到db中即可登录成功。 比如:zuidaima 111111,md5后密码是 3931MUEQD1939MQMLM4AISPVNE,md5的java类 SpringBoot开发非常美观的java博客系统(包含后台管理功能) SpringBoot开发非常美观的java博客系统(包含后台管理功能)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值