1.ES持久层技术
编辑
1.1 ES应用场景
编辑
1全文搜索
Elasticsearch可以用于实现全文搜索功能,例如搜索引擎、文档管理系统、电子商务搜索等。它支持复杂的查询语句、中文分词、近似搜索等功能,可以快速地搜索并返回匹配的结果。
编辑
2日志分析
Elasticsearch可以用于实现实时日志分析,例如监控系统、异常日志分析等。它可以快速地索引和搜索大量的日志数据,并支持聚合、可视化等功能,可以帮助用户快速定位和解决问题。 ELK
3业务分析
Elasticsearch可以用于实现业务分析,例如企业数据分析、市场调研等。它可以对海量数据进行搜索、聚合和分析,支持多种数据格式和数据源,例如数据库、日志、网页等,可以帮助用户了解业务情况、市场趋势等。
4搜索推荐
Elasticsearch可以用于实现搜索推荐功能,例如电商搜索推荐、新闻推荐等。它可以根据用户的搜索历史、行为等数据,进行个性化推荐,并支持实时更新和调整推荐结果。
5地理信息系统
Elasticsearch可以用于实现地理信息系统,例如地图搜索、位置分析等。它支持地理坐标索引和查询,可以快速地搜索和聚合地理数据,并支持地图可视化等功能。 GEO
1.2 Elasticsearch持久层技术盘点
- Elasticsearch官方提供的Java客户端 : Elasticsearch官方提供了Java客户端(High-Level REST Client和Low-Level REST Client),可以通过Java代码与ES进行交互。这些客户端提供了各种API和方法,用于执行索引、搜索、更新、删除等操作,并且支持与ES集群的连接和通信。
- Spring Data Elasticsearch : Spring Data Elasticsearch是SpringData框架提供的一个模块,用于简化与Elasticsearch的集成和操作。对于比较简单的查询操作,Spring Data Elasticsearch可以比较简便地实现查询功能,但是对于比较复杂的查询,Spring Data Elasticsearch使用起来依然比较繁琐,而且它的版本更新速度跟不上 Elasticsearch官方版本的更新速度,所以企业应用较少。
- Jest: Jest是一个开源的Java HTTP客户端库,专门用于与Elasticsearch进行交互。它提供了一组易于使用的API和方法,可以执行索引、搜索、更新、删除等操作。Jest具有良好的可扩展性和灵活性,并且支持与ES集群的连接和通信。
- Transport Client (已弃用): Transport Client是Elasticsearch早期版本中提供的Java客户端,自Elasticsearch 7.0版本起,Transport Client已被官方弃用。
- Elegent Data Elasticsearch :Elegent Data Elasticsearch是Elegent Data的子框架。Elegent Data是传智教育研究院开发的一款针对NoSQL的持久层框架。Elegent Data Elasticsearch 改变了大家头疼的Elasticsearch持久化操作的难题。因为它可以让用户按照myBatisPlus的风格来操作Elasticsearch,轻松上手。
使用Elegent Data Elasticsearch框架,可以让你的程序更优雅。我们选择Elegent Data Elasticsearch框架。
1.3 Elegent Data
1.3.1 Elegent Data 简介
Elegent Data是一个优雅的NoSQL数据持久层框架。
(1)使用这个组件可以让你更轻松、更优雅地在项目中操作 Elasticsearch 等NoSQL数据库,风格类似于MyBatisPlus,让你更专注业务代码的开发。
(2)支持用户自行扩展。
开源项目地址 :gitee.com/chuanzhiliu...
我们看一下Elegent Data框架的系统架构图:
编辑
1.3.2 Elegent Data Elasticsearch快速入门
(1)在pom文件中添加依赖
xml
<dependency>
<groupId>cn.elegent.data</groupId>
<artifactId>elegent-data-elasticsearch7</artifactId>
<version>1.0.0</version>
</dependency>
(2)application.yml添加配置
less
spring:
elasticsearch:
rest:
uris: http://127.0.0.1:9201
(3)创建DTO
typescript
@Data
public class OrderDTO {
private Long id;//id
private String orderNo;//订单编号
private String innerCode;//机器编号
private String addr;//点位地址
private Long skuId;//商品id
private String skuName;//商品名称
private Integer status;//订单状态:0-创建;1-支付完成;2-出货成功;3-出货失败;
private Integer amount;//支付金额
private Integer bill;//分账金额
private Integer price;//商品金额
}
(4)创建服务类
scala
@Component
public class OrderEsService extends Elasticsearch7DataService<OrderDTO> {
}
(5)创建Controller类,测试分页查询、列表查询、增加、删除、修改操作。
typescript
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderEsService orderEsService;
@GetMapping("/page")
public Pager<OrderDTO> findPage(){
ElegentQueryWapper elegentQueryWapper=new ElegentQueryWapper();
elegentQueryWapper.from("order")
.eq("status","1")
.eq("sku_name","统一奶茶");
return orderEsService.page(elegentQueryWapper,1,10);
}
@GetMapping("/list")
public List<OrderDTO> findList(){
ElegentQueryWapper elegentQueryWapper=new ElegentQueryWapper();
elegentQueryWapper.from("order");
return orderEsService.list(elegentQueryWapper);
}
@GetMapping("/statistics")
public List<StatisticsVo> findStatistics(){
ElegentQueryWapper queryWapper=new ElegentQueryWapper();
queryWapper.from("order")
.eq("status","1")
.count("price").groupBy("sku_id");
return orderEsService.statistics(queryWapper);
}
@GetMapping("/save")
public void save(){
OrderDTO orderDTO=new OrderDTO();
orderDTO.setId(200L);
orderDTO.setAddr("测试地址");
orderDTO.setStatus(1);
orderEsService.save("order",orderDTO.getId()+"",orderDTO);
}
@GetMapping("/update")
public void update(){
OrderDTO orderDTO=new OrderDTO();
orderDTO.setId(200L);
orderDTO.setAddr("地址测试");
orderDTO.setStatus(2);
orderEsService.update("order",orderDTO.getId()+"",orderDTO);
}
@GetMapping("/delete")
public void delete(){
orderEsService.delete("order","200");
}
@GetMapping("/findById/{id}")
public OrderDTO findById( @PathVariable("id") String id){
OrderDTO order = orderEsService.findById("order", id);
return order;
}
}
2.订单搜索
2.1 需求分析
用户在微信小程序中能够根据日期范围检索查询历史订单。
编辑
除了小程序,还有管理后台也需要订单查询功能。
编辑
2.2 实现思路
(1)在订单微服务,封装查询逻辑,通过使用ElegentData查询elasticsearch中的订单数据。
(2)在C端网关路由到订单微服务
2.3 代码实现
2.3.1 订单微服务搜索功能的实现
(1)在订单微服务的pom文件中,添加依赖
xml
<dependency>
<groupId>cn.elegent.data</groupId>
<artifactId>elegent-data-elasticsearch7</artifactId>
<version>1.0.0</version>
</dependency>
(2)在订单微服务的配置中(配置中心),添加以下配置
less
spring:
elasticsearch:
rest:
uris: http://127.0.0.1:9201
(3)创建搜索服务类 OrderEsService
scala
@Component
public class OrderEsService extends Elasticsearch7DataService<OrderVO> {
}
(4)OrderService新增方法
javascript
/**
* 搜索订单
* @param pageIndex
* @param pageSize
* @param orderNo
* @param openId
* @param startDate
* @param endDate
* @return
*/
Pager<OrderVO> search(Integer pageIndex, Integer pageSize, String orderNo, String openId, String startDate, String endDate);
(5)OrderServiceImpl实现此方法
typescript
@Autowired
private OrderEsService orderEsService;
@Override
public Pager<OrderVO> search(Integer pageIndex, Integer pageSize, String orderNo, String openId, String startDate, String endDate) {
ElegentQueryWapper elegentQueryWapper=new ElegentQueryWapper();
elegentQueryWapper.from("order");
if(orderNo!=null){
elegentQueryWapper.eq("order_no",orderNo);
}
if(openId!=null){
elegentQueryWapper.eq("open_id",openId);
}
if(startDate!=null && endDate!=null){
elegentQueryWapper.between( "create_time",startDate, endDate );
}
return orderEsService.page(elegentQueryWapper, pageIndex, pageSize);
}
(6)OrderController调用 service的search方法
less
/**
* 搜索
* @param pageIndex
* @param pageSize
* @param orderNo
* @param openId
* @param startDate
* @param endDate
* @return
*/
@GetMapping("/search")
public Pager<OrderVO> search(
@RequestParam(value = "pageIndex",required = false,defaultValue = "1") Integer pageIndex,
@RequestParam(value = "pageSize",required = false,defaultValue = "10") Integer pageSize,
@RequestParam(value = "orderNo",required = false,defaultValue = "") String orderNo,
@RequestParam(value = "openId",required = false,defaultValue = "") String openId,
@RequestParam(value = "startDate",required = false,defaultValue = "") String startDate,
@RequestParam(value = "endDate",required = false,defaultValue = "") String endDate){
return orderService.search(pageIndex,pageSize,orderNo,openId,startDate,endDate);
}
2.3.2 C端网关路由配置
yaml
spring:
cloud:
gateway:
routes:
#订单微服务
- id: order
uri: lb://order-service
predicates:
- Path=/order/**
filters:
2.4 测试
VSCODE的测试脚本
css
GET http://{{hostname}}:{{port}}/order/search?pageIndex=1&pageSize=10&openId=oJ9WJ5MhIS-hiwuUX0GmsHDzqTyQ&startDate=2020-01-01&endDate=2023-12-31 HTTP/1.1
Content-Type: {{contentType}}
3. 数据同步技术
我们现在已经实现了ES数据的查询功能,但是ES数据从哪里来呀?这就需要数据同步技术实现数据从Mysql到ES的搬运。
编辑
3.1 技术方案选型
3.1.1 MQ异步通知
MQ(消息队列)异步通知是一种常见的异步通信模式,用于在分布式系统中实现解耦和异步处理。它通过将消息发送到消息队列中,然后由消费者从队列中获取消息并进行处理,实现了消息的异步传递和处理。
我们可以在数据的增删改方法操作上,发送数据MQ, 消费者接受数据后进行数据的同步处理。
3.1.2 canal
阿里的Canal是一种开源的分布式数据库复制与实时数据订阅系统。它由阿里巴巴集团开发,旨在解决大规模分布式数据库的数据同步和实时数据订阅的需求。
Canal基于MySQL的主从复制原理,通过解析MySQL的binlog日志来实现数据的增量订阅和同步。它支持多种订阅方式,包括基于数据库表的增量订阅、基于全局事务的增量订阅以及基于时间点的全量订阅。Canal可以将数据库的变更数据实时地推送给订阅者,使得订阅者能够实时获取到数据库的最新数据。
3.1.3 Elegent Pipe
传智教育研究院研发的一款数据同步框架。它的实现原理与Canal是类似的,都是基于MySQL的主从复制原理,通过解析MySQL的binlog日志来实现数据的增量订阅和同步。与Canal不同的是,Elegent Pipe 是分为服务端和客户端两个部分,服务端处理监听后,通过ElegentAC将数据库变动的内容发送给客户端,所以ElegentPipe 更适合微服务架构应用程序的开发。
开源项目地址: gitee.com/chuanzhiliu...
3.2 Elegent Pipe快速入门
3.2.1 开启binlog
mysql8是默认开启binlog的 ,mysql5.7默认不开启.
我们可以通过以下命令查看binlog是否开启
sql
SHOW VARIABLES LIKE '%log_bin%';
要开启MySQL的binlog,您可以按照以下步骤进行操作:
- 编辑MySQL的配置文件
my.cnf或my.ini,具体位置根据您的操作系统和MySQL版本而定。 - 找到并修改以下参数:
ini
[mysqld]
log-bin=mysql-bin
server-id=1
-
log-bin:指定binlog日志文件的前缀名称,可以根据需要自定义。server-id:指定MySQL实例的唯一标识,每个MySQL实例需要有不同的server-id。
- 保存并退出配置文件。
- 重启MySQL服务,以使配置生效。
开启binlog后,MySQL将开始记录所有的数据库更改操作,并将其写入binlog文件中。这些binlog文件可以用于数据恢复、数据备份、数据同步等用途。
请注意,在修改MySQL配置文件之前,请确保您对MySQL有足够的权限,并且备份了重要的数据。此外,开启binlog会增加MySQL的写入负载,因此在生产环境中,应该根据系统的性能和资源情况进行评估和调整。
3.2.2 代码实现
(1)在服务端的工程引入依赖 (由于ElegentPipe依赖ElegentAC,所以还要引入elegent-AC-mqtt )
xml
<dependency>
<groupId>cn.elegent.pipe</groupId>
<artifactId>elegent-pipe-server</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>cn.elegent.ac</groupId>
<artifactId>elegent-AC-mqtt</artifactId>
<version>1.1.0</version>
</dependency>
(2)在服务端的配置引入依赖
yaml
elegent:
pipe:
host: 127.0.0.1
port: 3306
username: root
passwd: HuangShu_2023
tables:
- demo:users
ac:
host: 127.0.0.1
port: 1883
clientId: transmitServer
username: admin
password: public
keepAliveInterval: 30
connectionTimeout: 60
server:
port: 8888
(3)在客户端工程引入依赖
xml
<dependency>
<groupId>cn.elegent.pipe</groupId>
<artifactId>elegent-pipe-client</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>cn.elegent.ac</groupId>
<artifactId>elegent-AC-mqtt</artifactId>
<version>1.1.0</version>
</dependency>
(4)在客户端工程的配置文件添加
yaml
elegent:
ac:
host: 127.0.0.1
port: 1883
clientId: transmitClient
username: admin
password: public
keepAliveInterval: 30
connectionTimeout: 60
(5)在客户端工程添加类用于处理数据增删改之后的操作
typescript
@ElegentPipe(db="demo",table = "users")
public class UserTransmit implements PipeService {
@Override
public void insertHandler(TransmitDTO transmitDTO) {
System.out.println("修改前数据:"+transmitDTO.getBefore());
System.out.println("修改后数据:"+transmitDTO.getAfter());
}
@Override
public void updateHandler(TransmitDTO transmitDTO) {
System.out.println("修改前数据:"+transmitDTO.getBefore());
System.out.println("修改后数据:"+transmitDTO.getAfter());
}
@Override
public void deleteHandler(TransmitDTO transmitDTO) {
System.out.println("修改前数据:"+transmitDTO.getBefore());
System.out.println("修改后数据:"+transmitDTO.getAfter());
}
}
3.2.3 常见错误
Java监听mysql的binlog 报错解决办法
报错:com.github.shyiko.mysql.binlog.network.AuthenticationException: Client does not support authentication protocol requested by server; consider upgrading MySQL client
这是你的mysql认证规则不正确导致的。
解决方案:在mysql中执行以下命令
sql
alter user 'root'@'localhost' identified with mysql_native_password by '密码'; --修改认证规则
flush privileges; --刷新权限
4. 订单数据同步
4.1 需求分析
将订单库中的订单表数据,同步到ES中
4.2 实现思路
(1)创建数据同步的服务端模块,引入elegent-pipe-server以及Springboot依赖
(2)在服务端模块配置文件中,配置监听dkd_order库的tb_order表。
(3)在订单微服务引入elegent-pipe-client
(4)编写数据处理类,接收数据,并通过Elegent Data Elasticsearch将数据保存到 elasticsearch中。
4.3 代码实现
4.3.1 构建数据同步服务
(1)创建dkd_pipe_service模块,Pom文件引入elegent-pipe-server以及Springboot依赖
xml
<dependency>
<groupId>cn.elegent.pipe</groupId>
<artifactId>elegent-pipe-server</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>cn.elegent.ac</groupId>
<artifactId>elegent-AC-mqtt</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(2)创建配置文件 application.yml
yaml
elegent:
pipe:
host: 127.0.0.1
port: 3306
username: root
passwd: HuangShu_2023
tables:
- dkd_order:tb_order
ac:
host: 127.0.0.1
port: 1883
clientId: transmitServer
username: admin
password: public
keepAliveInterval: 30
connectionTimeout: 60
spring:
application:
name: pipe-service
server:
port: 8999
(3)添加启动类
typescript
@SpringBootApplication
public class TransmitServerApplication {
public static void main(String[] args){
SpringApplication.run(TransmitServerApplication.class, args);
}
}
4.3.2 同步逻辑的实现
(1)订单微服务 添加依赖
xml
<dependency>
<groupId>cn.elegent.pipe</groupId>
<artifactId>elegent-pipe-client</artifactId>
<version>1.1.0</version>
</dependency>
(2)订单微服务 创建数据同步处理类
typescript
/**
* 接受订单数据同步
*/
@ElegentPipe(db="dkd_order",table = "tb_order")
@Slf4j
public class OrderPipe implements PipeService {
@Autowired
private OrderEsService orderEsService;
@Override
public void insertHandler(TransmitDTO transmitDTO) {
Map<String, Serializable> orderMap = transmitDTO.getAfter();
orderEsService.save("order", (Long)orderMap.get("id")+"",orderMap );
log.info("ES插入订单数据{}",orderMap);
}
@Override
public void updateHandler(TransmitDTO transmitDTO) {
Map<String, Serializable> orderMap = transmitDTO.getAfter();
orderEsService.update("order",(Long)orderMap.get("id")+"",orderMap );
log.info("ES修改订单数据{}",orderMap);
}
@Override
public void deleteHandler(TransmitDTO transmitDTO) {
Map<String, Serializable> orderMap = transmitDTO.getBefore();
orderEsService.delete("order",(Long)orderMap.get("id")+"");
log.info("ES删除订单数据{}",orderMap);
}
}