Spring Session動作原理
12683 ワード
本文はvivoインターネット技術微信公衆番号に先発した.https://mp.weixin.qq.com/s/KCOFv0nRuymkX79-RZi9eg
作者:張正林
HTTPプロトコル自体は無状態であり,セッション情報を保存するためにブラウザCookieがセッション要求をSessionIDで識別し,サーバはSessionIDをkeyとしてセッション情報を格納する.単一インスタンスアプリケーションでは、アプリケーションプロセス自体のストレージが考慮され、アプリケーションのボリュームが増加するにつれて横方向の拡張が必要となり、マルチインスタンスsession共有の問題が伴う.
Spring Sessionはマルチプロセスsession共有の問題を解決するために,Spring Sessionの使用方法とSpring Sessionの動作原理について説明する.
1、導入背景
アプリケーションの配置がTomcatにある場合、sessionはTomcatメモリによって維持され、アプリケーションが複数のインスタンスを配置する場合、sessionは共有できません.Spring Sessionは,分散シーンにおけるsession共有の問題を解決するためである.
2、使い方
Spring SessionはHazelcast、Redis、MongoDB、リレーショナル・データベースに格納することをサポートし、本稿では主にsessionがRedisに格納されることを議論する.
web.xml構成:
Spring主な構成:
3、ワークフロー
Tomcat web.xml解析手順:
初期化順序:Listener>Filter>サーブレット.
1)TomcatのlistenerでSessionRepositoryFilterをSpringコンテナにロードします.
前のセクションSpringプロファイルでは、親SpringHttpSessionConfigurationでSessionRepositoryFilterが生成されたRedisHttpSessionConfigurationが宣言されています.
RedisHttpSessionConfigurationクラス継承関係
2)filter初期化
web.xmlに配置されているfilterはDelegatingFilterProxyです.
DelegatingFilterProxyクラス継承関係
DelegatingFilterProxy初期化エントリは、親GenericFilterBeanにあります.
DelegatingFilterProxy Springコンテナに行って第1ステップ初期化されたspringSessionRepositoryFilterを取ります.
これでsessionRepositoryFilterの初期化が完了し、DelegatingFilterProxyは実際にSessionRepositoryFilterをエージェントしました.
SessionRepositoryFilterワークコアプロセス:
4、キャッシュメカニズム
セッションごとに、Redisが実際にキャッシュしたデータは次のとおりです.
spring:session:sessions:1b8b2340-da25-4ca6-864c-4af28f033327
spring:session:sessions:expires:1b8b2340-da25-4ca6-864c-4af28f033327
spring:session:expirations:1557389100000
Spring:session:sessionsはhash構造で、Spring Sessionの主な内容を格納します.
Spring:session:sessions:expiresはstring構造であり、空の値を格納します.
Spring:session:expirationsはset構造で、1557389100000時点で期限切れのspring:session:sessions:expiresキー値を格納します.
RedisSessionExpirationPolicy、3つのキー値生成プロセス:
Redis期限切れキーには、タイミング削除、不活性削除、定期削除の3つの削除ポリシーがあります.タイミング削除:タイマーをメンテナンスすることで、期限が切れたらすぐに削除するのが最も有効ですが、cpu時間を最も浪費します. 不活性削除:プログラムはキーを取り出したときに期限が切れたかどうかを判断し、期限が切れてから削除する.この方法はcpu時間に友好的で、メモリに友好的ではない. 定期削除:一定時間ごとに期限切れキーを削除する操作を実行し、削除するたびの実行時間と頻度を制限する折衷です.
Redisは、不活性な削除と定期的な削除のポリシーを採用しています.このことから,Redisに依存する期限切れポリシーは,期限切れkeyをリアルタイムで削除することは信頼できないことが分かる.
一方、Spring Sessionが期限切れになった後にビジネスロジック処理を行う可能性があります.また、sessionの中の情報が必要です.spring:session:sessionsキー値が1つしかないと、Redis削除は削除され、ビジネスはsession情報を取得できません.
Spring:session:expirationsキーにはspring:session:sessions:expiresキーが格納されていますが、spring:session:sessions:expiresキーの有効期限はspring:session:expirationsキーとspring:sessionsキー(実際のSpring Sessionが有効期限切れイベント処理に購読するspring:session:sessions:expiresキーより5分早く、次のセクションで具体的に説明します)です.これにより、期限切れのイベントに購読するとspring:session:sessionsキー値も取得できます.
Redis自体のクリーンアップメカニズムでspring:session:sessions:expiresがタイムリーにクリアされていない場合は、spring Sessionが提供するタイミングタスクポケットを通じてspring:session:sessions:expiresクリアを保証できます.
RedisSessionExpirationPolicy,sessionスケジュールタスクのクリーンアップ
5、イベント購読
デフォルトでは、少なくともサブスクリプション・キースペース通知gxEイベント(http://redisdoc.com/topic/notification.html).
コンフィギュレーション
RedisHttpSessionConfiguration、リスニングイベントの登録:
RedisOperationsSessionRepository、イベント処理:
イベント購読サンプル:
6、まとめ
Spring Sessionは私たちに良い分布式環境の下で資源共有問題の解決構想を提供して、それはサーブレット規範に基づいて実現して、業務の使用時に簡単な配置だけでsession共有を実現することができて、業務と低い結合を実現して、これはすべて後で私たちのプロジェクト開発の中で署名することができる設計理念です.
詳細はvivoインターネット技術微信公衆番号に注目してください
注:転載記事はまずマイクロ信号:labs 2020に連絡してください.
作者:張正林
HTTPプロトコル自体は無状態であり,セッション情報を保存するためにブラウザCookieがセッション要求をSessionIDで識別し,サーバはSessionIDをkeyとしてセッション情報を格納する.単一インスタンスアプリケーションでは、アプリケーションプロセス自体のストレージが考慮され、アプリケーションのボリュームが増加するにつれて横方向の拡張が必要となり、マルチインスタンスsession共有の問題が伴う.
Spring Sessionはマルチプロセスsession共有の問題を解決するために,Spring Sessionの使用方法とSpring Sessionの動作原理について説明する.
1、導入背景
アプリケーションの配置がTomcatにある場合、sessionはTomcatメモリによって維持され、アプリケーションが複数のインスタンスを配置する場合、sessionは共有できません.Spring Sessionは,分散シーンにおけるsession共有の問題を解決するためである.
2、使い方
Spring SessionはHazelcast、Redis、MongoDB、リレーショナル・データベースに格納することをサポートし、本稿では主にsessionがRedisに格納されることを議論する.
web.xml構成:
springSessionRepositoryFilter
org.springframework.web.filter.DelegatingFilterProxy
springSessionRepositoryFilter
/*
Spring主な構成:
p:poolConfig-ref="jedisPoolConfig"
3、ワークフロー
Tomcat web.xml解析手順:
contextInitialized(ServletContextEvent arg0); // Listener
init(FilterConfig filterConfig); // Filter
init(ServletConfig config); // Servlet
初期化順序:Listener>Filter>サーブレット.
1)TomcatのlistenerでSessionRepositoryFilterをSpringコンテナにロードします.
前のセクションSpringプロファイルでは、親SpringHttpSessionConfigurationでSessionRepositoryFilterが生成されたRedisHttpSessionConfigurationが宣言されています.
@Bean
public SessionRepositoryFilter extends ExpiringSession> springSessionRepositoryFilter(
SessionRepository sessionRepository) {
......
return sessionRepositoryFilter;
}
RedisHttpSessionConfigurationクラス継承関係
2)filter初期化
web.xmlに配置されているfilterはDelegatingFilterProxyです.
DelegatingFilterProxyクラス継承関係
DelegatingFilterProxy初期化エントリは、親GenericFilterBeanにあります.
public final void init(FilterConfig filterConfig) throws ServletException {
......
// Let subclasses do whatever initialization they like.
initFilterBean();
......
}
DelegatingFilterProxy Springコンテナに行って第1ステップ初期化されたspringSessionRepositoryFilterを取ります.
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
//targetBeanName springSessionRepositoryFilter
this.targetBeanName = getFilterName();
}
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
これでsessionRepositoryFilterの初期化が完了し、DelegatingFilterProxyは実際にSessionRepositoryFilterをエージェントしました.
SessionRepositoryFilterワークコアプロセス:
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
// HttpServletRequest, HttpServletRequest getSession(boolean create)
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response, this.servletContext);
......
try {
filterChain.doFilter(strategyRequest, strategyResponse);
}
finally {
// session
wrappedRequest.commitSession();
}
}
4、キャッシュメカニズム
セッションごとに、Redisが実際にキャッシュしたデータは次のとおりです.
spring:session:sessions:1b8b2340-da25-4ca6-864c-4af28f033327
spring:session:sessions:expires:1b8b2340-da25-4ca6-864c-4af28f033327
spring:session:expirations:1557389100000
Spring:session:sessionsはhash構造で、Spring Sessionの主な内容を格納します.
hgetall spring:session:sessions:1b8b2340-da25-4ca6-864c-4af28f033327
1) "creationTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01j\x9b\x83\x9d\xfd"
3) "maxInactiveInterval"
4) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\a\b"
5) "lastAccessedTime"
6) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01j\x9b\x83\x9d\xfd"
Spring:session:sessions:expiresはstring構造であり、空の値を格納します.
Spring:session:expirationsはset構造で、1557389100000時点で期限切れのspring:session:sessions:expiresキー値を格納します.
smembers spring:session:expirations:1557389100000
1) "\xac\xed\x00\x05t\x00,expires:1b8b2340-da25-4ca6-864c-4af28f033327"
RedisSessionExpirationPolicy、3つのキー値生成プロセス:
public void onExpirationUpdated(Long originalExpirationTimeInMilli,
ExpiringSession session) {
String keyToExpire = "expires:" + session.getId();
long toExpire = roundUpToNextMinute(expiresInMillis(session));
......
// spring:session:sessions:expires spring:session:expirations key
String expireKey = getExpirationKey(toExpire);
BoundSetOperations
Redis期限切れキーには、タイミング削除、不活性削除、定期削除の3つの削除ポリシーがあります.
Redisは、不活性な削除と定期的な削除のポリシーを採用しています.このことから,Redisに依存する期限切れポリシーは,期限切れkeyをリアルタイムで削除することは信頼できないことが分かる.
一方、Spring Sessionが期限切れになった後にビジネスロジック処理を行う可能性があります.また、sessionの中の情報が必要です.spring:session:sessionsキー値が1つしかないと、Redis削除は削除され、ビジネスはsession情報を取得できません.
Spring:session:expirationsキーにはspring:session:sessions:expiresキーが格納されていますが、spring:session:sessions:expiresキーの有効期限はspring:session:expirationsキーとspring:sessionsキー(実際のSpring Sessionが有効期限切れイベント処理に購読するspring:session:sessions:expiresキーより5分早く、次のセクションで具体的に説明します)です.これにより、期限切れのイベントに購読するとspring:session:sessionsキー値も取得できます.
Redis自体のクリーンアップメカニズムでspring:session:sessions:expiresがタイムリーにクリアされていない場合は、spring Sessionが提供するタイミングタスクポケットを通じてspring:session:sessions:expiresクリアを保証できます.
RedisSessionExpirationPolicy,sessionスケジュールタスクのクリーンアップ
public void cleanExpiredSessions() {
long now = System.currentTimeMillis();
long prevMin = roundDownMinute(now);
......
// spring:session:expirations
String expirationKey = getExpirationKey(prevMin);
// session
Set
5、イベント購読
デフォルトでは、少なくともサブスクリプション・キースペース通知gxEイベント(http://redisdoc.com/topic/notification.html).
コンフィギュレーション
public void configure(RedisConnection connection) {
String notifyOptions = getNotifyOptions(connection);
String customizedNotifyOptions = notifyOptions;
if (!customizedNotifyOptions.contains("E")) {
customizedNotifyOptions += "E";
}
boolean A = customizedNotifyOptions.contains("A");
if (!(A || customizedNotifyOptions.contains("g"))) {
customizedNotifyOptions += "g";
}
if (!(A || customizedNotifyOptions.contains("x"))) {
customizedNotifyOptions += "x";
}
if (!notifyOptions.equals(customizedNotifyOptions)) {
connection.setConfig(CONFIG_NOTIFY_KEYSPACE_EVENTS, customizedNotifyOptions);
}
}
RedisHttpSessionConfiguration、リスニングイベントの登録:
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
RedisOperationsSessionRepository messageListener) {
......
//psubscribe del expired
container.addMessageListener(messageListener,
Arrays.asList(new PatternTopic("__keyevent@*:del"),
new PatternTopic("__keyevent@*:expired")));
//psubscribe created
container.addMessageListener(messageListener, Arrays.asList(new PatternTopic(
messageListener.getSessionCreatedChannelPrefix() + "*")));
return container;
}
RedisOperationsSessionRepository、イベント処理:
public void onMessage(Message message, byte[] pattern) {
......
if (channel.startsWith(getSessionCreatedChannelPrefix())) {
...
// spring:session created
handleCreated(loaded, channel);
return;
}
// spring:session:sessions:expires
String body = new String(messageBody);
if (!body.startsWith(getExpiredKeyPrefix())) {
return;
}
boolean isDeleted = channel.endsWith(":del");
if (isDeleted || channel.endsWith(":expired")) {
......
if (isDeleted) {
// spring:session:sessions:expires del
handleDeleted(sessionId, session);
}
else {
// spring:session:sessions:expires expired
handleExpired(sessionId, session);
}
......
return;
}
}
イベント購読サンプル:
@Component
public class SessionExpiredListener implements ApplicationListener {
@Override
public void onApplicationEvent(SessionExpiredEvent event) {
......
}
}
6、まとめ
Spring Sessionは私たちに良い分布式環境の下で資源共有問題の解決構想を提供して、それはサーブレット規範に基づいて実現して、業務の使用時に簡単な配置だけでsession共有を実現することができて、業務と低い結合を実現して、これはすべて後で私たちのプロジェクト開発の中で署名することができる設計理念です.
詳細はvivoインターネット技術微信公衆番号に注目してください
注:転載記事はまずマイクロ信号:labs 2020に連絡してください.