详解SpringCloud微服务技术栈:深入ElasticSearch(3)——数据同步(酒店管理项目)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:详解SpringCloud微服务技术栈:深入ElasticSearch(2)——自动补全、拼音搜索
📚订阅专栏:微服务技术全家桶
希望文章对你们有所帮助

数据同步,可以说是非常重要的,我看到的很多面经里面就有关于数据同步的问题。
在之前从MySQL中批量导入了酒店数据到ElasticSearch,因此当MySQL数据发生改变时,ElasticSearch也必须跟着改变,这就是ES与MySQL之间的数据同步。而之前在学习Redis的时候,这就是一个很重要的问题,即双写一致性问题。

同步方案分析

如果是一个单体项目,那么MySQL数据怎么懂,ElasticSearch也相应做变化就好了,但是现在我们使用的是微服务架构,酒店管理(MySQL)的业务和搜索酒店(ElasticSearch)的业务很可能都不在同一个微服务下,数据同步就没办法直接实现。有三种实现方案:

方案一:同步调用
在这里插入图片描述
但是这种方式,admin调用了demo,业务耦合度太高了,影响性能。
方案二:异步通知
当有新增的数据时,依旧先写MySQL,写入后是将消息放入到消息队列中,至于谁需要这个消息,就与admin业务没有关系了,有效解除了耦合,是比较推荐的方案,但是这种方案比较依赖MQ的可靠性,实现复杂度会上升。
在这里插入图片描述
方案三:监听binlog
学习MySQL主从同步的时候有接触过,MySQL中的binlog默认关闭,一旦开启,每当MySQL在做修改的时候,都会将相应的操作记录在binlog中,那么就可以使用canal这种中间件来监听binlog,并通知demo做更新。
在这里插入图片描述
耦合度确实很低,但是会对MySQL产生一定的压力。

在这里将会使用方案二。

导入酒店管理项目

之前一直在做hotel-demo,现在要导入hotel-admin来作为酒店管理的微服务,当数据发生增删改的时候,要对ElasticSearch中的数据完成相同操作。
hotel-admin项目从网盘中下载并导入工程:

链接:https://pan.baidu.com/s/19gdxEBRxpAXdaozyw3uJGQ?pwd=rzbg
提取码:rzbg

修改配置后即可启动自行测试。
在这里插入图片描述

声明队列和交换机

之前学习MQ的时候,要发送消息,就需要指定好交换机exchange、队列queue以及RoutingKey。
ES增和改的时候,实际上都是一样的语句(id不存在就新增,id存在就修改),所以在ES操作中,增和改可以算成是一个业务。
因此消息队列只有2个,一个为hotel.insert.queue,一个为hotel.delete.queue,且每一个队列都有一个唯一的RoutingKey,交换机为hotel.topic。

声明队列和交换机一般都是在消费者中进行的。
1、在hotel-demo、hotel-admin中引入依赖:

	<!--SpringAMQP-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>

2、在hotel-demo和hotel-admin配置中配置一下AMQP的地址:
在这里插入图片描述
3、hotel-demo与hotel-admin中定义一个常量类:

public class MqConstants {
    //交换机
    public final static String HOTEL_EXCHANGE = "hotel.topic";
    //监听新增或修改的队列
    public final static String HOTEL_INSERT_QUEUE = "hotel.insert.queue";
    //监听删除的队列
    public final static String HOTEL_DELETE_QUEUE = "hotel.delete.queue";
    //新增或修改的RoutingKey
    public final static String HOTEL_INSERT_KEY = "hotel.insert";
    //删除的RoutingKey
    public final static String HOTEL_DELETE_KEY = "hotel.delete";
}

4、在hotel-demo中基于bean的方式声明并绑定关系:

@Configuration
public class MqConfig {
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(MqConstants.HOTEL_EXCHANGE, true, false);
    }

    @Bean
    public Queue insertQueue(){
        return new Queue(MqConstants.HOTEL_INSERT_QUEUE, true);
    }

    @Bean
    public Queue deleteQueue(){
        return new Queue(MqConstants.HOTEL_DELETE_QUEUE, true);
    }

    @Bean
    public Binding insertQueueBinding() {
        return BindingBuilder.bind(insertQueue()).to(topicExchange()).with(MqConstants.HOTEL_INSERT_KEY);
    }

    @Bean
    public Binding deleteQueueBinding() {
        return BindingBuilder.bind(deleteQueue()).to(topicExchange()).with(MqConstants.HOTEL_DELETE_KEY);
    }
}

发送MQ消息

从hotel-admin中的增删改业务中增加消息发送到MQ的环节。原先这个项目导入的环节,业务逻辑都放在controller里面实现了,这种肯定是不规范的,业务逻辑应该全部放在service层中进行。我新增了Service层:

HotelController:

	@PostMapping
    public void saveHotel(@RequestBody Hotel hotel){
        hotelService.saveHotel(hotel);
    }

    @PutMapping()
    public void updateHotelById(@RequestBody Hotel hotel){
        hotelService.updateHotelById(hotel);
    }

    @DeleteMapping("/{id}")
    public void deleteHotelById(@PathVariable("id") Long id) {
        hotelService.deleteHotelById(id);
    }

IHotelService:

public interface IHotelService extends IService<Hotel> {
    void saveHotel(Hotel hotel);
    void updateHotelById(Hotel hotel);
    void deleteHotelById(Long id);
}

实现类HotelService:

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public void saveHotel(Hotel hotel) { //新增酒店并发送到消息队列
        save(hotel);
        rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_INSERT_KEY, hotel.getId());
    }

    @Override
    public void updateHotelById(Hotel hotel) { //修改酒店并发送到消息队列
        if (hotel.getId() == null) {
            throw new InvalidParameterException("id不能为空");
        }
        updateById(hotel);
        rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_INSERT_KEY, hotel.getId());
    }

    @Override
    public void deleteHotelById(Long id) { //删除酒店并发送到消息队列
        removeById(id);
        rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE, MqConstants.HOTEL_DELETE_KEY, id);
    }
}

监听MQ消息

消费者hotel-demo需要完成消息的监听并且更新ElasticSearch中的数据,而消息的监听都是通过编写实体类来实现的。

@Component
public class HotelListener {

    @Resource
    IHotelService hotelService;

    /**
     * 监听酒店新增或修改的业务
     * @param id 酒店id
     */
    @RabbitListener(queues = MqConstants.HOTEL_INSERT_QUEUE)
    public void listenHotelInsertOrUpdate(Long id) {
        hotelService.insertById(id);
    }

    /**
     * 监听酒店删除的业务
     * @param id 酒店id
     */
    @RabbitListener(queues = MqConstants.HOTEL_DELETE_QUEUE)
    public void listenHotelDelete(Long id) {
        hotelService.deleteById(id);
    }
}

之前在HotelService中没有实现着两个方法,所以需要在实现类中用RestClient实现:

	@Override
    public void deleteById(Long id) {
        try {
            //准备request
            DeleteRequest request = new DeleteRequest("hotel").id(id.toString());
            //发送请求
            client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void insertById(Long id) {
        try {
            //根据id查询酒店数据
            Hotel hotel = getById(id);
            //转换为文档类型
            HotelDoc hotelDoc = new HotelDoc(hotel);
            //准备request
            IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
            //准备DSL
            request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
            //发送请求
            client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

测试同步功能

重启hotel-demo和hotel-admin,查看MQ后台的交换机、消息队列以及他们是否绑定成功:
在这里插入图片描述
在酒店管理系统中修改上海希尔顿酒店的价格为2687,消息队列中存入了消息,且直接被消费者监听和消费掉了:
在这里插入图片描述

在这里插入图片描述
至此,数据同步成功完成,把业务也分离的很好。

  • 47
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布布要成为最负责的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值