• 使用redis 的订阅服务


    1.业务使用场景

    我们在使用表单动态添加字段,如果新增字段,再保存数据,这个时候就会出错,出错的原因是seata 再本地缓存元数据,修改物理表的时候,这个元数据并没有发生变化,因此需要刷新元数据,因为我们使用的是多服务实例的部署,因此,如果某个表发生变化时,所有的服务实例都需要将元数据进行刷新。

    这个我们可以使用 redis 的订阅服务,当某个微服务的元数据发生变化时,我们可以使用redis通知各个微服务实例对数据进行更改。

    redis 支持 发布订阅服务。
    他有两个端:

    1. 订阅端
      可以有多个订阅端,可以订阅某个频道,当消息发送端发送消息时,订阅端可以接收到消息进行处理
    2. 消息发送端
      可以方某个频道发送消息。

    2. 解决方案

    在每一个微服务实例启动的时候,我们启用订阅,当元数据发生变化时,我们发布事件进行通知微服务实例。

    相关代码:

    public class SubPubUtil {
    
        /**
         * 发布消息。
         * @param channel
         * @param message
         */
        public static void publishMessage(String channel,String message ){
            RedisTemplate<String, Object> redisTemplate= SpringUtil.getBean("redisTemplate");
            redisTemplate.execute((RedisCallback<Long>) connection -> {
                byte[] chanelBytes = channel.getBytes(StandardCharsets.UTF_8);
                byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
                connection.publish(chanelBytes,messageBytes);
                return 1L;
            });
        }
    
    
        /**
         * 订阅消息。
         * @param channel
         * @param listener
         */
        public static void subscribeMessage(String channel,MessageListener listener){
            RedisTemplate<String, Object> redisTemplate= SpringUtil.getBean("redisTemplate");
            redisTemplate.execute((RedisCallback<Long>) connection -> {
                byte[] chanelBytes = channel.getBytes(StandardCharsets.UTF_8);
                connection.subscribe(listener,chanelBytes);
                return 1L;
            });
        }
    
    
    }
    
    

    在微服务实例启动时,启用订阅。

    public class DbChangeListener  implements CommandLineRunner, Ordered {
    
    
        @Override
        public void run(String... args) throws Exception {
            SubPubUtil.subscribeMessage("dbChange", new MessageListener() {
                @SneakyThrows
                @Override
                public void onMessage(Message message, byte[] bytes) {
                    String dataSource =new  String(message.getBody(),"utf-8");
                    clearMedata(dataSource);
                }
            });
        }
    
        /**
         * 清理数据源的元数据。
         * @throws NoSuchFieldException
         * @throws IllegalAccessException
         */
        public void clearMedata(String dataSource) throws NoSuchFieldException, IllegalAccessException {
            DataSourceProxy dataSourceProxy = (DataSourceProxy) DataSourceUtil.getDataSourcesByAlias(dataSource);
            try (Connection connection = dataSourceProxy.getConnection()) {
                TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType());
    
                TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType())
                        .refresh(connection, dataSourceProxy.getResourceId());
            } catch (Exception ignore) {
            }
    
        }
    
        @Override
        public int getOrder() {
            return 0;
        }
    }
    
    

    当元数据发生变化时,我们可以使用如下代码发布事件,通知各个微服务实例,对元数据进行清理。

    public static void clearDbMetaData(String dataSource){
            if(StringUtils.isEmpty(dataSource)){
                dataSource=DataSourceUtil.LOCAL;
            }
            SubPubUtil.publishMessage("dbChange",dataSource);
        }
    
  • 相关阅读:
    让Vim查找字符忽略大小写
    How to Add a User to Sudoers on Ubuntu
    Docker 批量删除images
    解决 Laradock 安装时候出现 Can't open /home/laradock/.nvm/nvm.sh 的问题
    Add User To Docker Group In Ubuntu Linux
    Parted分区和创建逻辑卷LVM
    How To List Users and Groups on Linux
    How to Install Node.js and NPM on Mac OS
    linux中的alias命令详解
    Hadoop数据类型
  • 原文地址:https://www.cnblogs.com/yg_zhang/p/16721200.html
Copyright © 2020-2023  润新知