【Lilishop商城】No2-4.确定软件架构搭建三(本篇包括ES检索)

news2025/7/20 21:03:38

  仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在:

【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客


全篇只介绍重点架构逻辑,具体编写看源代码就行,读起来也不复杂~

谨慎:源代码中有一些注释是错误的,有的注释意思完全相反,有的注释对不上号,我在阅读过程中就顺手更新了,并且在我不会的地方添加了新的注释,所以在读源代码过程中一定要谨慎啊!

目录

A1.ES检索

B1.ES基本搭建

B2.更新系统日志的ES存储搭建(关联No2-3)

C1.ElasticsearchRepository操作ES方式

C2.ElasticsearchOperations操作ES方式

C3.直接使用RestHighLevelClient操作ES方式(待更新)

B3.es分词查询(待更新)

剩余内容:消息中间件AMQP、定时任务等


A1.ES检索

ES学习可以看这篇文章,特别详细:ElasticSearch从入门到精通,史上最全(持续更新,未完待续,每天一点点)

我用的苹果本搭建的es和Kibana,安转时用brew安装一直报错(提示版本问题,我的本版本低了安装不了最新的),之后就直接去官网下载对应的7.3的版本的,然后直接在 bin 里面运行的,也挺方便的。

系统使用的是spring整合的es ,spring-data-elasticsearch,用的版本是Elasticsearch7.x这个版本较7.x之前使用上有些改动,虽然我没有用过之前的,但是为了防止使用混淆,还是了解了一下。

其中有一点要清楚的是spring-data-elasticsearch封装操作ES有两种方式,最终都是使用es本身的RestHighLevelClient进行的操作~

能使用RestHighLevelClient尽量使用它。

主要原因是灵活性和更新速度,Spring 将 ElasticSearch 过度封装,让开发者很难跟 ES 的 DSL 查询语句进行关联。再者就是更新速度,ES 的更新速度是非常快,但是 spring-data-elasticsearch 更新速度比较缓慢。

 具体的看这篇文章,挺清晰的:springboot2.3配置与使用elasticsearch7.x

B1.ES基本搭建

(1.ElasticsearchRepository;2.ElasticsearchOperations;3.RestHighLevelClient)

springboot本身就自带了spring-boot-starter-data-elasticsearch依赖包,我们就不用依赖了。

该系统使用的也是7.x以上版本的es,这个的配置项就不能直接使用spring的了,已经过时了,所以我们就只需要配置RestHighLevelClient的bean和ElasticsearchOperations的bean就好了,我们需要给出自定义的Properties,当然如果直接写在配置类里也行,就是不建议~

1.在frameword模块中创建Elasticsearch所需要的参数类,并在业务模块中的 yml 里配置参数项;
2.添加 elasticsearch 配置,创建RestHighLevelClient bean;

# 1.在frameword模块中创建Elasticsearch所需要的参数类,并在业务模块中的 yml 里配置参数项;

//参数类,详见:cn.lili.elasticsearch.config.ElasticsearchProperties

#举例 management-api 的yml配置 /lilishop-master/manager-api/src/main/resources/application.yml

lili:
  data:
    # 自定义 elasticsearch 配置
    elasticsearch:
      # cluster集群
      cluster-name: elasticsearch
      # 当前连接节点所属集群的配置信息
      cluster-nodes: 127.0.0.1:9200
      index:
        # 默认分片的副本数
        number-of-replicas: 0
        # 默认主分片数
        number-of-shards: 3
       # 索引前缀
      index-prefix: lili
      # 协议
      scheme: http
#          account:
#            username: elastic
#            password: LiLiShopES
//2.添加 elasticsearch 配置,创建RestHighLevelClient bean;

//详见:cn.lili.elasticsearch.config.ElasticsearchConfig

@Slf4j
@Configuration
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {

    @Autowired
    private ElasticsearchProperties elasticsearchProperties;

    private RestHighLevelClient client;

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {

        RestClientBuilder restBuilder = RestClient
                .builder(this.getHttpHosts());

        //修改 HTTP 客户端通信,例如添加压缩或加密层。
        restBuilder.setHttpClientConfigCallback(httpClientBuilder ->
                httpClientBuilder
                        //自定义连接存活策略
                        .setKeepAliveStrategy(getConnectionKeepAliveStrategy())
                        .setMaxConnPerRoute(10).
                        setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(1).build()));
        String username = elasticsearchProperties.getAccount().getUsername();
        String password = elasticsearchProperties.getAccount().getPassword();

        //如果有认证用户,就添加用户信息
        if (username != null && password != null) {
            final CredentialsProvider credential = new BasicCredentialsProvider();
            credential.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));

            //修改 HTTP 客户端通信,例如添加压缩或加密层。
            restBuilder.setHttpClientConfigCallback(httpClientBuilder ->
                    httpClientBuilder
                            .setDefaultCredentialsProvider(credential)
                            //自定义连接存活策略
                            .setKeepAliveStrategy(getConnectionKeepAliveStrategy())
                            .setMaxConnPerRoute(10)
                            .setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(Runtime.getRuntime().availableProcessors()).build()));
        }

        //配置请求身份验证、超时和其他可以在请求级别设置的属性。
        restBuilder.setRequestConfigCallback(requestConfigBuilder ->
                requestConfigBuilder.setConnectTimeout(1000) //time until a connection with the server is established.
                        .setSocketTimeout(12 * 1000) //time of inactivity to wait for packets[data] to receive.
                        .setConnectionRequestTimeout(-1)); //time to fetch a connection from the connection pool 0 for infinite.

        // 创建ES客户端对象
        client = new RestHighLevelClient(restBuilder);
        return client;
    }

    /**
     * @Description: 配置集群服务器域名/ip
     * @param: []
     * @return: org.apache.http.HttpHost[]
     **/
    private HttpHost[] getHttpHosts() {
        List<String> clusterNodes = elasticsearchProperties.getClusterNodes();
        HttpHost[] httpHosts = new HttpHost[clusterNodes.size()];
        for (int i = 0; i < clusterNodes.size(); i++) {
            String[] node = clusterNodes.get(i).split(":");
            httpHosts[i] = new HttpHost(node[0], Convert.toInt(node[1]), elasticsearchProperties.getScheme());
        }
        return httpHosts;
    }

    private ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
        return (response, context) -> 2 * 60 * 1000;
    }

    /**
     * it gets called when bean instance is getting removed from the context if
     * scope is not a prototype
     * If there is a method named shutdown or close then spring container will try
     * to automatically configure them as callback methods when bean is being
     * destroyed
     */
    @PreDestroy
    public void clientClose() {
        try {
            this.client.close();
        } catch (IOException e) {
            log.error("es clientClose错误", e);
        }
    }

}

我们在 config 里面创建了 RestHighLevelClient bean ,同时 config 又实现了 AbstractElasticsearchConfiguration ,在这个里面又创建了ElasticsearchOperations。

之后使用 ElasticsearchOperations 正常注入使用就好啦,见下面B2

ElasticsearchRepository也可以正常实现使用,见下面B2

B2.更新系统日志的ES存储搭建(关联No2-3)

之前No2-3里面系统日志框架中我们已经写了 service 类了,现在需要将具体的日志存储给加上。

C1.ElasticsearchRepository操作ES方式

集成ElasticsearchRepository方式就相当于 mps 的BaseMapper,也需要对应的实体类,实体类里面需要使用@Document标注索引。然后直接创建service类继承ElasticsearchRepository,然后直接使用就可以。

这种方式对于增删改比较方便~

1.系统日志实体类;要加上@Document标注索引,否则使用时会报错

2.系统日志Repository类,实现ElasticsearchRepository类并泛型指定为系统日志实体类;

3.系统日志业务类;

//1.系统日志实体类;要加上@Document标注索引,否则使用时会报错

//详见:cn.lili.modules.permission.entity.vo.SystemLogVO

@Data
//加上了@Document注解之后,默认情况下这个实体中所有的属性都会被建立索引、并且分词
//indexName 索引库的名称,个人建议以项目的名称命名;
@Document(indexName = "#{@elasticsearchProperties.indexPrefix}_" + EsSuffix.LOGS_INDEX_NAME)
@ToString
@NoArgsConstructor
public class SystemLogVO implements Serializable {

。。。
}
//2.系统日志Repository类,实现ElasticsearchRepository类;
//详见:cn.lili.modules.permission.entity.vo.SystemLogVO
/**
 * 日志
 *
 * @author paulG
 * @since 2021/12/13
 **/
public interface SystemLogRepository extends ElasticsearchRepository<SystemLogVO, String> {

}
//2.系统日志业务类;

//详见:cn.lili.modules.permission.service.SystemLogService
//详见:cn.lili.modules.permission.serviceimpl.SystemLogServiceImpl

//举例,
@Service
public class SystemLogServiceImpl implements SystemLogService {

    @Autowired
    private SystemLogRepository systemLogRepository;

    /**
     * ES
     */
    @Autowired
    private ElasticsearchOperations restTemplate;

    @Override
    public void saveLog(SystemLogVO systemLogVO) {
        systemLogRepository.save(systemLogVO);
    }
。。。
}

此时就可以直接调用管理员登录接口 /manager/passport/user/login 了,调用过程中会经过系统日志切面类,进而在保存日志的线程类中执行系统日志save的业务。

登录成功后,就直接去 Kibana里面搜索查看,就能查看到当前日志啦

C2.ElasticsearchOperations操作ES方式

ElasticsearchOperations方式更简单,由于我们已经通过config 创建了该 bean,所以直接在业务类中注入该bean就行,然后直接使用~

这种方式对于查询较方便

还是在SystemLogServiceImpl系统日志业务类中使用,查询日志列表;

//详见:cn.lili.modules.permission.serviceimpl.SystemLogServiceImpl

@Service
public class SystemLogServiceImpl implements SystemLogService {

    @Autowired
    private SystemLogRepository systemLogRepository;

    /**
     * ES
     */
    @Autowired
    private ElasticsearchOperations restTemplate;


    @Override
    public IPage<SystemLogVO> queryLog(String storeId, String operatorName, String key, SearchVO searchVo, PageVO pageVO) {
        pageVO.setNotConvert(true);
        IPage<SystemLogVO> iPage = new Page<>();

        //查询条件构造器,可以往里面添加一些过滤或者其他条件
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        if (pageVO.getPageNumber() != null && pageVO.getPageSize() != null) {
            int pageNumber = pageVO.getPageNumber() - 1;
            if (pageNumber < 0) {
                pageNumber = 0;
            }
            Pageable pageable = PageRequest.of(pageNumber, pageVO.getPageSize());
            //添加分页条件
            nativeSearchQueryBuilder.withPageable(pageable);
            iPage.setCurrent(pageVO.getPageNumber());
            iPage.setSize(pageVO.getPageSize());
        }

        if (CharSequenceUtil.isNotEmpty(storeId)) {
            //添加查询条件
            nativeSearchQueryBuilder.withFilter(QueryBuilders.matchQuery("storeId", storeId));
        }

        if (CharSequenceUtil.isNotEmpty(operatorName)) {
            //添加查询条件
            nativeSearchQueryBuilder.withFilter(QueryBuilders.wildcardQuery("username", "*" + operatorName + "*"));
        }

        if (CharSequenceUtil.isNotEmpty(key)) {
            BoolQueryBuilder filterBuilder = new BoolQueryBuilder();
            filterBuilder.should(QueryBuilders.wildcardQuery("requestUrl", "*" + key + "*"))
                    .should(QueryBuilders.wildcardQuery("requestParam", "*" + key + "*"))
                    .should(QueryBuilders.wildcardQuery("responseBody", "*" + key + "*"))
                    .should(QueryBuilders.wildcardQuery("name", "*" + key + "*"));
            //添加查询条件
            nativeSearchQueryBuilder.withFilter(filterBuilder);
        }
        //时间有效性判定
        if (searchVo.getConvertStartDate() != null && searchVo.getConvertEndDate() != null) {
            BoolQueryBuilder filterBuilder = new BoolQueryBuilder();
            //大于方法
            filterBuilder.must(
                    QueryBuilders.rangeQuery("createTime")
                            .gte(DateUtil.format(searchVo.getConvertStartDate(), "dd/MM/yyyy"))
                            .lte(DateUtil.format(searchVo.getConvertEndDate(), "dd/MM/yyyy")).format("dd/MM/yyyy||yyyy"));
            //添加查询条件
            nativeSearchQueryBuilder.withFilter(filterBuilder);
        }

        if (CharSequenceUtil.isNotEmpty(pageVO.getOrder()) && CharSequenceUtil.isNotEmpty(pageVO.getSort())) {
            //添加排序条件
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(pageVO.getSort()).order(SortOrder.valueOf(pageVO.getOrder().toUpperCase())));
        } else {
            //添加排序条件
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));
        }

        //执行查询
        SearchHits<SystemLogVO> searchResult = restTemplate.search(nativeSearchQueryBuilder.build(), SystemLogVO.class);

        iPage.setTotal(searchResult.getTotalHits());

        iPage.setRecords(searchResult.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList()));
        return iPage;
    }

}

注意,这里在正常业务中需要添加两个 VO ,接收前端请求传过来的日期搜索参数SearchVO和分页查询参数PageVO

【如果现在添加了,会有一系列工具类需要处理,需要添加自定义DateUtil、StringUtils】

//日期搜索参数SearchVO,详见:cn.lili.common.vo.SearchVO
//分页查询参数PageVO,详见:cn.lili.common.vo.PageVO

 添加好controller、service后,调用接口执行~记得添加token

C3.直接使用RestHighLevelClient操作ES方式(待更新)

这种更适用于查询。并且是比较推荐的。会涉及到高亮等等

我们在后面商品查询时会用到,这里先略过

B3.es分词查询(待更新)

剩余内容:消息中间件AMQP、定时任务等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows文本绘制 TextOut、DrawText、CreateFont、SetTextColor、SetBkColor、SetBkMode

文本绘制 TextOut-将文字绘制在指定坐标位置 DrawText-在矩形区域绘制字符串 int DrawText(HDC hdc, //DC句柄LPCSTR lpString, //字符串int nCount, //字符串长度LPRECT lpRect, //绘制文字的矩形框UINT uFormat //绘制的方式,重点&#xff0c;花样繁多的关键点 );绘制文字样…

持续集成和上传源码

1.测试左移&#xff0c;测试右移 2.持续集成 是指通过自动化的方式&#xff0c;频繁多次将代码集成到主干。 快速发现错误 每完成一点更新&#xff0c;就集成到主干&#xff0c;可以快速发现错误&#xff0c;定位错误也比较容易。 防止分支大幅偏离主干 如果不是经常集成&…

nginx源码分析--双端列表

1.基本数据结构 struct ngx_queue_s {ngx_queue_t *prev;ngx_queue_t *next; };结构成员: ngx_queue_t *prev;前驱指针 ngx_queue_t *next;后继指针 2.操作函数--头结点 2.1基本函数 define ngx_queue_init(q) \(…

七牛云 vue 图片上传简单解说,js 上传文件图片

七牛云 vue 图片上传简单解说&#xff0c;js 上传文件图片 一、七牛云简介 首次使用七牛云存储进行项目的图片存储&#xff0c;整了一上午才整明白&#xff0c;这些官方的教程把明白人也给说糊涂了&#xff0c;文档很不规范。 七牛云有免费的使用额度&#xff0c;https://ww…

[附源码]SSM计算机毕业设计汽车租赁管理系统-JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Go学习之旅:包、变量和函数(DAY 1)

文章目录前引包、变量和函数1、包的概念和所用2、导出名或者导出函数3.1、函数参数声明方式&#xff08;一&#xff09;3.2、函数参数声明方式&#xff08;二&#xff09;4、函数返回值支持多值返回5、函数命名返回值6、变量声明7、变量的基础类型8、变量的默认值&#xff08;零…

pytorch案例代码-3

双向循环神经网络 双向循环神经网络在RNN/LSTM/GRU里都有。比如RNN cell&#xff0c;只是把h0和x1传入做线性变换产生h1继续传入同一个cell做线性变换&#xff0c;线性变换的W和b共享&#xff0c;沿着这个方向就把所有隐层和最后的输出算出来了。 那么其中的每个结点&#xff0…

android-apk解包打包

title: android-apk解包打包 categories: Android tags: [android, 加壳] date: 2022-09-28 10:29:51 comments: false mathjax: true toc: true android-apk解包打包, 以下所有操作都需要在配置好 java 环境下进行 前篇 android apk解包和打包 - https://blog.csdn.net/u0114…

(十五)Spring之面向切面编程AOP

文章目录基础环境AOP介绍AOP的七大术语切点表达式Spring的AOP的使用环境准备基于AspectJ的AOP注解式开发通知类型前置通知Before后置通知AfterReturning环绕通知Around异常通知AfterThrowing最终通知After关于JoinPoint切面的先后顺序通用切点表达式全注解式开发AOP基于XML配置…

9、前端笔记-CSS-CSS三大特性

三大特性&#xff1a;层叠性、继承性、优先级 1、层叠性&#xff08;覆盖性&#xff09; 给相同的选择器设置相同的样式&#xff0c;此时一个样式会覆盖&#xff08;层叠&#xff09;其他冲突的样式。 层叠性原则&#xff1a; 同一选择器&#xff0c;样式冲突&#xff0c;遵…

【SpringBoot】MVC配置解决跨域但仍然存在跨域

文章目录1. 跨域问题出现与解决1. 跨域问题出现与解决 检查SpringBoot中的MVC配置。 public void addCorsMappings(CorsRegistry registry) {//允许跨域访问资源定义registry.addMapping("/**")//(只允许本地的指定端口访问)允许所有.allowedOrigins("*")…

数据结构之线性表中的单链表【详解】

文章目录前言&#xff1a;一、单链表1.单链表和顺序表的优缺点2.单链表的概念和学习3.单链表的各个接口的实现&#xff08;详解每一步&#xff09;3.1.先铺垫一下大致的思路3.2.然后这边我们看一下我们大致要实现的函数有哪些3.3.接下来我们就开始实现这些代码&#xff0c;并且…

用信号量实现进程同步与互斥(含代码分析)

信号量简单的来说就是一个变量&#xff0c;代表着系统中互斥资源的数量&#xff0c;通常用原语来实现对信号量机制的操作。 一对原语&#xff1a;wait(S)也称为P操作&#xff0c;singnal(S)也称为V操作。S表示信号量 对于wait原语本身的内部逻辑代码如下&#xff08;这里以一…

黑苹果外接显示器最优解决方案

黑苹果无法外接显示器 黑苹果外接显示器解决方案 先给解决方案 电脑端 > 安装 PC端 Duet Display买个二手电视盒子40块钱左右&#xff0c;还带电源电视盒子 > 安装安卓端 Duet Display&#xff08;U盘安装就行&#xff09;电视盒子 > 用鼠标开启开发者模式双头USB&…

SpringBoot SpringBoot 原理篇 1 自动配置 1.12 bean 的加载控制【注解式】

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇1 自动配置1.12 bean 的加载控制【注解式】1.12.1 问题引入1.12.2 bean的加载…

【JVM技术专题】「原理专题」全流程分析Java对象的创建过程及内存布局

前言概要 对应过程则是&#xff1a;对象创建、对象内存布局、对象访问定位的三个过程。 对象的创建过程 对象的创建方式 java中对象的创建方式有很多种&#xff0c;常见的是通过new关键字和反射这两种方式来创建。除此之外&#xff0c;还有clone、反序列化等方式创建。 通过n…

CSS @property,让不可能变可能

本文主要讲讲 CSS 非常新的一个特性&#xff0c;CSS property&#xff0c;它的出现&#xff0c;极大的增强的 CSS 的能力&#xff01; 根据 MDN -- CSS Property&#xff0c;property CSS at-rule 是 CSS Houdini API 的一部分, 它允许开发者显式地定义他们的 CSS 自定义属性&…

vue项目分环境配置打包处理

vue项目分环境配置打包处理 目录 vue项目分环境配置打包处理 本地环境配置 生产环境配置 打包处理 打包配置处理&#xff08;扩展&#xff09; 本地环境配置 定义需要的变量&#xff0c;这里定义了各种变量标识&#xff0c;可参考使用&#xff0c;自定义项目需要的变量&…

设计问卷调查有哪些技巧?

调查问卷可以很好地帮助我们进行市场调研&#xff0c;所以想要做出一份有效的调查问卷&#xff0c;首先要确定问卷的主题。明确的主题就是定基调&#xff0c;可以让我们的后续过程更加顺利。然后我们再开始进行题目的设置和问卷的设计等动作。不过&#xff0c;在问卷设计的过程…

【js】【爬虫】fetch + sessionStorage 快速搭建爬虫环境及各种踩坑

文章目录导读需求开发环境fetch介绍为什么选择fetchfetch的封装使用数据存储数据访问封装多页面处理方案数据过大&#xff0c;拆分处理参考资料导读 需求 一说爬虫&#xff0c;很多人都会向导python&#xff0c;不过&#xff0c;真正省心的方案&#xff0c;应当是通过js控制获…