Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot整合Redis实现分布式缓存、分布式锁等,实战分享!

SpringBoot整合Redis实现分布式缓存、分布式锁等,实战分享!

作者头像
Java极客技术
发布于 2023-02-23 09:35:26
发布于 2023-02-23 09:35:26
3.4K00
代码可运行
举报
文章被收录于专栏:Java极客技术Java极客技术
运行总次数:0
代码可运行

一、摘要

在前几篇文章中,我们详细介绍了 redis 的一些功能特性以及主流的 java 客户端 api 使用方法。

在当前流行的微服务以及分布式集群环境下,Redis 的使用场景可以说非常的广泛,能解决集群环境下系统中遇到的不少技术问题,在此列举几个使用 redis 经常用到的功能!

  • 分布式缓存:在分布式的集群架构中,将缓存存储在内存中会出现很多的问题,比如用户回话信息,因为这部分信息需要与其他机器共享,此时利用 Redis 可以很好的解决机器之间数据共享的问题,缓存也是 Redis 中使用最多的场景
  • 分布式锁:在高并发的情况下,我们需要一个锁来防止并发带来的脏数据,Java 自带的锁机制显然对进程间的并发并不好使,此时利用 Redis 的单线程特性,实现分布式锁控制
  • 接口限流:在集群环境下,可以利用 redis 的分布式自增ID功能,精准的统计每个接口在指定时间内的请求次数,利用这个特性,可以定向限制某个接口恶意频刷

当然 Redis 的使用场景并不仅仅只有这么多,还有很多未列出的场景,如发布/订阅,分布锁集合等。

现实中我们大部分的微服务项目,都是基于 SpringBoot 框架进行快速开发,在 SpringBoot 项目中我们应该如何使用 Redis 呢?

代码实践如下!

二、代码实践

2.1、添加 redis 相关依赖包

实际上,在 SpringBoot 项目中,使用redis非常简单,开发者只需要在项目中添加如下的依赖即可!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在之前的 redis 系列文章中,我们知道官方推荐的 java 版本的 redis 客户端,一共有三个,分别是JedisLettuceRedisson,其中大部分场景下,使用Jedis或者Lettuce就足够了。

在 SpringBoot 1.x 版本里面,spring-boot-starter-data-redis默认集成的客户端是Jedis;从 SpringBoot 2.x 开始,spring-boot-starter-data-redis默认集成的客户端是Lettuce

springBoot-2.1.0版本为例,我们打开spring-boot-starter-data-redis依赖配置,核心配置如下!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starters</artifactId>
    <version>2.1.0.RELEASE</version>
  </parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
  <version>2.1.0.RELEASE</version>
  <name>Spring Boot Data Redis Starter</name>

  <!--省略其他配置...-->
 
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.0.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>2.1.2.RELEASE</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <artifactId>jcl-over-slf4j</artifactId>
          <groupId>org.slf4j</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>io.lettuce</groupId>
      <artifactId>lettuce-core</artifactId>
      <version>5.1.2.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

</project>

可以很清晰的看到,spring-boot-starter-data-redis默认集成的客户端是Lettuce

2.2、配置 redis 相关连接信息

依赖包添加完成之后,我们还需要在application.properties全局配置文件中,添加相关的 redis 配置信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0

最后,我们来跑一个最简单的单元测试,看看是否能联通(确保 redis 的服务端已经启动)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private StringRedisTemplate stringRedisTemplate;

@Test
public void test() throws Exception {
    String uuid = UUID.randomUUID().toString();
    stringRedisTemplate.opsForValue().set(uuid, uuid, 60, TimeUnit.SECONDS);
    System.out.println(stringRedisTemplate.opsForValue().get(uuid));
}

如果控制台输出正常,说明基本配置已经完成,如果有错误,看错误信息然后依次排查!

2.3、重新配置 RedisTemplate 的序列化策略

SpringBoot 为我们提供了一个高度封装的RedisTemplate类来操作redis的各个命令,开发者无需关心具体的客户端 api 问题,通过RedisTemplate提供的方法,就可以操作redis,方便开发者可以无成本替换 java 客户端。

当我们存储对象的时候,RedisTemplate默认采用的是 Jdk 提供的序列化工具库,该工具有个要求,缓存的对象必须显式实现序列化接口,才能保存。

通常情况下,我们会自定义RedisTemplate的序列化策略,采用Jackson将对象转成json,查询的时候将json转成对象。

具体实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
public class RedisTemplateConfig {

    /**
     * 默认是JDK的序列化策略,这里配置redisTemplate采用的是Jackson2JsonRedisSerializer的序列化策略
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);

        // 使用Jackson序列号对象
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSeial.setObjectMapper(objectMapper);

        // 使用RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 配置连接工厂
        template.setConnectionFactory(factory);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        // 值采用json序列化
        template.setValueSerializer(jacksonSeial);
        // 使用StringRedisSerializer来序列化和反序列化redis的hash-key值
        template.setHashKeySerializer(new StringRedisSerializer());
        // 值采用json序列化
        template.setHashValueSerializer(jacksonSeial);

        //执行后续方法
        template.afterPropertiesSet();
        return template;
    }
}
2.4、RedisTemplate 使用介绍

我们知道,redis 提供的数据结构很丰富,支持字符串、哈希表、列表、集合、有序集合等数据类型的存储,RedisTemplate对这五种数据结构分别定义了不同的操作类,具体如下:

  • ValueOperations:操作最简单的K-V数据
  • ListOperations:操作list类型的数据
  • HashOperations:操作hash类型的数据
  • SetOperations:操作set类型的数据
  • ZSetOperations:操作zset类型的数据

相关的 api 操作如下!

2.4.1、操作字符串的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RedisApplication.class)
public class RedisTest {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void test() throws Exception {
        // 设置值,默认不过期
        stringRedisTemplate.opsForValue().set("userName", "张三");

        // 获取值
        String value = stringRedisTemplate.opsForValue().get("userName");
        System.out.println("获取userName对应的值:" +  value);

        // 设置值并且设置2秒过期时间,过期之后自动删除
        stringRedisTemplate.opsForValue().set("email", "123@123.com", 2, TimeUnit.SECONDS);
        Thread.sleep(1000);
        System.out.println("获取email过期时间(单位秒):" + stringRedisTemplate.getExpire("email"));
        System.out.println("获取email对应的值:" +  stringRedisTemplate.opsForValue().get("email"));
        Thread.sleep(1000);
        System.out.println("获取email对应的值:" +  stringRedisTemplate.opsForValue().get("email"));

        // 删除key
        Boolean result = stringRedisTemplate.delete("userName");
        System.out.println("删除userName结果:" +  result);
    }
}

2.4.2、操作对象的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserVo {

    private String email;

    private String name;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public UserVo(String email, String name) {
        this.email = email;
        this.name = name;
    }

    public UserVo() {
    }

    @Override
    public String toString() {
        return "UserVo{" +
                "email='" + email + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private RedisTemplate redisTemplate;

@Test
public void test() throws Exception {
    // 设置对象值,并且2秒自动过期
    ValueOperations<String, UserVo> operations = redisTemplate.opsForValue();
    UserVo user = new UserVo("aa@126.com", "张三");
    operations.set("user", user, 2, TimeUnit.SECONDS);

    //获取对象值
    UserVo userVo = operations.get("user");
    System.out.println(userVo.toString());
    System.out.println("获取user过期时间(单位秒):" + redisTemplate.getExpire("user"));


    //删除key
    Boolean deleteValue = redisTemplate.delete("user");
    System.out.println("删除userName结果:" +  deleteValue);
}
2.4.3、操作列表的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private RedisTemplate redisTemplate;

@Test
public void test() throws Exception {
    // 向列表中添加数据
    ListOperations<String, UserVo> operations = redisTemplate.opsForList();
    // 往List左侧插入一个元素
    operations.leftPush("userList", new UserVo("aa@126.com", "张三"));
    operations.leftPush("userList", new UserVo("bb@126.com", "里斯"));
    //往 List 右侧插入一个元素
    operations.rightPush("userList", new UserVo("cc@126.com", "王五"));
    operations.rightPush("userList", new UserVo("dd@126.com", "赵六"));
    // 获取List 大小
    Long size = operations.size("userList");
    System.out.println("获取列表总数:" + size);
    //遍历整个List
    List<UserVo> allUserVo1 = operations.range("userList", 0, size);
    System.out.println("遍历列表所有数据:" + JacksonUtils.toJson(allUserVo1));
    //遍历整个List,-1表示倒数第一个即最后一个
    List<UserVo> allUserVo2 = operations.range("userList", 0, -1);
    System.out.println("遍历列表所有数据:" + JacksonUtils.toJson(allUserVo2));
    //从 List 左侧取出第一个元素,并移除
    Object userVo1 = operations.leftPop("userList", 200, TimeUnit.MILLISECONDS);
    System.out.println("从左侧取出第一个元素并移除:" + userVo1.toString());
    //从 List 右侧取出第一个元素,并移除
    Object userVo2 = operations.rightPop("userList", 200, TimeUnit.MILLISECONDS);
    System.out.println("从右侧取出第一个元素并移除:" + userVo2.toString());

}
2.4.4、操作哈希的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private RedisTemplate redisTemplate;

@Test
public void test() throws Exception {
    // 向hash中添加数据
    HashOperations<String, String, Integer> operations = redisTemplate.opsForHash();
    //Hash 中新增元素。
    operations.put("score", "张三", 2);
    operations.put("score", "里斯", 1);
    operations.put("score", "王五", 3);
    operations.put("score", "赵六", 4);

    Boolean hasKey = operations.hasKey("score", "张三");
    System.out.println("检查是否存在【score】【张三】:" + hasKey);
    Integer value = operations.get("score", "张三");
    System.out.println("获取【score】【张三】的值:" + value);
    Set<String> keys = operations.keys("score");
    System.out.println("获取hash表【score】所有的key集合:" + JacksonUtils.toJson(keys));
    List<Integer> values = operations.values("score");
    System.out.println("获取hash表【score】所有的value集合:" + JacksonUtils.toJson(values));
    Map<String,Integer> map = operations.entries("score");
    System.out.println("获取hash表【score】下的map数据:" + JacksonUtils.toJson(map));
    Long delete = operations.delete("score", "里斯");
    System.out.println("删除【score】中key为【里斯】的数据:" + delete);
    Boolean result = redisTemplate.delete("score");
    System.out.println("删除整个key:" + result);

}
2.4.5、操作集合的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private RedisTemplate redisTemplate;

@Test
public void test() throws Exception {
    // 向集合中添加数据
    SetOperations<String, String> operations = redisTemplate.opsForSet();
    //向集合中添加元素,set元素具有唯一性
    operations.add("city", "北京","上海", "广州", "深圳", "武汉");
    Long size = operations.size("city");
    System.out.println("获取集合总数:" + size);
    //判断是否是集合中的元素
    Boolean isMember = operations.isMember("city", "广州");
    System.out.println("检查集合中是否存在指定元素:" + isMember);
    Set<String> cityNames = operations.members("city");
    System.out.println("获取集合所有元素:" + JacksonUtils.toJson(cityNames));
    Long remove = operations.remove("city", "广州");
    System.out.println("删除指定元素结果:" + remove);
    //移除并返回集合中的一个随机元素
    String cityName = operations.pop("city");
    System.out.println("移除并返回集合中的一个随机元素:" + cityName);
}
2.4.6、操作有序集合的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private RedisTemplate redisTemplate;

@Test
public void test() throws Exception {
    // 向有序集合中添加数据
    ZSetOperations<String, String> operations = redisTemplate.opsForZSet();
    //向有序集合中添加元素,set元素具有唯一性
    operations.add("cityName", "北京", 100);
    operations.add("cityName", "上海", 95);
    operations.add("cityName", "广州", 75);
    operations.add("cityName", "深圳", 85);
    operations.add("cityName", "武汉", 70);

    //获取变量指定区间的元素。0, -1表示全部
    Set<String> ranges = operations.range("cityName", 0, -1);
    System.out.println("获取有序集合所有元素:" + JacksonUtils.toJson(ranges));
    Set<String> byScores = operations.rangeByScore("cityName", 85, 100);
    System.out.println("获取有序集合所有元素(按分数从小到大):"+ JacksonUtils.toJson(byScores));
    Long zCard = operations.zCard("cityName");
    System.out.println("获取有序集合成员数: " + zCard);
    Long remove = operations.remove("cityName", "武汉");
    System.out.println("删除某个成员数结果: " + remove);

}
2.4.7、操作分布锁相关的 api
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Autowired
private StringRedisTemplate stringRedisTemplate;


@Test
public void test(){
    //分布式自增ID
    for (int i = 0; i < 10; i++) {
        Long incrementId = stringRedisTemplate.opsForValue().increment("orderId");
        //设置2秒后自动过期
        stringRedisTemplate.expire("orderId", 2, TimeUnit.SECONDS);
        System.out.println("orderId当前值:" +  incrementId);
    }

    //分布式加锁,5秒自动过期
    boolean lock = lock("LOCK", "test", 5L);
    System.out.println("加锁结果:" +  lock);
    boolean unlock = releaseLock("LOCK", "test");
    System.out.println("解锁结果:" +  unlock);
}



/**
 * 直接加锁
 * @param key
 * @param value
 * @param expire
 * @return
 */
public boolean lock(String key,String value, Long expire){
    String luaScript = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call('expire', KEYS[1], ARGV[2]) else return 0 end";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
    Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value, String.valueOf(expire));
    return result.equals(Long.valueOf(1));
}


/**
 * 释放锁
 * @param key
 * @param value
 * @return
 */
public boolean releaseLock(String key,String value){
    String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
    Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key),value);
    return result.equals(Long.valueOf(1));
}
2.5、如果要换成 jedis,如果更换?

从 SpringBoot 2.x 开始,spring-boot-starter-data-redis默认集成的客户端是Lettuce,但是有的项目使用了Jedis依赖包相关的代码,如何无缝替换呢?

pom.xml文件中,添加Jedis依赖包,排除相关的包即可,示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- Redis相关依赖包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </exclusion>
        <exclusion>
            <artifactId>lettuce-core</artifactId>
            <groupId>io.lettuce</groupId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

最后,在application.properties中,添加jedis相关配置,内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# Redis数据库索引(默认为0)
spring.redis.database=1
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# Redis服务器连接超时配置
spring.redis.timeout=1000

# 连接池配置
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-wait=1000
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.min-idle=0
spring.redis.jedis.pool.time-between-eviction-runs=100
2.6、手动封装一个分布式锁实现类

默认情况下,无论是Jedis还是Lettuce,都没有为我们提供redis分布式锁的实现,因此我们自己进行封装,当然你也可以直接添加Redisson包,里面也提供了分布式锁实现的相关 API。

如果当前 redis 是单机环境,或者哨兵模式,我们完全可以自行封装一个分布式锁实现类,具体代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Collections;

/**
 * redis分布式锁服务类
 * 采用LUA脚本实现,保证加锁、解锁操作原子性
 */
@Component
public class RedisLockService {

    /**
     * 分布式锁过期时间,单位秒
     */
    private static final Long DEFAULT_LOCK_EXPIRE_TIME = 60L;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 尝试在指定时间内加锁
     * @param key
     * @param value
     * @param timeout 锁等待时间
     * @return
     */
    public boolean tryLock(String key,String value, Duration timeout){
        long waitMills = timeout.toMillis();
        long currentTimeMillis = System.currentTimeMillis();
        do {
            boolean lock = lock(key, value, DEFAULT_LOCK_EXPIRE_TIME);
            if (lock) {
                return true;
            }
            try {
                Thread.sleep(1L);
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        } while (System.currentTimeMillis() < currentTimeMillis + waitMills);
        return false;
    }

    /**
     * 直接加锁
     * @param key
     * @param value
     * @param expire
     * @return
     */
    public boolean lock(String key,String value, Long expire){
        String luaScript = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then return redis.call('expire', KEYS[1], ARGV[2]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value, String.valueOf(expire));
        return result.equals(Long.valueOf(1));
    }


    /**
     * 释放锁
     * @param key
     * @param value
     * @return
     */
    public boolean releaseLock(String key,String value){
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = stringRedisTemplate.execute(redisScript, Collections.singletonList(key),value);
        return result.equals(Long.valueOf(1));
    }
}

三、关于 key 的设计

通常情况下,我们对key采用如下方式进行设计,以便与其他项目中的key错开,避免发生冲突!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
固定前缀:项目名:数据库名:表名:字段名:具体的值

其次,无论什么时候,只要有可能就利用key超时的优势,尽可能避免数据永久存储,因为一旦所有的key都永久存储,大量无效的key,会服务器资源非常严重不足,甚至不可用!

四、小结

本文主要围绕在 SpringBoot 项目中,如何集成 redis 并正确使用进行了简单的分享,内容难免有缺漏,欢迎网友留言指出!

五、参考

1、博客园 - 卡斯特梅的雨伞 - springboot中RedisTemplate的使用

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java极客技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
EasyGBS新版本使用https访问,不能播放视频是什么原因?
国标视频云服务EasyGBS支持设备/平台通过国标GB28181协议注册接入,并能实现视频的实时监控直播、录像、检索与回看、语音对讲、云存储、告警、平台级联等功能。平台部署简单、可拓展性强,支持将接入的视频流进行全终端、全平台分发,分发的视频流包括RTSP、RTMP、FLV、HLS、WebRTC等格式。
TSINGSEE青犀视频
2022/08/29
2780
国标GB28181视频级联智能分析平台EasyGBS播放视频控制台报错401
我们接触过很多用户反馈的视频级联智能分析平台EasyGBS播放问题。不能播放的情况很多,由于控制台出现的报错不同,也需要通过不同的方式进行排查。
TSINGSEE青犀视频
2021/04/02
5450
全网最透彻HTTPS(面试常问)
每篇文章都希望你能收获到东西,这篇将带你深入 HTTPS 加解密原理,希望看完能够有这些收获:
敖丙
2020/05/26
1.6K0
全网最透彻HTTPS(面试常问)
HTTPS终于搞懂了
近些年来,越来越多的网站使用 HTTPS 协议进行数据传输,原因在于 HTTPS 相较于 HTTP 能够提供更加安全的服务。
捡田螺的小男孩
2023/02/22
1K0
HTTPS终于搞懂了
EasyGBS国标GB28181流媒体视频直播服务支持https过程中发现https视频流遇到域名解析问题的解决方法
之前的文章中我们提到了EasyGBS是如何支持https的,也就是分别在CMS和SMS中配置对应的https证书和域名host就可以了:
EasyNVR
2020/04/23
7830
EasyGBS国标GB28181流媒体视频直播服务支持https过程中发现https视频流遇到域名解析问题的解决方法
经得住拷问的HTTPS原理解析
HTTPS 是在 HTTP 和 TCP 之间建立了一个安全层,HTTP 与 TCP 通信的时候,必须先进过一个安全层,对数据包进行加密,然后将加密后的数据包传送给 TCP,相应的 TCP 必须将数据包解密,才能传给上面的 HTTP。
用户4456933
2021/06/01
6920
经得住拷问的HTTPS原理解析
带你全面了解Http和Https(开发人员必备)
Http和Https属于计算机网络范畴,但作为开发人员,不管是后台开发或是前台开发,都很有必要掌握它们。
Criss@陈磊
2019/08/01
8400
网络相关知识
一次http网络请求的过程 浏览器发起请求-> 解析域名得到ip进行TCP连接 ->浏览器发送HTTP请求和头信息发送->服务器对浏览器进行应答,响应头信息和浏览器所需的内容-> 关闭TCP连接或保持-> 浏览器得到数据数据进行操作。 先找到对方ip地址,然后用指定的传输协议传送到指定的端口。
六月的雨
2020/04/08
6740
网络相关知识
如何使用netcat工具排查EasyGBS视频平台因端口导致的视频播放异常问题?
EasyGBS国标视频云服务是基于国标GB28181协议的视频平台,可实现的视频功能包括视频直播、录像、语音对讲、云存储、告警、级联等,支持将接入的视频流进行全终端、全平台的分发,可分发的视频流包括RTSP、RTMP、FLV、HLS、WebRTC等格式。
TSINGSEE青犀视频
2022/09/22
3790
【Linux网络#10】:Https协议原理
🔥 之前我们已经了解了 Http 协议,但是后面我也说道 Http 协议无论是 GET 还是 Post 方法传输数据。都是以明文进行传输,这意味着数据极易受到拦截和篡改。 而为了解决这一问题,HTTPS应运而生。本文将详细探讨HTTPS协议的工作原理、HTTP与HTTPS的区别、加密技术的应用以及如何通过证书认证保障安全通信
IsLand1314
2025/06/02
1100
【Linux网络#10】:Https协议原理
轻松实现在web页面中直接播放rtsp视频流「建议收藏」
我之前研究在 web 中直接播放 rtsp 视频流时,写过一篇文章:【前端】rtsp 与 rtmp 视频流的播放方法。阅读这篇文章对你的学习有很大帮助。在文章中我有过详细的分析和解读,并给出了 结论:
全栈程序员站长
2022/11/07
9.1K1
轻松实现在web页面中直接播放rtsp视频流「建议收藏」
HTTP和HTTPS的区别,看完后随便吊打面试官
下面我们来一起学习一下 HTTPS ,首先问你一个问题,为什么有了 HTTP 之后,还需要有 HTTPS ?我突然有个想法,为什么我们面试的时候需要回答标准答案呢?为什么我们不说出我们自己的想法和见解,却要记住一些所谓的标准回答呢?技术还有正确与否吗?
淘课之家
2020/03/15
3.7K1
HTTP和HTTPS的区别,看完后随便吊打面试官
《透视HTTP协议》:快速了解HTTPS/HTTP2/HTTP3 的变动改进点
https被认为是通信安全的http,除了http多了s和默认端口改成了443之外,其他都是沿用的http(除了明文和不安全),最主要的改变就是http是over tcp,而https是 https over ssl over tcp。安全的特性都是ssl做的
小柔
2024/05/15
7290
《透视HTTP协议》:快速了解HTTPS/HTTP2/HTTP3 的变动改进点
白话HTTPS
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)的应用层传输协议。
叔牙
2021/12/11
1.6K1
白话HTTPS
HTTPS对中间人真就束手就擒了?
由于近几年来各大网站越来越注重安全性都改成了https协议,不像前十几年前直接是http协议直接裸奔在互联网。HTTPS 从协议上解决了 HTTP 时代的中间人攻击问题,但是 HTTPS 在用户主动信任了伪造证书的时候也会发生中间人攻击(比如早期的 12306 需要手动信任证书),所以从这一角度来看,HTTPS对中间人也不能完全束手就擒了。
北京-宏哥
2024/07/17
1.4K0
HTTPS对中间人真就束手就擒了?
即时通讯安全篇(九):为什么要用HTTPS?深入浅出,探密短连接的安全性
对于IM开发者来说,IM里最常用的通信技术就是Socket长连接和HTTP短连接(通常一个主流im会是这两种通信手段的结合)。从通信安全的角度来说,Socket长连接的安全性,就是基于SSL/TLS加密的TCP协议来实现的(比如微信的mmtls,见《微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解》);而对于HTTP短连接的安全性,也就是HTTPS了。
JackJiang
2022/05/13
7660
即时通讯安全篇(九):为什么要用HTTPS?深入浅出,探密短连接的安全性
详解HTTPS、TLS、SSL
HTTP也称作HTTP over TLS。TLS的前身是SSL,TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。下图描述了在TCP/IP协议栈中TLS(各子协议)和HTTP的关系
sunsky
2020/08/20
1.3K0
详解HTTPS、TLS、SSL
看完这篇 HTTPS,和面试官扯皮就没问题了
下面我们来一起学习一下 HTTPS ,首先问你一个问题,为什么有了 HTTP 之后,还需要有 HTTPS ?我突然有个想法,为什么我们面试的时候需要回答标准答案呢?为什么我们不说出我们自己的想法和见解,却要记住一些所谓的标准回答呢?技术还有正确与否吗?
Jlion
2022/04/07
3280
看完这篇 HTTPS,和面试官扯皮就没问题了
一分钟了解HTTP和HTTPS协议
很多人存在这样的疑惑就是http与https的区别,这篇文章就跟大家介绍一下。一句话总结HTTPS是身披SSL外壳的HTTP,HTTPS更安全,实际使用中绝大多数的网站现在都采用的是HTTPS协议,这也是未来互联网发展的趋势。
星哥玩云
2022/05/28
6470
一分钟了解HTTP和HTTPS协议
【HTTP】HTTPS TLS 1.2
在个人过去的读书笔记中已经介绍过一次,在这一篇文章中介绍了HTTP1.1的缺点,以及SSL、TLS的历史,之后介绍了有关SSL加密的主要加密方案:公开密钥加密 和 共享密钥加密,最后简单介绍了HTTPS的交互过程,但是书中的过程比较粗,这节我们讲细一点点。
阿东
2022/09/12
1.3K0
推荐阅读
相关推荐
EasyGBS新版本使用https访问,不能播放视频是什么原因?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验