用redis的消息订阅功能更新应用内的caffeine本地缓存

news2024/12/28 4:07:17

1、为什么要更新caffeine缓存?

1.1,caffeine缓存的优点和缺点

生产环境中,caffeine缓存是我们在应用中使用的本地缓存,
它的优势在于存在于应用内,访问速度最快,通常都不到1ms就能做出响应,
缺点在于不方便管理,因为存在于多台负载均衡的web服务器上,
很难象管理redis缓存一样对它做出更新、删除。

1.2,通常我们会把caffeine缓存的时间设置为5分钟或10分钟,

但当有大型促销活动开始时,如果缓存还没过期,
则web服务显示的数据不会立刻得到更新,
我们如何更新多台web服务器的的应用内缓存?
使用redis的消息订阅是解决方法之一,
我们从后台发送一条消息到redis,
订阅了redis的web服务收到消息可以对缓存进行处理,
这样实现对多台web服务器上的缓存的更新

1.3, 生产环境中通常会使用多级缓存,

我们在更新caffeine缓存时,
也不要去访问数据库,避免导致对数据库的并发访问,
而是更新完redis后,
本地缓存从redis获取数据,
而几百几千数量级的并发访问对于redis来说压力很小

2、演示项目的相关信息

2.1、项目地址

https://github.com/liuhongdi/redispubsub

2.2、项目功能说明:

web服务通过订阅redis的消息,
实现对缓存的更新/删除/清除

3,项目结构:如图:

在这里插入图片描述

4、配置文件说明

4.1、pom.xml

<!--redis begin-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.11.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.1</version>
        </dependency>
        <!--redis   end-->

        <!-- fastjson begin-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
        <!-- fastjson   end-->

        <!--local cache begin-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>2.8.5</version>
        </dependency>
        <!--local cache   end-->

        <!--mybatis begin-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--mybatis end-->

        <!--mysql begin-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--mysql end-->

4.2,application.properties

#error
server.error.include-stacktrace=always
#errorlog
logging.level.org.springframework.web=trace

#redis1
spring.redis1.host=127.0.0.1
spring.redis1.port=6379
spring.redis1.password=lhddemo
spring.redis1.database=0

spring.redis1.lettuce.pool.max-active=32
spring.redis1.lettuce.pool.max-wait=300
spring.redis1.lettuce.pool.max-idle=16
spring.redis1.lettuce.pool.min-idle=8

spring.redis1.enabled=1

#mysql
spring.datasource.url=jdbc:mysql://localhost:3306/store?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=lhddemo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#mybatis
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mybatis.type-aliases-package=com.example.demo.mapper
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#profile
spring.profiles.active=cacheenable

5、goods数据表的建表sql:

CREATE TABLE `goods` (
 `goodsId` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `goodsName` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT 'name',
 `subject` varchar(200) NOT NULL DEFAULT '' COMMENT '标题',
 `price` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '价格',
 `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock',
 PRIMARY KEY (`goodsId`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'

6、java代码说明

6.1、CacheConfig.java

@Profile("cacheenable")   //prod这个profile时缓存才生效
@Configuration
@EnableCaching //开启缓存
public class CacheConfig {
    public static final int DEFAULT_MAXSIZE = 10000;
    public static final int DEFAULT_TTL = 600;
    private SimpleCacheManager cacheManager = new SimpleCacheManager();

    //定义cache名称、超时时长(秒)、最大容量
    public enum CacheEnum{
        goods(60,1000),       //有效期600秒, 最大容量1000
        homePage(7200,1000),  //有效期2个小时 , 最大容量1000
        ;
        CacheEnum(int ttl, int maxSize) {
            this.ttl = ttl;
            this.maxSize = maxSize;
        }
        private int maxSize=DEFAULT_MAXSIZE;    //最大數量
        private int ttl=DEFAULT_TTL;        //过期时间(秒)
        public int getMaxSize() {
            return maxSize;
        }
        public int getTtl() {
            return ttl;
        }
    }

    //创建基于Caffeine的Cache Manager
    @Bean
    @Primary
    public CacheManager caffeineCacheManager() {
        ArrayList<CaffeineCache> caches = new ArrayList<CaffeineCache>();
        for(CacheEnum c : CacheEnum.values()){
            caches.add(new CaffeineCache(c.name(),
                    Caffeine.newBuilder().recordStats()
                            .expireAfterWrite(c.getTtl(), TimeUnit.SECONDS)
                            .maximumSize(c.getMaxSize()).build())
            );
        }
        cacheManager.setCaches(caches);
        return cacheManager;
    }
    @Bean
    public CacheManager getCacheManager() {
        return cacheManager;
    }
}

说明:创建了两个缓存 goods,homePage

6.2,RedisConfig.java

@Configuration
public class RedisConfig {

    @Bean
    @Primary
    public LettuceConnectionFactory redis1LettuceConnectionFactory(RedisStandaloneConfiguration redis1RedisConfig,
                                                                    GenericObjectPoolConfig redis1PoolConfig) {
        LettuceClientConfiguration clientConfig =
                LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofMillis(100))
                        .poolConfig(redis1PoolConfig).build();
        return new LettuceConnectionFactory(redis1RedisConfig, clientConfig);
    }

    @Bean
    public RedisTemplate redis1Template(
            @Qualifier("redis1LettuceConnectionFactory") LettuceConnectionFactory redis1LettuceConnectionFactory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate();

        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        //使用StringRedisSerializer来序列化和反序列化redis的key值
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        //开启事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.setConnectionFactory(redis1LettuceConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Configuration
    public static class Redis1Config {
        @Value("${spring.redis1.host}")
        private String host;
        @Value("${spring.redis1.port}")
        private Integer port;
        @Value("${spring.redis1.password}")
        private String password;
        @Value("${spring.redis1.database}")
        private Integer database;

        @Value("${spring.redis1.lettuce.pool.max-active}")
        private Integer maxActive;
        @Value("${spring.redis1.lettuce.pool.max-idle}")
        private Integer maxIdle;
        @Value("${spring.redis1.lettuce.pool.max-wait}")
        private Long maxWait;
        @Value("${spring.redis1.lettuce.pool.min-idle}")
        private Integer minIdle;

        @Bean
        public GenericObjectPoolConfig redis1PoolConfig() {
            GenericObjectPoolConfig config = new GenericObjectPoolConfig();
            config.setMaxTotal(maxActive);
            config.setMaxIdle(maxIdle);
            config.setMinIdle(minIdle);
            config.setMaxWaitMillis(maxWait);
            return config;
        }

        @Bean
        public RedisStandaloneConfiguration redis1RedisConfig() {
            RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
            config.setHostName(host);
            config.setPassword(RedisPassword.of(password));
            config.setPort(port);
            config.setDatabase(database);
            return config;
        }
    }
}

实现到redis的访问连接配置

7、RedisListenerConfig.java

@Configuration
public class RedisListenerConfig {

    //创建两个消息监听器MessageListener
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                            MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic(Constants.CHANNEL_GOODS));
        container.addMessageListener(listenerAdapter, new PatternTopic(Constants.CHANNEL_HOME));
        return container;
    }

    //指定接收消息的类名和方法名
    @Bean
    MessageListenerAdapter listenerAdapter(RedisMessageReceiver messageReceiver) {
        System.out.println("listenerAdapter");
        return new MessageListenerAdapter(messageReceiver, "onReceiveMessage");
    }

    //指定StringRedisTemplate的生成
    @Bean
    StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }
}

说明:收到消息后,根据消息内容进行处理,
我们收到的针对商品缓存的消息有三类:deleteall,update,delete
分别调用三个不同的处理方法

8、LocalCacheServiceImpl.java

@Service
public class LocalCacheServiceImpl implements LocalCacheService {

    @Resource
    private RedisTemplate redis1Template;

    //更新缓存
    @CachePut(value = "goods", key="#goodsId")
    @Override
    public Goods updateGoodsCache(Long goodsId){
        System.out.println("get data from redis");
        Goods goodsr = (Goods) redis1Template.opsForValue().get("goods_"+String.valueOf(goodsId));
        return goodsr;
    }

    //删除缓存
    @CacheEvict(value = "goods" ,key = "#goodsId")
    @Override
    public void deleteGoodsCache(Long goodsId) {
        System.out.println("删除缓存 ");
    }

    //清除缓存
    @CacheEvict(value = "goods", allEntries=true)
    @Override
    public void deleteGoodsCacheAll() {
        System.out.println("已删除全部缓存 ");
    }
}

说明:实现了对缓存的处理

9、HomeController.java

@RestController
@RequestMapping("/home")
public class HomeController {
    @Resource
    private RedisTemplate redis1Template;
    @Resource
    private GoodsService goodsService;
    @Resource
    private CacheManager getCacheManager;

    //发清空缓存的消息
    @GetMapping("/deleteall")
    public String deleteall(){
        String ret = "清除缓存的消息已发出";
        //删除id为4的商品的缓存
        Msg msg_del = new Msg();
        msg_del.setMsgType("deleteall");
        msg_del.setContent("");
        redis1Template.convertAndSend("goodsCache",JSON.toJSONString(msg_del));
        return ret;
    }

    //发更新缓存和删除缓存的消息
    @GetMapping("/update")
    public String update(){
           String ret = "";
           int goodsId = 3;
            //更新redis
           System.out.println("get data from redis");
           String key = "goods_"+String.valueOf(goodsId);
           Goods goodsr = (Goods)redis1Template.opsForValue().get(key);
           ret = "更新前:<br/>"+goodsr.toString()+"<br/>";
           String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(System.currentTimeMillis());
           goodsr.setGoodsName("更新后的商品名,更新时间:"+now);
           redis1Template.opsForValue().set(key,goodsr);
           Goods goodsr2 = (Goods)redis1Template.opsForValue().get(key);
           ret += "更新后:<br/>"+goodsr2.toString()+"<br/>";

            //发布消息,接收者更新本地cache
            Msg msg_up = new Msg();
            msg_up.setMsgType("update");
            msg_up.setContent("3,5");
        redis1Template.convertAndSend("goodsCache",JSON.toJSONString(msg_up));

        //删除id为4的商品的缓存
            Msg msg_del = new Msg();
            msg_del.setMsgType("delete");
            msg_del.setContent("4");
        redis1Template.convertAndSend("goodsCache",JSON.toJSONString(msg_del));
        return ret;
    }


    //商品详情 参数:商品id
    @Cacheable(value = "goods", key="#goodsId",sync = true)
    @GetMapping("/goodsget")
    @ResponseBody
    public Goods goodsInfo(@RequestParam(value="goodsid",required = true,defaultValue = "0") Long goodsId) {
        Goods goods = goodsService.getOneGoodsById(goodsId);
        return goods;
    }

    //统计,如果是生产环境,需要加密才允许访问
    @GetMapping("/stats")
    @ResponseBody
    public Object stats() {
        CaffeineCache caffeine = (CaffeineCache)getCacheManager.getCache("goods");
        Cache goods = caffeine.getNativeCache();
        String statsInfo="cache名字:goods<br/>";
        Long size = goods.estimatedSize();
        statsInfo += "size:"+size+"<br/>";
        ConcurrentMap map= goods.asMap();
        statsInfo += "map keys:<br/>";
        for(Object key : map.keySet()) {
            statsInfo += "key:"+key.toString()+";value:"+map.get(key)+"<br/>";
        }
        statsInfo += "统计信息:"+goods.stats().toString();
        return statsInfo;
    }
 }

说明:更新/删除/清空缓存的操作 我们都是通过发送redis消息实现,
在生产环境中,这些功能需要放到管理后台

10、测试效果

10.1、生成缓存:

分别访问:
http://127.0.0.1:8080/home/goodsget?goodsid=3
http://127.0.0.1:8080/home/goodsget?goodsid=4
http://127.0.0.1:8080/home/goodsget?goodsid=5

使商品id分别为 3/4/5的这三件商品生成caffeine缓存
查看效果:访问:
http://127.0.0.1:8080/home/stats
可以看到缓存的数据:

cache名字:goods
size:3
map keys:
key:3;value: Goods:goodsId=3 goodsName=100分电动牙刷 subject=好用到让你爱上刷牙 price=59.00 stock=15
key:4;value: Goods:goodsId=4 goodsName=蜂蜜牛奶手工皂 subject=深入滋养,肌肤细腻嫩滑 price=70.00 stock=33
key:5;value: Goods:goodsId=5 goodsName=紫光筷子筒 subject=紫光智护,干爽防潮更健康 price=189.00 stock=20
统计信息:CacheStats{hitCount=3, missCount=6, loadSuccessCount=6, loadFailureCount=0, totalLoadTime=624491686, evictionCount=3, evictionWeight=3}

10.2、更新缓存:访问:

http://127.0.0.1:8080/home/update
我们在这个update方法中实现了两项功能:
更新了缓存中商品id为3的商品的名字
删除了缓存中商品id为4的对象

查看效果:,访问:
http://127.0.0.1:8080/home/stats
cache名字:goods
size:2
map keys:
key:3;value: Goods:goodsId=3 goodsName=更新后的商品名,更新时间:2020-08-06 15:21:49.049 subject=好用到让你爱上刷牙 price=59.00 stock=15
key:5;value: Goods:goodsId=5 goodsName=紫光筷子筒 subject=紫光智护,干爽防潮更健康 price=189.00 stock=20
统计信息:CacheStats{hitCount=1, missCount=3, loadSuccessCount=3, loadFailureCount=0, totalLoadTime=169516569, evictionCount=0, evictionWeight=0}

可以看到缓存中商品id为3的对象商品名被更新,
商品id为4的对象已被删除

10.3,清除缓存:

访问:http://127.0.0.1:8080/home/deleteall
查看效果:访问:
http://127.0.0.1:8080/home/stats
cache名字:goods
size:0
map keys:
统计信息:CacheStats{hitCount=1, missCount=3, loadSuccessCount=3, loadFailureCount=0, totalLoadTime=169516569, evictionCount=0, evictionWeight=0}

可以看到缓存名为goods的缓存中的对象已被清空

11、查看spring boot版本

. ____ _ __ _ _

 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)

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

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

相关文章

Gitlab数据自动备

【场景】&#xff1a;将Gitlab服务器定时备份到Gitlab备份服务器 1.设置Gitlab服务器以及Gitlab备份服务器时间 1.1查看系统时间&#xff1a; date 1.2修改具体时间&#xff1a; date -s "2023-06-02 15:15:00" 1.3把时间写入CMOS&#xff1a; clock -w 1.4把…

深入了解Altium Designer 2023的规则设置

在PCB设计中&#xff0c;规则设置是确保PCB设计符合标准和规范的关键步骤&#xff0c;Altium Designer 2023作为一款强大的PCB设计软件&#xff0c;提供了丰富的规则设置功能&#xff0c;可帮助电子工程师实现高效准确的设计。下面将详细介绍AD 2023中的规则设置功能&#xff0…

【OpenMMLab AI实战营第二期笔记】人体关键点检测与MMPose

人体关键点检测与MMPose 介绍 人体姿态估计&#xff08;Human Pose Estimation&#xff09;是计算机视觉领域中的一个重要研究方向&#xff0c;也是计算机理解人类动作、行为必不可少的一步&#xff0c;人体姿态估计是指通过计算机算法在图像或视频中定位人体关键点&#xff…

TDEngine3.0环境搭建总结

TDEngine3.0环境搭建总结 一、TDengine 介绍二、TDengine的下载三、TDengine Server安装及配置3.1 安装3.2 taos的参数配置3.3 启动3.4 taosAdapter 四、TDengine Client 安装4.1 linux客户端安装4.2 windows客户端安装 一、TDengine 介绍 TDengine 官网 TDengine的介绍   T…

算法工程师的岗位职责(合集)

算法工程师的岗位职责1 职责&#xff1a; 1、负责运动控制的数据采集、信号处理、仪器控制等模块研发和维护,包括关键技术方案设计/详细设计/调试/验证/测试/现场调试 2、编写软件使用说明书等相关技术性文件 3、完成项目中有关机器人轨迹设计、分析、控制的需求分析(7轴机械手…

Maven依赖传递

Maven 依赖传递是 Maven 的核心机制之一&#xff0c;它能够一定程度上简化 Maven 的依赖配置。本节我们将详细介绍依赖传递及其相关概念。 依赖传递 如下图所示&#xff0c;项目 A 依赖于项目 B&#xff0c;B 又依赖于项目 C&#xff0c;此时 B 是 A 的直接依赖&#xff0c;C…

java爬虫详解及简单实例

java爬虫是一种自动化程序&#xff0c;可以模拟人类在互联网上的行为&#xff0c;从网站上抓取数据并进行处理。下面是Java爬虫的详细解释&#xff1a; 1、爬虫的基本原理 Java爬虫的基本原理是通过HTTP协议模拟浏览器发送请求&#xff0c;获取网页的HTML代码&#xff0c;然后…

PS2024后期调色滤镜插件Alien Skin Exposure7

Exposure是一款常见的ps调色滤镜插件&#xff0c;相信许多朋友都曾经用过它。一张普通的图片经过后期调色处理后&#xff0c;可以得到更加靓丽的效果。因此选择一款专业性强、操作简单的后期调色软件很重要。那么&#xff0c;我们应该如何选择后期调色软件呢&#xff1f;下面给…

第三大章docker的部署

1. 红为写的命令 systemctl stop firewalld.service setenforce 0 #安装依赖包yum install -y yum-utils device-mapper-persistent-data lvm2 -------------------------------------------------------------------------------------------- yum-utils&#xff1a;提供了…

揭秘虚拟直播:3D场景与2D背景的区别

虚拟直播是指通过技术手段创造出虚拟场景&#xff0c;将主播或演员放置其中进行实时直播的一种形式。这种直播方式结合了虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;和实时渲染等技术&#xff0c;近年来&#xff0c;随着VR和AR技术的不断成熟和普…

Flink第八章:FlinkSQL

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作. Flink第三章:基本操作(二) Flink第四章:水位线和窗口 Flink第五章:处理函数 Flink第六章:多流操作 Flink第七章:状态编程 Flink第八章:FlinkSQL 文章目录 系列文章目录前言一、常用函数1.快速上手案例2.连接外部数据…

chatgpt赋能python:Python岗位需求日渐增加

Python岗位需求日渐增加 Python编程语言在当前的IT行业中越来越受欢迎。其灵活性和易用性使得Python在各种领域中使用广泛&#xff0c;比如Web开发、数据科学、人工智能等。作为一名有10年Python编程经验的工程师&#xff0c;我认为Python是一种非常有前途的编程语言&#xff…

IDC MarketScape《决策支持分析数据平台供应商评估》报告发布,亚马逊云科技位列“领导者”

随着科学技术的不断发展&#xff0c;人们的生活方式也在不断改变。现在&#xff0c;人们可以通过互联网获得更多的信息&#xff0c;也可以通过智能手机随时随地与他人进行交流。此外&#xff0c;人工智能技术的进步也使得机器能够完成一些复杂的任务&#xff0c;从而提高了人们…

重磅新品 | 立仪发布高速版嵌入式光谱共焦G系列

重磅新品 | 立仪发布高速版嵌入式光谱共焦G系列 立仪科技作为国产光谱共焦技术的引领者&#xff0c;在光谱共焦技术上数年磨一剑&#xff0c;打破封锁&#xff0c;而此次研发出的嵌入式光谱共焦位移传感器G系列采用先进的FPGACPU硬件架构等技术自研而成并结合高强度LED光源、创…

python---变量和简单的数据类型

python---变量和简单的数据类型 1. 变量的命名2. 字符串2.1 使用方法修改字符串的大小写2.2 合并&#xff08;拼接&#xff09;字符串2.3 使用制表符或换行符来添加空白2.4 删除空白2.5 使用字符串时避免使用语法错误 3. 数字3.1 整数3.2 浮点数3.3 使用函数str()避免类型错误 …

基于springboot地方旅游系统的设计与实现

摘 要 本次设计内容是基于Springboot的旅游系统的设计与实现&#xff0c;采用B/S三层架构分别是Web表现层、Service业务层、Dao数据访问层&#xff0c;并使用Springboot&#xff0c;MyBatis二大框架整合开发服务器端&#xff0c;前端使用vue&#xff0c;elementUI技术&…

vue-admin-template刷新侧边栏数据丢失

使用vue-admin-template时&#xff0c;刷新页面侧边栏消失&#x1f612;&#xff0c;仔细查看代码后找到原因&#xff0c;使用的路由与vuex有关&#xff0c;而在刷新页面时vue会重新加载vue实例&#xff0c;vuex中的数据会被初始化&#xff0c;所以看不到侧边栏是因为数据被重置…

虚拟现实 VR 智慧办公室可视化

“虚拟现实”是来自英文“Virtual Reality”&#xff0c;简称 VR 技术&#xff0c;其是通过利用计算机仿真系统模拟外界环境&#xff0c;主要模拟对象有环境、技能、传感设备和感知等&#xff0c;为用户提供多信息、三维动态、交互式的仿真体验。 图扑软件基于自研可视化引擎 H…

containerd 容器概述

containerd 容器概述 官方文档: https://containerd.io在 2016 年 12 月 14 日&#xff0c;Docker 公司宣布将 containerd 从 Docker 中分离&#xff0c;由开源社区独立发展和 运营。Containerd 完全可以单独运行并管理容器&#xff0c;而 Containerd 的主要职责是镜像管理和…

K8S-解决报错--总结日记

问题一&#xff1a;etcd和apiserver无法正常启动 问题查看nodes节点发生报错 解决方法/步骤 步骤一&#xff1a;K8S集群节点异常重启后&#xff0c;再终端执行kubectl get nodes命令&#xff0c;出现报错dial tcp 10.200.18.100:6443: connect: connection refused。 步骤二…