Redis7高级之Redlock算法和Redisson的使用(十)

10.1 Redlock 红锁算法

1.解决手写分布式锁的单点故障问题

  • Redis 提供了 Redlock 算法,用来实现基于多个实例的分布式锁
  • 锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作
  • Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用

在这里插入图片描述

2.设计理念

假设我们有N个Redis主节点,例如 N = 5这些节点是完全独立的,我们不使用复制或任何其他隐式协调系统,为了取到锁客户端执行以下操作:

1获取当前时间,以毫秒为单位;
2依次尝试从5个实例,使用相同的 key 和随机值(例如 UUID)获取锁。当向Redis 请求获取锁时,客户端应该设置一个超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为 10 秒,则超时时间应该在 5-50 毫秒之间。这样可以防止客户端在试图与一个宕机的 Redis 节点对话时长时间处于阻塞状态。如果一个实例不可用,客户端应该尽快尝试去另外一个 Redis 实例请求获取锁;
3客户端通过当前时间减去步骤 1 记录的时间来计算获取锁使用的时间。当且仅当从大多数(N/2+1,这里是 3 个节点)的 Redis 节点都取到锁,并且获取锁使用的时间小于锁失效时间时,锁才算获取成功;
4如果取到了锁,其真正有效时间等于初始有效时间减去获取锁所使用的时间(步骤 3 计算的结果)。
5如果由于某些原因未能获得锁(无法在至少 N/2 + 1 个 Redis 实例获取锁、或获取锁的时间超过了有效时间),客户端应该在所有的 Redis 实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。

该方案为了解决数据不一致的问题,直接舍弃了异步复制只使用 master 节点,同时由于舍弃了 slave,为了保证可用性,引入了 N 个节点,官方建议是 5。

客户端只有在满足下面的这两个条件时,才能认为是加锁成功。

条件1:客户端从超过半数(大于等于N/2+1)的Redis实例上成功获取到了锁;

条件2:客户端获取锁的总耗时没有超过锁的有效时间。

3. 解决方案

在这里插入图片描述

为什么是奇数? N = 2X + 1 (N是最终部署机器数,X是容错机器数)

1 先知道什么是容错

失败了多少个机器实例后我还是可以容忍的,所谓的容忍就是数据一致性还是可以Ok的,CP数据一致性还是可以满足

加入在集群环境中,redis失败1台,可接受。2X+1 = 2 * 1+1 =3,部署3台,死了1个剩下2个可以正常工作,那就部署3台。

加入在集群环境中,redis失败2台,可接受。2X+1 = 2 * 2+1 =5,部署5台,死了2个剩下3个可以正常工作,那就部署5台。

2 为什么是奇数?

最少的机器,最多的产出效果

​ 加入在集群环境中,redis失败1台,可接受。2N+2= 2 * 1+2 =4,部署4台

​ 加入在集群环境中,redis失败2台,可接受。2N+2 = 2 * 2+2 =6,部署6台

10.2 Redisson进行代码改造

Redisson 就是 Redlock算法 的实现

  • POM

    <!--redisson-->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.13.4</version>
    </dependency>
    
  • RedisConfig

    import org.redisson.Redisson;
    import org.redisson.config.Config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    /**
     * @author 晓风残月Lx
     * @date 2023/4/1 10:31
     */
    @Configuration
    public class RedisConfig {
    
        @Bean
        public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory)
        {
            RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(lettuceConnectionFactory);
            //设置key序列化方式string
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            //设置value的序列化方式json
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
    
            redisTemplate.afterPropertiesSet();
    
            return redisTemplate;
        }
    
    //    v 8.0 引入 redisson
        @Bean
        public Redisson redisson() {
            Config config = new Config();
            config.useSingleServer()
                    .setAddress("redis://192.168.238.111:6379")
                    .setDatabase(0)
                    .setPassword("123456");
            return (Redisson) Redisson.create(config);
        }
    }
    
  • InventoryController

    import com.xfcy.service.InventoryService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author 晓风残月Lx
     * @date 2023/4/1 10:32
     */
    @RestController
    @Api(tags = "redis分布式锁测试")
    public class InvetoryController {
    
        @Autowired
        private InventoryService inventoryService;
    
        @ApiOperation("扣减库存sale,一次卖一个")
        @GetMapping(value = "/inventory/sale")
        public String sale()
        {
            return inventoryService.sale();
        }
    
    
        @ApiOperation("扣减库存saleByRedisson,一次卖一个")
        @GetMapping(value = "/inventory/saleByRedisson")
        public String saleByRedisson()
        {
            return inventoryService.saleByRedisson();
        }
    }
    
  • InventoryService

        /**
         * v 9.0    引入Redisson对应的官网推荐RedLock算法实现
         *
         * @return
         */
        @Autowired
        private Redisson redisson;
    
        public String saleByRedisson() {
            String retMessage = "";
    
            RLock redissonLock = redisson.getLock("xfcyRedisLock");
            redissonLock.lock();
            try {
                //1 查询库存信息
                String result = stringRedisTemplate.opsForValue().get("inventory001");
                //2 判断库存是否足够
                Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
                //3 扣减库存
                if (inventoryNumber > 0) {
                    stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
                    retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
    
                } else {
                    retMessage = "商品卖完了,o(╥﹏╥)o";
                }
                System.out.println(retMessage);
            } finally {
                // 改进点,只能删除属于自己的 key,不能删除别人的
                if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()){
                    redissonLock.unlock();
                }
            }
            return retMessage + "\t" + "服务端口号:" + port;
        }
    

10.3 多机案例(解决单点故障)

使用 Redisson 的 MultiLock 多重锁

  • 使用docker 启动 3 台redis master ,3台master 并无从属关系

    docker run -p 6381:6379 --name redis-master-1 -d redis
    docker run -p 6382:6379 --name redis-master-2 -d redis
    docker run -p 6383:6379 --name redis-master-3 -d redis
    
  • 进入到redis容器实例

    docker exec -it redis-master-1 redis-cli
    docker exec -it redis-master-2 /bin/bash 
    docker exec -it redis-master-3 /bin/bash  
    
  • 建 Module redis_redlock

  • POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.10.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.atguigu.redis.redlock</groupId>
        <artifactId>redis_redlock</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>3.19.1</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.14</version>
            </dependency>
            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
            <!--swagger-ui-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.4</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.11</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
        </dependencies>
    
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-configuration-processor</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  • YML

    server.port=9090
    spring.application.name=redlock
    
    
    spring.swagger2.enabled=true
    
    
    spring.redis.database=0
    #没配置密码
    spring.redis.password=
    spring.redis.timeout=3000
    spring.redis.mode=single
    
    spring.redis.pool.conn-timeout=3000
    spring.redis.pool.so-timeout=3000
    spring.redis.pool.size=10
    
    spring.redis.single.address1=192.168.238.111:6381
    spring.redis.single.address2=192.168.238.111:6382
    spring.redis.single.address3=192.168.238.111:6383
    
  • 主启动

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class RedisRedlockApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisRedlockApplication.class, args);
        }
    }
    
  • 配置类

    • CacheConfiguration
    import org.apache.commons.lang3.StringUtils;
    import org.redisson.Redisson;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * @author 晓风残月Lx
     * @date 2023/4/2 17:18
     */
    
    @Configuration
    @EnableConfigurationProperties(RedisProperties.class)
    public class CacheConfiguration {
    
        @Autowired
        RedisProperties redisProperties;
    
        @Bean
        RedissonClient redissonClient1() {
            Config config = new Config();
            String node = redisProperties.getSingle().getAddress1();
            node = node.startsWith("redis://") ? node : "redis://" + node;
            SingleServerConfig serverConfig = config.useSingleServer()
                    .setAddress(node)
                    .setTimeout(redisProperties.getPool().getConnTimeout())
                    .setConnectionPoolSize(redisProperties.getPool().getSize())
                    .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }
    
        @Bean
        RedissonClient redissonClient2() {
            Config config = new Config();
            String node = redisProperties.getSingle().getAddress2();
            node = node.startsWith("redis://") ? node : "redis://" + node;
            SingleServerConfig serverConfig = config.useSingleServer()
                    .setAddress(node)
                    .setTimeout(redisProperties.getPool().getConnTimeout())
                    .setConnectionPoolSize(redisProperties.getPool().getSize())
                    .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }
    
        @Bean
        RedissonClient redissonClient3() {
            Config config = new Config();
            String node = redisProperties.getSingle().getAddress3();
            node = node.startsWith("redis://") ? node : "redis://" + node;
            SingleServerConfig serverConfig = config.useSingleServer()
                    .setAddress(node)
                    .setTimeout(redisProperties.getPool().getConnTimeout())
                    .setConnectionPoolSize(redisProperties.getPool().getSize())
                    .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }
    
    
        /**
         * 单机
         * @return
         */
        /*@Bean
        public Redisson redisson()
        {
            Config config = new Config();
    
            config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);
    
            return (Redisson) Redisson.create(config);
        }*/
    }
    
    • RedisPoolProperties

      import lombok.Data;
      
      /**
       * @author 晓风残月Lx
       * @date 2023/4/2 17:21
       */
      @Data
      public class RedisPoolProperties {
          private int maxIdle;
      
          private int minIdle;
      
          private int maxActive;
      
          private int maxWait;
      
          private int connTimeout;
      
          private int soTimeout;
      
          /*
            池大小
           */
          private  int size;
      }
      
    • RedisProperties

      import lombok.Data;
      import org.springframework.boot.context.properties.ConfigurationProperties;
      
      /**
       * @author 晓风残月Lx
       * @date 2023/4/2 17:19
       */
      @ConfigurationProperties(prefix = "spring.redis", ignoreUnknownFields = false)
      @Data
      public class RedisProperties {
      
          private int database;
      
          /*
          等待节点回复命令的时间。 该时间从命令发送成功时开始计时
           */
          private int timeout = 3000;
      
          private String password;
      
          private String mode;
      
          /*
              池配置
           */
          private RedisPoolProperties pool;
      
          /*
              单机信息配置
           */
          private RedisSingleProperties single;
      
      }
      
    • RedisSingleProperties

      import lombok.Data;
      
      /**
       * @author 晓风残月Lx
       * @date 2023/4/2 17:18
       */
      @Data
      public class RedisSingleProperties {
      
          private String address1;
      
          private String address2;
      
          private String address3;
      }
      
  • 业务类

    • RedLockController

      import cn.hutool.core.util.IdUtil;
      import lombok.extern.slf4j.Slf4j;
      import org.redisson.Redisson;
      import org.redisson.RedissonMultiLock;
      import org.redisson.RedissonRedLock;
      import org.redisson.api.RLock;
      import org.redisson.api.RedissonClient;
      import org.redisson.config.Config;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      /**
       * @author 晓风残月Lx
       * @date 2023/4/2 17:25
       */
      @RestController
      @Slf4j
      public class RedLockController {
      
          public static final String CACHE_KEY_REDLOCK = "ATGUIGU_REDLOCK";
      
          @Autowired
          RedissonClient redissonClient1;
      
          @Autowired
          RedissonClient redissonClient2;
      
          @Autowired
          RedissonClient redissonClient3;
      
          boolean isLockBoolean;
      
          @GetMapping(value = "/multiLock")
          public String getMultiLock() throws InterruptedException
          {
              String uuid =  IdUtil.simpleUUID();
              String uuidValue = uuid+":"+Thread.currentThread().getId();
      
              RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);
              RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);
              RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);
      
              RedissonMultiLock redLock = new RedissonMultiLock(lock1, lock2, lock3);
              redLock.lock();
              try
              {
                  System.out.println(uuidValue+"\t"+"---come in biz multiLock");
                  try { TimeUnit.SECONDS.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); }
                  System.out.println(uuidValue+"\t"+"---task is over multiLock");
              } catch (Exception e) {
                  e.printStackTrace();
                  log.error("multiLock exception ",e);
              } finally {
                  redLock.unlock();
                  log.info("释放分布式锁成功key:{}", CACHE_KEY_REDLOCK);
              }
              return "multiLock task is over  "+uuidValue;
          }
      }
      
  • 测试

    • 就是在发送请求时,模拟一台机器挂掉,查看系统是否还能运行

10.4 Redis 的缓存淘汰策略

1.Redis 内存满了怎么办?

  • 查看 Redis 最大占用内存

    在这里插入图片描述

  • redis默认内存多少

    • 如果在 64位操作系统, maxmemory 设置0或者不设置最大内存大小表示不限制Redis内存使用
  • 一般生产上如何配置

    • 推荐Redis设置为最大物理内存的四分之三
  • 如何修改redis内存设置

    • 通过配置文件修改 (单位是 byte)

      在这里插入图片描述

    • 通过命令修改

    在这里插入图片描述

  • 查看redis内存使用情况

    • info memory
    • config get maxmemory

设置了maxmemory的选项,假如redis 内存使用达到上限,没有加上过期时间就会导致数据写满 maxmemory,这就需要内存淘汰策略

2.Redis 过期键的删除策略

  1. 立即删除

    • 对CPU不友好,用处理器性能换取存储空间(拿时间换空间)
  2. 惰性删除

    • 对memory 不友好,用存储空间换取处理器性能(拿空间换时间)

    • 开启惰性删除, lazyfree-lazy-eviction=yes

      在这里插入图片描述

  3. 定期删除

    • 每隔一段时间执行一次删除过期键操作并通过限制删除操作执行时长和频率来减少删除操作对CPU时间的影响。
    • 定期抽样key,判断是否过期
    • 容易出现漏网之鱼

3.redis 缓存淘汰策略

在这里插入图片描述

3.1 LRU 和 LFU

  • LRU
    • 最近最少使用的页面置换算法,淘汰最长时间未被使用的页面,看页面最后一次被使用到发生调度的时间长短,首先淘汰最长时间未被使用的页面
  • LFU
  • 最近最不常用页面置换算法,淘汰一定时期内被访问次数最少的页面,看一定时间段内被访问次数最少的页,看一定时间段内页面被使用的频率,淘汰一定时期内被访问次数最少的页

3.2种缓存淘汰策略

  1. noevication : 不会驱逐任何key,表示即使内存达到上限也不进行置换,所有能引起内存增加的命令都返回 error
  2. allkeys-lru: 对所有key使用 LRU算法进行删除,优先删除掉最近不经常使用的key,用以保存新数据
  3. volatie-lru : 对所有设置了过期时间的key使用LRU 算法删除
  4. allkeys-random :对所有key随机删除
  5. volatie-random : 对所有设置了过期时间的key随机删除
  6. volatie-ttl :对所有设置了过期时间的key随即删除
  7. allkeys-lfu:对所有key使用LFU算法进行删除
  8. volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除

推荐使用

  • 在所有的 key 都是最近经常使用的,那么就需要选择 allkeys-lru 进行置换最近最不经常使用的key,如果不确定使用哪种策略,那么推荐使用 allkeys-lru
  • 如果所有的key的访问概率都是差不多的,那么可以选用 allkeys-random 策略去置换数据
  • 如果对数据有足够的了解,能够为key指定hint(expire/ttl指定),那么可以选择 volatile-ttl 进行置换(不大推荐,要求过高)

4.性能配置建议

  • 避免存储 bigkey
  • 开启惰性淘汰 lazyfree-lazy-eviction=yes

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络考试复习——第一章 1.5 1.6

1.5 计算机网络的类别 1.5.1计算机网络的定义&#xff1a; 系统集合&#xff0c;连接起来&#xff0c;协议工作&#xff0c;资源共享 计算机网络主要是由一些通用的、可编程的硬件互连而成的&#xff0c;而这些硬件并非专门用来实现某一特定目的&#xff08;例如&#xff0…

Java源码(一)ThreadLocal、SpringBoot Jar 启动原理

思维导图 一、ThreadLocal 1.场景 项目采用SSMShiro登录认证&#xff0c;改造需求如下&#xff1a; 后台管理员登录需要限制&#xff0c;同一个用户的不同IP需要通过过自定义验证后才能登录。 2.问题 在完成需求后发现有管理员用户&#xff08;这里就用A&#xff09;通过验…

Android build.gradle配置详解

Android Studio是采用gradle来构建项目的&#xff0c;gradle是基于groovy语言的&#xff0c;如果只是用它构建普通Android项目的话&#xff0c;是可以不去学groovy的。当我们创建一个Android项目时会包含两个Android build.gradle配置详解文件&#xff0c;如下图&#xff1a; 一…

区块链3链(TRC ERC BSC)授权持币生息源码

分享一款3链&#xff08;TRC ERC BSC&#xff09;授权持币生息源码、来自群友投稿的资源、据说是运营级的。简单的看了下没有问题什么大问题、有能力的可以拿来二开其他的模板。 搭建非常简单&#xff0c;教程就不写了、环境NGINX1.2PHP7.2MYSQL5.6TP默认伪静态 此类源码需要…

【Python】数学 - 用 Python 自动化求解函数 f(x) 的值

目录 1、缘起 2、求以下函数的值 3、代码清单 3.1、求解 f(0)、f(1)、 f(​编辑)、f(​编辑) 3.2、求解 g(0)、g(1)、g(​编辑)、g(​编辑) 3.3、求解 h(0)、h(1)、h(​编辑)、h(​编辑) 4、总结 1、缘起 Python 是一种强大的编程语言&#xff0c;它具有广泛的应用领域。…

Python模拟星空

文章目录前言Turtle基础1.1 Turtle画板1.2 Turtle画笔1.3 Turtle画图1.4 Turtle填色1.5 Turtle写字模拟星空模拟星球浪漫星空尾声前言 Python模拟星空&#xff0c;你值得拥有&#xff01;uu们一周不见啦&#xff0c;本周博主参考网上大佬们的星空&#xff0c;给大家带来了属于…

C语言操作符优先级

在平时写代码时&#xff0c;经常会用到操作符&#xff0c;但是如果不了解这些操作符的优先级&#xff0c;可能会让程序的执行效果和我们预期的不一样。 例如&#xff1a; int a 2;int b 3;int c 4;//int ret a b * c;//我们想要执行的顺序是ab的值再乘c//如果了解操作符优…

chat GPT人工智能写论文-怎么用chatGpt写论文

用chatGPT写文章会重复吗 使用 ChatGPT 写文章可能会出现重复的情况。因为 ChatGPT 是基于机器学习的自然语言处理技术&#xff0c;它并不具备人类的创造性思维&#xff0c;其生成的文本内容是基于已有语言数据的统计模型而产生的。 当输入信息重复、语言结构复杂或指定主题较…

【测试】《软件测试》阅读总结

第一章 软件测试的流程是什么&#xff1f; 需求分析--------测试计划----------测试开发--------测试执行-------测试报告 如何描述一个BUG 版本&#xff0c;测试环境、测试步骤和测试数据、实际结果、预期结果、附件&#xff08;截图、错误日志&#xff09; 软件测试过程包括…

HashMap,HashTable和ConcurrentHashMap之间有什么区别?

前言 在之前HashMap的学习中,我们可以知道HashMap是线程不安全的数据结构,它存储的一般是数据的键值对(Key-Value模型),其中Key允许为null,它底层是数组链表的实现,当单个链表的数据元素过多时,会转变为红黑树,在多线程环境下,对某个HashMap对象进行操作,是无法保证线程安全的,…

代理服务器与CDN的概念

代理服务器 特点&#xff1a;本身不产生内容&#xff0c;处于中间位置转发上下游的请求和响应 面向下游的客户端&#xff1a;它是服务器面向上游的服务器&#xff1a;它是客户端 正向代理&#xff1a;代理的对象是客户端 隐藏客户端身份绕过防火墙&#xff08;突破访问限制&am…

今天面了一个来京东要求月薪25K,明显感觉他背了很多面试题...

最近有朋友去京东面试&#xff0c;面试前后进行了20天左右&#xff0c;包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说&#xff0c;80%的人都会栽在第一轮面试&#xff0c;要不是他面试前做足准备&#xff0c;估计都坚持不完后面几轮面试。 其实&…

LeetCode-146. LRU 缓存

目录LRU理论题目思路代码实现一代码实现二题目来源 146. LRU 缓存 LRU理论 LRU 是 Least Recently Used 的缩写&#xff0c;这种算法认为最近使用的数据是热门数据&#xff0c;下一次很大概率将会再次被使用。而最近很少被使用的数据&#xff0c;很大概率下一次不再用到。当缓…

把ChatGPT接入我的个人网站

效果图 详细内容和使用说明可以查看我的个人网站文章 把ChatGPT接入我的个人网站 献给有外网服务器的小伙伴 如果你本人已经有一台外网的服务器&#xff0c;并且页拥有一个OpenAI API Key&#xff0c;那么下面就可以参照我的教程来搭建一个自己的ChatGPT。 需要的环境 Cento…

大数据分析工具Power BI(三):导入数据操作介绍

导入数据操作介绍 进入PowBI,弹出的如下页面也可以直接关闭,在Power BI中想要导入数据需要通过Power Query 编辑器,Power Query 主要用来清洗和整理数据。

Go分布式爬虫笔记(十七) 4月Day1

文章目录17 协程线程与协程对比调度方式调度策略栈大小上下文切换速度GMP调度循环调度算法如果本地运行队列已经满了&#xff0c;无法处理全局运行队列中的协程怎么办&#xff1f;查找协程的先后顺序主动调度被动调度抢占调度执行时间过长的抢占调度陷入到系统调用中的抢占调度…

leetcode:颠倒二进制位(详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路及图示 题目&#xff1a; 颠倒给定的 32 位无符号整数的二进制位。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中&#xff0c;没有无符号整数类型。…

ThreeJS-聚光灯物体投影(二十)

聚光灯&#xff08;灯泡&#xff09; 关键代码&#xff1a; //直线光&#xff08;由光源发出的灯光&#xff09; // const directionalLight new THREE.DirectionalLight(0xFFFFFF, 0.7); // directionalLight.position.set(10, 10, 10); …

【蓝桥杯冲刺】蓝桥杯12届省赛C++b组真题-编程题

目录 试题F&#xff1a;时间显示 解题思路 代码 试题G&#xff1a;砝码称重 解题思路 代码 试题H&#xff1a;杨辉三角 解题思路 代码 试题I&#xff1a;双向排序 解题思路 试题J&#xff1a;括号序列 解题思路 试题F&#xff1a;时间显示 【问题描述】 小蓝要和…

Linux总结(二)

基础IO 1.什么叫文件? 我们需要在操作系统的角度理解文件。 文件 = 文件内容 + 属性(所以即使是空文件,也会占空间,因为我们是需要保存文件属性的,属性也是数据,所以占空间) C/C++程序默认会打开三个文件流,叫做标准输入(stdin),标准输出(stdout),标准错误(std…