spring.session 随笔0 集成设计

news2024/11/14 22:39:31

0. 上个月划水时间关注的,最近断断续续的了解了一些

RUNOOB redis命令:APPEND
整合shiro实现分布式session同步(定制cacheManager)

我想想,还是照自己思绪发散的顺序开始描述这块的内容吧,可能侧重点有些奇怪。


由于工程使用的spring.boot.dependencies(BOM)版本是2.3.4 RELEASE,故这里使用的redisson库的版本为 3.14.1,望周知

1. redisson --> spring.data.redis

因为上个月搞分布式锁的时候,了解了下redis的java客户端实现redisson,感觉到各方面的支持还怪全面的。故这次也打算使用redisson作为redis连接框架,正好免了引入其他的redis客户端框架。

mvn repository中发现apache.redisson-spring-boot-starter并非spring官方所作,通过查看spring-data-redis的自动配置类RedisAutoConfiguration.class的源码,可以察觉spring-data-redis原生支持的redis客户端框架唯有jedis、lettuce。

package org.springframework.boot.autoconfigure.data.redis;

@Configuration(proxyBeanMethods = false)
// RedisOperation 即 RedisTemplate 的实现接口
// 说明当我们注入一个 RedisTemplate 的时候,该配置类将生效
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
        // @ConditionalOnMissingBean 允许我们自己配置 RedisTemplate
        // 这里的只是默认的
        // 不过,我们其实也可以不去自己配置这个类,redisson.spring本身也有实现类
        // 包括 RedisConnectionFactory,也是同理的
	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
			throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

可能有 好事者(至少我自己) 会问: 为什么不是使用注解 @EnableRedissonHttpSession 来开启 redisson 的session支持呢?

  1. 其实吧,有,但是被注解摒弃了,我甚至还在网上看到有博客在介绍这个过时的配置类
package org.redisson.spring.session.config;

/**
 * Deprecated. Use spring-session implementation based on Redisson Redis Data module
 *
 * @author Nikita Koksharov
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// 这个配置类也被摒弃了
@Import(RedissonHttpSessionConfiguration.class)
@Configuration
// 官方嫌弃
@Deprecated
public @interface EnableRedissonHttpSession {

    int maxInactiveIntervalInSeconds() default MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
    
    String keyPrefix() default "";
    
}

从javadoc可以得知,与spring的集成逻辑,应该走 redisson.data 模块

  1. 反过来想,Redisson 肯定会将推荐的集成方式(配置类)放在 redisson-spring-boot-srarter 的自动配置类 RedissonAutoConfiguration.class

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lWZflL56-1687008021654)(img.png)]


spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.redisson.spring.starter.RedissonAutoConfiguration

其实如果你了解 spring.data 模块,也可以隐隐的捕获到这一信息(抽象层次: 应用领域 -> XxxTemplate -> XxxConnectFactory -> 底层的第三方客户端框架),不应该是第三方框架自己来组织 repository。这里说的 应用领域 可以狭义的理解为 spring.session

package org.redisson.spring.starter;

@Configuration
@ConditionalOnClass({Redisson.class, RedisOperations.class})
@AutoConfigureBefore(RedisAutoConfiguration.class)
@EnableConfigurationProperties({RedissonProperties.class, RedisProperties.class})
public class RedissonAutoConfiguration {

    private static final String REDIS_PROTOCOL_PREFIX = "redis://";
    private static final String REDISS_PROTOCOL_PREFIX = "rediss://";

    @Autowired(required = false)
    private List<RedissonAutoConfigurationCustomizer> redissonAutoConfigurationCustomizers;

    @Autowired
    private RedissonProperties redissonProperties;

    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private ApplicationContext ctx;

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean(RedisConnectionFactory.class)
    public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
        return new RedissonConnectionFactory(redisson);
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(RedissonClient.class)
    public RedissonClient redisson() throws IOException {
        // ...
    }
}

简单来说,我们可以通过配置或者手动注入bean:RedissonClient来拉通…


加赠 spring.data.RedisTemplate 的族谱

在这里插入图片描述

2. spring.data.redis --> spring.session.data.redis

同上,redisson 自己搞的session相关的配置类也弃用了,彻底的走上了 spring.data 的整合套路

package org.redisson.spring.session.config;

/**
 * Deprecated. Use spring-session implementation based on Redisson Redis Data module
 *
 * @author Nikita Koksharov
 *
 */
@Configuration
@Deprecated
public class RedissonHttpSessionConfiguration extends SpringHttpSessionConfiguration implements ImportAware {
    @Bean
    public RedissonSessionRepository sessionRepository(
            RedissonClient redissonClient, ApplicationEventPublisher eventPublisher) {
        // ...
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        // ...
    }
}

基于引入的 spring-session-data-redis 模块,那么实际将走的配置类也不再是 spring.session 的了 。简单看一下,整合spring.session和spring.data.redis后的全新配置类

package org.springframework.session.data.redis.config.annotation.web.http;

/**
 * Exposes the {@link SessionRepositoryFilter} as a bean named
 * {@code springSessionRepositoryFilter}. In order to use this a single
 * {@link RedisConnectionFactory} must be exposed as a Bean.
 *
 * @author Rob Winch
 * @author Eddú Meléndez
 * @author Vedran Pavic
 * @see EnableRedisHttpSession
 * @since 1.0
 */
@Configuration(proxyBeanMethods = false)
// 可以看到继承了SpringHttpSessionConfiguration(spring.session配置类)
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
		implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {

    // 这里隐掉了很多属性、方法:定时调度配置、事件相关、类加载相关的
    
	@Bean
	public RedisIndexedSessionRepository sessionRepository() {
        // 可以看到 redisTemplate 最终是在为 sessionRepository 服务的
		RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
		RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
        // 设置应用的事件发布器(后续可以用来回调监听、完善session的生命周期)
		sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
		if (this.indexResolver != null) {
            // indexResolver 是用于处理根据内存缓存的key找到对应redis中的key的
			sessionRepository.setIndexResolver(this.indexResolver);
		}
		if (this.defaultRedisSerializer != null) {
            // 序列化器
			sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
		}
		sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
		if (StringUtils.hasText(this.redisNamespace)) {
            // 一段字符串,表示 spring.session 的key(前缀)
			sessionRepository.setRedisKeyNamespace(this.redisNamespace);
		}
        // 这个配置就很贴心
		sessionRepository.setFlushMode(this.flushMode);
		sessionRepository.setSaveMode(this.saveMode);
        // 这个编号并不是 spring.datasource 的数据库,是redis库
        // 默认 0
		int database = resolveDatabase();
		sessionRepository.setDatabase(database);
        // 配置扩展走的是 开放配置 的方式
		this.sessionRepositoryCustomizers
				.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
		return sessionRepository;
	}

    // redisConnectFactory 搁这里传递呢
	@Autowired
	public void setRedisConnectionFactory(
			@SpringSessionRedisConnectionFactory ObjectProvider<RedisConnectionFactory> springSessionRedisConnectionFactory,
			ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
		RedisConnectionFactory redisConnectionFactoryToUse = springSessionRedisConnectionFactory.getIfAvailable();
		if (redisConnectionFactoryToUse == null) {
			redisConnectionFactoryToUse = redisConnectionFactory.getObject();
		}
		this.redisConnectionFactory = redisConnectionFactoryToUse;
	}
}

本来想到这,就直接转向 接入web.http呢,但是多看了一看SessionRepository,怎么说呢?还是再多看一眼源码吧,家人们…这javadoc写的真好

这个实现类,即我们上面配置类所注入的…

package org.springframework.session.data.redis;

// 阅读理解,哈哈哈
// 这里大概描述了1个session会话所对应redis中使用的3个key
// 以及这些个key的作用说明
/**
 * <p>
 * A {@link org.springframework.session.SessionRepository} that is implemented using
 * Spring Data's {@link org.springframework.data.redis.core.RedisOperations}. In a web
 * environment, this is typically used in combination with {@link SessionRepositoryFilter}
 * . This implementation supports {@link SessionDeletedEvent} and
 * {@link SessionExpiredEvent} by implementing {@link MessageListener}.
 * </p>
 *
 * <h2>Creating a new instance</h2>
 *
 * A typical example of how to create a new instance can be seen below:
 *
 * <pre>
 * RedisTemplate&lt;Object, Object&gt; redisTemplate = new RedisTemplate&lt;&gt;();
 *
 * // ... configure redisTemplate ...
 *
 * RedisIndexedSessionRepository redisSessionRepository =
 *         new RedisIndexedSessionRepository(redisTemplate);
 * </pre>
 *
 * <p>
 * For additional information on how to create a RedisTemplate, refer to the
 * <a href = "https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/"
 * > Spring Data Redis Reference</a>.
 * </p>
 *
 * <h2>Storage Details</h2>
 *
 * The sections below outline how Redis is updated for each operation. An example of
 * creating a new session can be found below. The subsequent sections describe the
 * details.
 *
 * <pre>
 * HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr2:attrName someAttrValue2
 * EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
 * APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
 * EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
 * SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
 * EXPIRE spring:session:expirations1439245080000 2100
 * </pre>
 *
 * <h3>Saving a Session</h3>
 *
 * <p>
 * Each session is stored in Redis as a
 * <a href="https://redis.io/topics/data-types#hashes">Hash</a>. Each session is set and
 * updated using the <a href="https://redis.io/commands/hmset">HMSET command</a>. An
 * example of how each session is stored can be seen below.
 * </p>
 *
 * <pre>
 * HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:attrName someAttrValue sessionAttr:attrName2 someAttrValue2
 * </pre>
 *
 * <p>
 * In this example, the session following statements are true about the session:
 * </p>
 * <ul>
 * <li>The session id is 33fdd1b6-b496-4b33-9f7d-df96679d32fe</li>
 * <li>The session was created at 1404360000000 in milliseconds since midnight of 1/1/1970
 * GMT.</li>
 * <li>The session expires in 1800 seconds (30 minutes).</li>
 * <li>The session was last accessed at 1404360000000 in milliseconds since midnight of
 * 1/1/1970 GMT.</li>
 * <li>The session has two attributes. The first is "attrName" with the value of
 * "someAttrValue". The second session attribute is named "attrName2" with the value of
 * "someAttrValue2".</li>
 * </ul>
 *
 *
 * <h3>Optimized Writes</h3>
 *
 * <p>
 * The {@link RedisIndexedSessionRepository.RedisSession} keeps track of the properties
 * that have changed and only updates those. This means if an attribute is written once
 * and read many times we only need to write that attribute once. For example, assume the
 * session attribute "sessionAttr2" from earlier was updated. The following would be
 * executed upon saving:
 * </p>
 *
 * <pre>
 * HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue
 * </pre>
 *
 * <h3>SessionCreatedEvent</h3>
 *
 * <p>
 * When a session is created an event is sent to Redis with the channel of
 * "spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe" such that
 * "33fdd1b6-b496-4b33-9f7d-df96679d32fe" is the session id. The body of the event will be
 * the session that was created.
 * </p>
 *
 * <p>
 * If registered as a {@link MessageListener}, then {@link RedisIndexedSessionRepository}
 * will then translate the Redis message into a {@link SessionCreatedEvent}.
 * </p>
 *
 * <h3>Expiration</h3>
 *
 * <p>
 * An expiration is associated to each session using the
 * <a href="https://redis.io/commands/expire">EXPIRE command</a> based upon the
 * {@link RedisIndexedSessionRepository.RedisSession#getMaxInactiveInterval()} . For
 * example:
 * </p>
 *
 * <pre>
 * EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
 * </pre>
 *
 * <p>
 * You will note that the expiration that is set is 5 minutes after the session actually
 * expires. This is necessary so that the value of the session can be accessed when the
 * session expires. An expiration is set on the session itself five minutes after it
 * actually expires to ensure it is cleaned up, but only after we perform any necessary
 * processing.
 * </p>
 *
 * <p>
 * <b>NOTE:</b> The {@link #findById(String)} method ensures that no expired sessions will
 * be returned. This means there is no need to check the expiration before using a session
 * </p>
 *
 * <p>
 * Spring Session relies on the expired and delete
 * <a href="https://redis.io/topics/notifications">keyspace notifications</a> from Redis
 * to fire a SessionDestroyedEvent. It is the SessionDestroyedEvent that ensures resources
 * associated with the Session are cleaned up. For example, when using Spring Session's
 * WebSocket support the Redis expired or delete event is what triggers any WebSocket
 * connections associated with the session to be closed.
 * </p>
 *
 * <p>
 * Expiration is not tracked directly on the session key itself since this would mean the
 * session data would no longer be available. Instead a special session expires key is
 * used. In our example the expires key is:
 * </p>
 *
 * <pre>
 * APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
 * EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
 * </pre>
 *
 * <p>
 * When a session expires key is deleted or expires, the keyspace notification triggers a
 * lookup of the actual session and a {@link SessionDestroyedEvent} is fired.
 * </p>
 *
 * <p>
 * One problem with relying on Redis expiration exclusively is that Redis makes no
 * guarantee of when the expired event will be fired if the key has not been accessed.
 * Specifically the background task that Redis uses to clean up expired keys is a low
 * priority task and may not trigger the key expiration. For additional details see
 * <a href="https://redis.io/topics/notifications">Timing of expired events</a> section in
 * the Redis documentation.
 * </p>
 *
 * <p>
 * To circumvent the fact that expired events are not guaranteed to happen we can ensure
 * that each key is accessed when it is expected to expire. This means that if the TTL is
 * expired on the key, Redis will remove the key and fire the expired event when we try to
 * access the key.
 * </p>
 *
 * <p>
 * For this reason, each session expiration is also tracked to the nearest minute. This
 * allows a background task to access the potentially expired sessions to ensure that
 * Redis expired events are fired in a more deterministic fashion. For example:
 * </p>
 *
 * <pre>
 * SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
 * EXPIRE spring:session:expirations1439245080000 2100
 * </pre>
 *
 * <p>
 * The background task will then use these mappings to explicitly request each session
 * expires key. By accessing the key, rather than deleting it, we ensure that Redis
 * deletes the key for us only if the TTL is expired.
 * </p>
 * <p>
 * <b>NOTE</b>: We do not explicitly delete the keys since in some instances there may be
 * a race condition that incorrectly identifies a key as expired when it is not. Short of
 * using distributed locks (which would kill our performance) there is no way to ensure
 * the consistency of the expiration mapping. By simply accessing the key, we ensure that
 * the key is only removed if the TTL on that key is expired.
 * </p>
 *
 * @author Rob Winch
 * @author Vedran Pavic
 * @since 2.2.0
 */
public class RedisIndexedSessionRepository
		implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener {
    // 隐掉所有代码 ...
}

3. spring.session.data.redis --> spring.web

有意思的是,这处的配置,居然给我在spring.session.data.redis配置类的父配置类中找到了(即spring.session的配置类)

package org.springframework.session.config.annotation.web.http;

@Configuration(proxyBeanMethods = false)
public class SpringHttpSessionConfiguration implements ApplicationContextAware {

    // 同样也隐去了一大坨别的东西,有空再慢慢看吧

    @PostConstruct
    public void init() {
        CookieSerializer cookieSerializer = (this.cookieSerializer != null) ? this.cookieSerializer
                : createDefaultCookieSerializer();
        this.defaultHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
    }

    // spring.session.SessionRepositoryFilter 就是他了
    @Bean
    public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(
            SessionRepository<S> sessionRepository) {
        // 可以想到,我们注入了的 sessionRepository 最终也是为了给这个过滤器服务的
        SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
        // 这又一个处理器,帮助request/response对象处理 sessionId 的逻辑实现
        sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
        return sessionRepositoryFilter;
    }
}

4. spring.web --> javax.servlet

这块其实在 spring.webmvc 相关的随笔中多少沾了点边,这里走个流程吧。毕竟饿了还不能及时的吃饭的话,就有点上班的意思了,这不好。

在这里插入图片描述

吃饭之前,我们可以再想个问题:且不说我们准备从请求、响应的参数中提取sessionId并处理,如果我们想要在拦截器中扩展请求域的上下文参数。这时候,把数据、处理逻辑维护到拦截器里面肯定是不够OOP的,

于是乎,javax.servlet 也对此类提案,发布了可接入的扩展方式——给request/response之上直接一层wrapper,而我们的扩展就是针对这个wrapper,原有逻辑委托内部的request/response即可

// org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

    // 接入 spring.session 的地方就发生在这里
    // 这俩都是当前类的静态内部类
    // 这还不完,该类还对原生的Session做了wrapper增强
    SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
    SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
            response);

    try {
        filterChain.doFilter(wrappedRequest, wrappedResponse);
    }
    finally {
        wrappedRequest.commitSession();
    }
}

大体的内部结构如此,后面还有时间再看吧,先吃到嘴的饭了:)

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

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

相关文章

树莓派+Docker+cpolar(内网穿透)+Nignx

首先安装Raspberry Pi Imager&#xff0c;用于给SD卡安装系统镜像。 使用Raspberry Pi Imager&#xff08;树莓派镜像烧录器&#xff09;烧录镜像文件到SD中&#xff0c;操作步骤如下图所示&#xff1a; docker安装nginx提供web服务 获取最新版本的docker安装包&#xff1a; su…

linux上启动两个nginx实例

一台机器上启动两个nginx实例&#xff0c;主要是端口冲突问题&#xff0c;另外&#xff0c;如果一个是通过编译安装&#xff0c;另一个是拷贝的&#xff0c;那么还需要修改静态文件根路径。 这里&#xff0c;我第一个nginx是通过源码编译安装&#xff0c;安装目录是默认的/usr/…

live555交叉编译

下载live555最新源码&#xff1a;http://www.live555.com/liveMedia/public/live555-latest.tar.gz 如果是要下载具体的live555版本&#xff0c;比如2023.06.14发布的版本&#xff0c;则可以浏览器输入&#xff1a;http://www.live555.com/liveMedia/public/live.2023.06.14.t…

GPT 模型的工作原理 你知道吗?

动动发财的小手&#xff0c;点个赞吧&#xff01; Source[1] 简介 当我使用 GPT 模型编写我的前几行代码时是 2021 年&#xff0c;那一刻我意识到文本生成已经到了一个拐点。在此之前&#xff0c;我在研究生院从头开始编写语言模型&#xff0c;并且我有使用其他文本生成系统的经…

GPT-4满分通过MIT本科数学考试这套提示词火了

万万想不到啊&#xff0c;MIT数学考试&#xff0c;被GPT-4攻破了&#xff1f;&#xff01; 突然有人在最新论文工作中高调宣布&#xff1a; GPT-4在MIT的数学和EECS&#xff08;电气工程和计算机科学系&#xff09;本科学位考试中&#xff0c;表现出的能力完全满足毕业要求。…

图解左连接、右连接、内连接、全连接(小白入门)

目录 一、七种连接情况二、左连接具体例子&#xff1a;力扣数据库题目 175. 组合两个表答案1. 连接条件为&#xff1a;on Person.PersonId Address.personId2. 连接条件为&#xff1a;on Person.PersonId ! Address.personId 三、左连接不包含内连接四、右连接五、右连接不包含…

利用腾讯云函数隐藏C2服务器

1、简介 腾讯云函数&#xff0c;可以为企业和开发者提供无服务器执行环境&#xff0c;无需购买和管理服务器&#xff0c;只需要在腾讯云上使用平台支持的语言编写核心代码并设置代码运行的条件&#xff0c;即可在腾讯云基础设施上弹性 安全地运行代码。 C2服务器所有流量通过腾…

CSS入门(网络安全方向)——id与class

CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;&#xff0c;是一种用来为结构化文档&#xff08;如 HTML 文档或 XML 应用&#xff09;添加样式&#xff08;字体、间距和颜色等&#xff09;的计算机语言&#xff0c;CSS 文件扩展名为 .css。 通过使用 CSS 我…

Python电商数据分析系列-薪资预测

Python电商数据分析系列-薪资预测 学习目标&#xff1a; 快速掌握简单且常用的数据分析任务 自己实现预测简单预测任务 学习内容&#xff1a; 搭建 Java 开发环境掌握 Java 基本语法掌握条件语句掌握循环语句 学习对象 想快速入门的本科生转行人员想学习新技能&#xff0c…

【Python】遇到 from PIL import Image 报错怎么办?

嗨害大家好鸭&#xff01;我是小熊猫~ 很久之前有一个辛苦的小程序员 它在做验证码识别的的时候 遇上了一个小小的错误沙堆 经过它一点也不辛苦的努力&#xff0c;解决了它 于是它决定把这个宝贵的经历分享给大家~ 问题的出现&#xff1a; 前方出现红色波浪线 开始感到有一…

基于风险的测试

测试级别划分 测试级别的划分能对应解决软件开发的复杂性问题。将一个大规模复杂的系统分解&#xff0c;从小的模块开始&#xff08;单元测试&#xff09;&#xff0c;逐步放大到整个系统级别。 测试类型的设计和安排&#xff0c;将测试类型安排在最适合对应的测试级别中来识别…

精准医疗、空间组学、细胞图谱,腾讯AI Lab用深度学习助力生命科学研究

近日&#xff0c;腾讯 AI Lab 三项研究分别入选国际顶级学术期刊 Nature Methods 和 Nature Communications&#xff0c;再次展示了在生命科学前沿领域上国际领先的技术实力。 这三项研究成果都属于生物细胞研究中的空间组学技术&#xff0c;对于推动精准医疗、细胞图谱绘制、人…

lua自动回收机制gc的理解

gc主要回收哪些垃圾&#xff1f; 在 Lua 中&#xff0c;垃圾回收&#xff08;garbage collection&#xff0c;简称 GC&#xff09;机制主要用于回收动态分配的内存和关闭未关闭的文件句柄。具体来说&#xff0c;Lua 的 GC 机制主要用于回收以下类型的垃圾&#xff1a; 对象&am…

力扣日记剑指 Offer II 003

1. 题目 LeetCode 剑指 Offer II 003. 前 n 个数字二进制中 1 的个数 1.1 题意 计算 0 到 n 之间的每个数的二进制表示中 1 的个数 1.2 分析 看时间复杂度&#xff0c;O(32n)应该能过&#xff08;也就是每个数一位一位去数1的个数&#xff09;&#xff0c;知道low-bit这个运…

插入排序代码

时间复杂度O&#xff08;n&#xff09;

开源中文医疗大模型

中文医疗大模型 中文医疗大模型是指通过利用自然语言处理技术和机器学习算法&#xff0c;在大量的医疗文本数据中预训练出来的模型。它可以实现对医疗信息的分类、摘要、问答系统、机器翻译等功能&#xff0c;是医疗行业中的重要工具。在医疗领域中&#xff0c;大规模语言模型&…

答应我,不会回答这五个问题,不要去面试好吗?

1、创建坐席组的功能模块&#xff0c;如何进行测试用例设计&#xff1f; 解答&#xff1a; 功能测试&#xff0c;使用等价类划分法去分析创建坐席的每个输入项的有效及无效类&#xff0c;同步考虑边界值去设计对应的测试用例&#xff1a; 先进行冒烟测试&#xff0c;正常创建…

CSS基础学习--17 布局 - 水平 垂直对齐

一、元素居中对齐 要水平居中对齐一个元素(如 <div>), 可以使用 margin: auto;。设置到元素的宽度将防止它溢出到容器的边缘。元素通过指定宽度&#xff0c;并将两边的空外边距平均分配&#xff1a; <!DOCTYPE html> <html> <head> <meta charset&q…

关于sub-gaussian 和 sub-exponential随机变量的集中不等式

Concentration inequalities under sub-Gaussian and sub-exponential conditions sub-guassian范数和sub-exponential范数&#xff1a; 如果 f k ( X ) f_{k}(X) fk​(X)为sub-gaussian随机变量&#xff0c;则有如下的定理&#xff1a; 应用&#xff1a; 1.向量值集中 2.…

Apache Doris 冷热分层技术如何实现存储成本降低 70%?|新版本特性

在数据分析的实际场景中&#xff0c;冷热数据往往面临着不同的查询频次及响应速度要求。例如在电商订单场景中&#xff0c;用户经常访问近 6 个月的订单&#xff0c;时间较久远的订单访问次数非常少&#xff1b;在行为分析场景中&#xff0c;需支持近期流量数据的高频查询且时效…