Maison >base de données >Redis >Que faire en cas d'incohérence de session distribuée dans Redis

Que faire en cas d'incohérence de session distribuée dans Redis

青灯夜游
青灯夜游avant
2021-11-12 10:47:142660parcourir

Que faire si la session distribuée est incohérente ? L'article suivant vous présentera la solution à l'incohérence des sessions distribuées dans Redis. J'espère qu'il vous sera utile !

Que faire en cas d'incohérence de session distribuée dans Redis

Solution d'incohérence de session distribuée

1. Quel est le rôle de Session ?

  • Session est une technologie de suivi de session de communication entre le client et le serveur. Le serveur et le client conservent les informations de session de base de l'ensemble de la communication. [Recommandations associées : Tutoriel vidéo Redis]

  • Lorsque le client accède au serveur pour la première fois, le serveur répondra avec un identifiant de session et le stockera dans un cookie local. Les visites ultérieures stockeront l'identifiant de session dans le cookie. Le sessionId est placé dans l'en-tête de la requête pour accéder au serveur

  • Si les données correspondantes ne sont pas trouvées via ce sessionId, le serveur créera un nouveau sessionId et répondra au client.

2. Quels sont les problèmes avec la session distribuée ?

Dans une application Web à serveur unique, les informations de session doivent uniquement être stockées sur le serveur. C'est la manière la plus courante avec laquelle nous sommes entrés en contact ces dernières années

Mais avec la popularité des systèmes distribués dans. Ces dernières années, un seul système ne peut plus répondre aux besoins croissants. Avec la demande croissante de millions d'utilisateurs, les serveurs de déploiement de cluster ont été utilisés par de nombreuses entreprises

Lorsque les requêtes à haute concurrence arrivent sur le serveur, elles sont distribuées à un serveur dans le cluster via l'équilibrage de charge, ce qui peut entraîner la distribution de plusieurs requêtes du même utilisateur sur différents serveurs du cluster, les données de session ne seront pas obtenues, le partage de session devient donc un problème.

Que faire en cas dincohérence de session distribuée dans Redis

3. Comment se fait généralement le clustering de services ?

  • Projet SpringBoot, puis changez simplement le numéro de port pour en démarrer quelques-uns, puis utilisez nginx comme proxy inverse unifié.
  • Projet de microservice SpringCloud, alors à ce stade, vous pouvez utiliser l'équilibrage de charge local du ruban.

4. La différence entre l'équilibrage de charge nginx et l'équilibrage de charge du ruban

  • L'équilibrage de charge nginx est l'équilibrage de charge côté serveur, l'accès uniforme à une adresse et la décision à quel serveur accéder en fonction de l'algorithme d'équilibrage de charge.
  • équilibrage de charge ruban, il s'agit d'un équilibrage de charge local (équilibrage de charge client), qui met en cache et enregistre les adresses client qui fournissent des services et implémente un équilibrage de charge basé sur des algorithmes locaux.

5. Solution de cohérence de session

1. Réplication de session (synchronisation)

Que faire en cas dincohérence de session distribuée dans Redis

Idée : plusieurs serveurs synchronisent les sessions entre eux, de sorte que chaque serveur Toutes les sessions soient incluses dans chacun session

Avantages : Les fonctions prises en charge par le serveur, l'application n'a pas besoin de modifier le code

Inconvénients :

  • La synchronisation de session nécessite une transmission de données, qui occupe la bande passante intranet et a un délai
  • Tous les serveurs incluent Toutes les sessions données, le volume de données est limité par la mémoire et ne peut pas être étendu horizontalement

2. Méthode de stockage du client

Que faire en cas dincohérence de session distribuée dans Redis

Idée : le serveur stocke les sessions de tous les utilisateurs et l'utilisation de la mémoire est importante, donc les sessions peuvent être stockées Dans le cookie du navigateur, chaque extrémité n'a besoin de stocker que les données d'un utilisateur

Avantages : Le serveur n'a pas besoin de stocker

Inconvénients :

  • Chaque requête http transporte la session, occupant la bande passante du réseau externe
  • Les données sont stockées à la fin et transmises sur le réseau, il existe des risques de sécurité tels que la fuite, la falsification et le vol. La taille des données stockées dans la session et le nombre de cookies de nom de domaine sont limités. Remarque : bien que cela. La solution n’est pas couramment utilisée, c’est bien une idée.
3. Cohérence du hachage du proxy inverse

Idée : afin de garantir une haute disponibilité, le serveur dispose de plusieurs serveurs redondants. La couche proxy inverse peut-elle faire quelque chose pour garantir que la demande du même utilisateur tombe sur le serveur ? à propos d'un serveur ? Option 1 : hachage proxy à quatre couches

La couche proxy inverse utilise l'adresse IP de l'utilisateur pour le hachage afin de garantir que les requêtes avec la même IP tombent sur le même serveur

Que faire en cas dincohérence de session distribuée dans RedisOption 2 : agent à sept couches hachage

Le proxy inverse utilise certains attributs commerciaux du protocole http pour le hachage, tels que sid, city_id, user_id, etc., ce qui peut mettre en œuvre la stratégie de hachage de manière plus flexible pour garantir que les requêtes du même utilisateur du navigateur tombent sur le même serveur

Avantages :

  • Il vous suffit de changer la configuration nginx, pas besoin de modifier le code de l'application
  • Équilibrage de charge, tant que l'attribut de hachage est uniforme, la charge de plusieurs serveurs est équilibrée
  • Peut prendre en charge l'expansion horizontale du serveur (la méthode de synchronisation de session n'est pas possible) (limitée par la mémoire)

Inconvénients :

  • Si le serveur est redémarré, une partie de la session sera perdue, entraînant un impact commercial, comme la reconnexion de certains utilisateurs
  • Si le serveur est étendu horizontalement et la session est redistribuée après rehachage, il y aura également Certains utilisateurs ne peuvent pas être acheminés vers la bonne session

La session a généralement une période de validité. Les deux défauts peuvent être considérés comme équivalents à un échec partiel de session, ce qui n'est généralement pas un gros problème.

Concernant le hachage à quatre couches ou à sept couches, je recommande personnellement le premier : laissez les logiciels professionnels faire des choses professionnelles, et le proxy inverse est responsable du transfert. Essayez de ne pas introduire d'attributs commerciaux de la couche d'application, sauf si vous y êtes obligé (par exemple). , il y a parfois de nombreuses activités multi-activités dans la salle informatique qui doivent être acheminées vers des serveurs situés dans différentes salles informatiques en fonction des attributs métier).

La différence entre l'équilibrage de charge à quatre et sept couches

4. Stockage centralisé unifié back-end

Que faire en cas dincohérence de session distribuée dans Redis

Avantages :

  • Aucun risque de sécurité
  • Peut être étendu horizontalement, la base de données/le cache peut être coupé horizontalement. Il suffit de la diviser en parties
  • Il n'y aura aucune perte de session lorsque le serveur sera redémarré ou étendu

Inconvénient : un appel réseau est ajouté et le code de l'application doit être modifié

Pour la base de données stockage ou cache, je recommande personnellement ce dernier : fréquence de lecture de session Elle sera très élevée et la pression sur la base de données sera relativement élevée. Si une haute disponibilité de session est nécessaire, le cache peut être rendu hautement disponible, mais dans la plupart des cas, la session peut être perdue et il n'est généralement pas nécessaire d'envisager une haute disponibilité.

Résumé

Méthodes courantes de conception architecturale pour assurer la cohérence des sessions :

  • Méthode de synchronisation de session : plusieurs serveurs synchronisent les données entre eux
  • Méthode de stockage client Un utilisateur ne stocke que ses propres données
  • Hash de proxy inverse la cohérence peut être obtenue avec un hachage à quatre couches et un hachage à sept couches, garantissant que la demande d'un utilisateur atterrit sur un seul serveur
  • Le serveur de stockage unifié back-end redémarre et se développe, et la session ne sera pas perdue (il est recommandé de unifier le cache back-end) Stockage)

6. Cas pratique : SpringSession+redis résout le problème d'incohérence des sessions distribuées

Étape 1 : Ajouter le package de dépendances de SpringSession et redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

Étape 2 : Fichier de configuration

# 为某个包目录下 设置日志
logging.level.com.ljw=debug

# 设置session的存储方式,采用redis存储
spring.session.store-type=redis
# session有效时长为10分钟
server.servlet.session.timeout=PT10M

## Redis 配置
## Redis数据库索引(默认为0)
spring.redis.database=0
## Redis服务器地址
spring.redis.host=127.0.0.1
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=

Étape 3 : Configurer l'intercepteur

@Configuration
public class SessionConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor())
                //排除拦截的2个路径
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/logout")
                //拦截所有URL路径
                .addPathPatterns("/**");
    }
}
@Configuration
public class SecurityInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        HttpSession session = request.getSession();
        //验证当前session是否存在,存在返回true true代表能正常处理业务逻辑
        if (session.getAttribute(session.getId()) != null){
            log.info("session拦截器,session={},验证通过",session.getId());
            return true;
        }
        //session不存在,返回false,并提示请重新登录。
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write("请登录!!!!!");
        log.info("session拦截器,session={},验证失败",session.getId());
        return false;
    }
}
  • HandlerInterceptor
    • preHandle : est appelé avant que le processeur métier ne traite la requête. Le prétraitement peut effectuer l'encodage, le contrôle de sécurité, la vérification des autorisations, etc. ;
    • postHandle : exécuté une fois que le processeur métier a terminé le traitement de la demande et avant de générer la vue. Post-traitement (le service est appelé et ModelAndView est renvoyé, mais la page n'est pas rendue), il existe une possibilité de modifier ModelAndView
    • afterCompletion : appelé après que DispatcherServlet a complètement traité la demande, il peut être utilisé pour nettoyer les ressources, etc. . Retour au traitement (la page a été rendue)

Étape 4 : Contrôleur

@RestController
@RequestMapping(value = "/user")
public class UserController {

    Map<String, User> userMap = new HashMap<>();

    public UserController() {
        //初始化2个用户,用于模拟登录
        User u1=new User(1,"user1","user1");
        userMap.put("user1",u1);
        User u2=new User(2,"user2","user2");
        userMap.put("user2",u2);
    }

    @GetMapping(value = "/login")
    public String login(String username, String password, HttpSession session) {
        //模拟数据库的查找
        User user = this.userMap.get(username);
        if (user != null) {
            if (!password.equals(user.getPassword())) {
                return "用户名或密码错误!!!";
            } else {
                session.setAttribute(session.getId(), user);
                log.info("登录成功{}",user);
            }
        } else {
            return "用户名或密码错误!!!";
        }
        return "登录成功!!!";
    }

    /**
     * 通过用户名查找用户
     */
    @GetMapping(value = "/find/{username}")
    public User find(@PathVariable String username) {
        User user=this.userMap.get(username);
        log.info("通过用户名={},查找出用户{}",username,user);
        return user;
    }

    /**
     *拿当前用户的session
     */
    @GetMapping(value = "/session")
    public String session(HttpSession session) {
        log.info("当前用户的session={}",session.getId());
        return session.getId();
    }

    /**
     * 退出登录
     */
    @GetMapping(value = "/logout")
    public String logout(HttpSession session) {
        log.info("退出登录session={}",session.getId());
        session.removeAttribute(session.getId());
        return "成功退出!!";
    }

}

Étape 5 : Classe d'entité

@Data
public class User implements  Serializable{

    private int id;
    private String username;
    private String password;

    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

}

Étape 6 : Test d'accès

Journal En premier:http://127.0.0.1:8080/user/login?username=user1&password=user1

Requêtehttp://127.0.0.1:8080/user/find/user1

7 . Analyse Le principe redis de SpringSession

Étape 1 : Analyser la structure des données redis de SpringSession

127.0.0.1:6379> keys *
1) "spring:session:sessions:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b"
3) "spring:session:expirations:1635413520000"
4) "spring:session:sessions:expires:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"
5) "spring:session:expirations:1635412980000"
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

Le point commun : les trois clés commencent toutes par spring:session:, qui représente les données redis de SpringSession. .

Type de requête

127.0.0.1:6379> type spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b
hash
127.0.0.1:6379> hgetall spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b
// session的创建时间
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\x01|\xc5\xdb\xecu"
// sesson的属性,存储了user对象
3) "sessionAttr:d3434f61-4d0a-4687-9070-610bd7790f3b"
4) "\xac\xed\x00\x05sr\x00\x1ecom.ljw.redis.controller.User\x16\"_m\x1b\xa0W\x7f\x02\x00\x03I\x00\x02idL\x00\bpasswordt\x00\x12Ljava/lang/String;L\x00\busernameq\x00~\x00\x01xp\x00\x00\x00\x01t\x00\x05user1q\x00~\x00\x03"
//最后的访问时间
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\x01|\xc5\xe1\xc7\xed"
//失效时间 100分钟
7) "maxInactiveInterval"
8) "\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\x17p"

Étape 2 : Analyser la stratégie d'expiration redis de SpringSession

Pour les données expirées, il existe généralement trois stratégies de suppression :

  • Suppression programmée, c'est-à-dire lors de la définition du délai d'expiration de la clé, créez une minuterie qui supprime la clé immédiatement lorsque son heure d'expiration arrive.

  • Suppression paresseuse, c'est-à-dire lors de l'accès à une clé, déterminez si la clé a expiré, supprimez-la si elle expire, sinon renvoyez la valeur de la clé.

  • 定期删除,即每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

  • redis删除过期数据采用的是懒性删除+定期删除组合策略,也就是数据过期了并不会及时被删除。

  • 但由于redis是单线程,并且redis对删除过期的key优先级很低;如果有大量的过期key,就会出现key已经过期但是未删除。

  • 为了实现 session 过期的及时性,spring session 采用了定时删除+惰性删除的策略。

定时删除

127.0.0.1:6379> type spring:session:expirations:1635413520000
set
127.0.0.1:6379> smembers  spring:session:expirations:1635413520000
1) "\xac\xed\x00\x05t\x00,expires:d3434f61-4d0a-4687-9070-610bd7790f3b"

Que faire en cas dincohérence de session distribuée dans Redis

2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 
3) "spring:session:expirations:1635413520000" 
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"
  • 1635412980000 是时间戳,等于 2021-10-28 17:23:00,即是该可以在这个时刻过期
  • springsession 定时(1分钟)轮询,删除spring:session:expirations:[?] 的过期成员元素,例如:spring:session:expirations:1635413520000
  • springsesion 定时检测超时的key的值,根据值删除seesion,例如key:spring:session:expirations:1635413520000,值为(sessionId):d3434f61-4d0a-4687-9070-610bd7790f3b的seesion

惰性删除

127.0.0.1:6379> type spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
string
127.0.0.1:6379> get spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
""
127.0.0.1:6379> ttl spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
(integer) 3143
127.0.0.1:6379>
  • 访问 spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的时候,判断key是否过期,过期则删除,否则返回改进的值。
  • 例如 访问spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的时候,判断 ttl 是否过期,过期就直接删除
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 
3) "spring:session:expirations:1635413520000" 
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

更多编程相关知识,请访问:编程视频!!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer