Heim  >  Artikel  >  Datenbank  >  Was tun bei Inkonsistenzen verteilter Sitzungen in Redis?

Was tun bei Inkonsistenzen verteilter Sitzungen in Redis?

青灯夜游
青灯夜游nach vorne
2021-11-12 10:47:142529Durchsuche

Was tun, wenn die verteilte Sitzung inkonsistent ist? Der folgende Artikel stellt Ihnen die Lösung für die Inkonsistenz verteilter Sitzungen in Redis vor. Ich hoffe, er wird Ihnen helfen!

Was tun bei Inkonsistenzen verteilter Sitzungen in Redis?

Lösung für verteilte Sitzungsinkonsistenzen

1. Welche Rolle spielt die Sitzung?

  • Session ist eine Kommunikationssitzungsverfolgungstechnologie zwischen dem Client und dem Server. Der Server und der Client verwalten die grundlegenden Sitzungsinformationen der gesamten Kommunikation. [Verwandte Empfehlungen: Redis-Video-Tutorial]

  • Wenn der Client zum ersten Mal auf den Server zugreift, antwortet der Server mit einer Sitzungs-ID und speichert diese in einem lokalen Cookie. Die Sitzungs-ID wird in den Anforderungsheader eingefügt, um auf den Server zuzugreifen.

  • Wenn die entsprechenden Daten über diese Sitzungs-ID nicht gefunden werden, erstellt der Server eine neue Sitzungs-ID und antwortet dem Client.

2. Welche Probleme gibt es bei verteilten Sitzungen?

In einer Einzelserver-Webanwendung müssen Sitzungsinformationen nur auf dem Server gespeichert werden. Dies ist die häufigste Art und Weise, wie wir in den letzten Jahren damit in Kontakt gekommen sind.

Aber mit der Popularität verteilter Systeme In den letzten Jahren kann ein einzelnes System den wachsenden Anforderungen nicht mehr gerecht werden. Aufgrund der steigenden Nachfrage von Millionen von Benutzern wurden Cluster-Bereitstellungsserver von vielen Unternehmen verwendet

Wenn Anforderungen mit hoher Parallelität am Server eintreffen, werden sie an einen Server in verteilt Der Cluster wird durch Lastausgleich belastet, was dazu führen kann, dass Sitzungsdaten nicht abgerufen werden, wenn mehrere Anforderungen desselben Benutzers an verschiedene Server im Cluster verteilt werden, sodass die Sitzungsfreigabe zu einem Problem wird.

Was tun bei Inkonsistenzen verteilter Sitzungen in Redis?

3. Wie erfolgt das Service-Clustering im Allgemeinen?

  • SpringBoot-Projekt, ändern Sie dann einfach die Portnummer, um ein paar zu starten, und verwenden Sie dann Nginx als einheitlichen Reverse-Proxy.
  • SpringCloud-Microservice-Projekt, dann können Sie zu diesem Zeitpunkt den lokalen Ribbon-Lastausgleich verwenden.

4. Der Unterschied zwischen Nginx-Lastausgleich und Ribbon-Lastausgleich

  • Nginx-Lastausgleich ist der serverseitige Lastausgleich, der einheitliche Zugriff auf eine Adresse und die Entscheidung, auf welchen Server auf der Grundlage des Lastausgleichsalgorithmus zugegriffen werden soll.
  • Ribbon-Lastausgleich, hierbei handelt es sich um einen lokalen Lastausgleich (Client-Lastausgleich), der die Client-Adressen, die Dienste bereitstellen, zwischenspeichert und aufzeichnet und einen Lastausgleich basierend auf lokalen Algorithmen implementiert. 5. Lösung zur Sitzungskonsistenz Sitzung
Vorteile: Die vom Server unterstützten Funktionen, die Anwendung muss den Code nicht ändern

Nachteile: Die Sitzungssynchronisierung erfordert eine Datenübertragung, die die Intranetbandbreite belegt und eine Verzögerung aufweist.

Alle Server umfassen alle Sitzungen Daten, das Datenvolumen ist durch den Speicher begrenzt und kann nicht horizontal erweitert werden. 2. Client-Speichermethode

Was tun bei Inkonsistenzen verteilter Sitzungen in Redis? Idee: Der Server speichert die Sitzungen aller Benutzer und die Speichernutzung ist daher groß Die Sitzungen können im Browser-Cookie gespeichert werden. Jedes Ende muss nur die Daten eines Benutzers speichern.

Vorteile: Der Server muss nicht speichern

Daten werden am Ende gespeichert und über das Netzwerk übertragen. Es bestehen jedoch Sicherheitsrisiken wie Lecks, Manipulation und Diebstahl. Die in der Sitzung gespeicherte Datengröße und die Anzahl der Domänennamen-Cookies sind begrenzt nicht häufig verwendet, es ist tatsächlich eine Idee.

3. Reverse-Proxy-Hash-Konsistenz
  • Idee: Um eine hohe Verfügbarkeit zu gewährleisten, kann die Reverse-Proxy-Schicht dafür sorgen, dass die Anfrage desselben Benutzers auf den Server gelangt über einen Server?

Option 1: Vierschichtiger Proxy-Hash

Die Reverse-Proxy-Schicht verwendet die IP des Benutzers für das Hashing, um sicherzustellen, dass Anfragen mit derselben IP auf demselben Server landenWas tun bei Inkonsistenzen verteilter Sitzungen in Redis?

Option 2: Siebenschichtiger Agent Hash

Der Reverse-Proxy verwendet bestimmte Geschäftsattribute im http-Protokoll, um Hashing durchzuführen, wie z. B. SID, City_ID, User_ID usw., wodurch die Hash-Strategie flexibler implementiert werden kann, um sicherzustellen, dass die Anfrage desselben Browserbenutzers auf denselben Server fällt

Vorteile:

  • Nur die Nginx-Konfiguration muss geändert werden, der Anwendungscode muss nicht geändert werden
  • Lastausgleich: Solange das Hash-Attribut einheitlich ist, wird die Last mehrerer Server ausgeglichen
  • Kann die horizontale Erweiterung des Servers unterstützen (Sitzungssynchronisationsmethode ist nicht möglich) (begrenzt durch den Speicher)

Nachteile:

  • Wenn der Server neu gestartet wird, geht ein Teil der Sitzung verloren, was zu geschäftlichen Auswirkungen führt, z. B. wenn sich einige Benutzer erneut anmelden.
  • Wenn die Der Server wird horizontal erweitert und die Sitzung wird nach dem erneuten Aufwärmen neu verteilt. Einige Benutzer können nicht zur richtigen Sitzung weitergeleitet werden

Sitzungen haben im Allgemeinen eine Gültigkeitsdauer. Die beiden Mängel können als gleichbedeutend mit einem teilweisen Sitzungsfehler angesehen werden ist im Allgemeinen kein großes Problem.

In Bezug auf Vier-Schicht-Hashing oder Sieben-Schicht-Hashing empfehle ich persönlich Ersteres: Lassen Sie professionelle Software professionelle Aufgaben erledigen, und der Reverse-Proxy ist für die Weiterleitung verantwortlich. Versuchen Sie, Geschäftsattribute der Anwendungsschicht nicht einzuführen, es sei denn, Sie müssen dies tun (z. B , manchmal gibt es viele Multi-Aktivitäten im Computerraum, die je nach Geschäftsattributen an Server in verschiedenen Computerräumen weitergeleitet werden müssen.

Der Unterschied zwischen vierschichtigem und siebenschichtigem Lastausgleich

4. Backend-einheitlicher zentraler Speicher

Was tun bei Inkonsistenzen verteilter Sitzungen in Redis?

Vorteile:

  • Keine Sicherheitsrisiken
  • Horizontal erweiterbar, Datenbank/ Der Cache kann horizontal geschnitten werden. Teilen Sie ihn einfach in Teile auf
  • Es kommt zu keinem Sitzungsverlust, wenn der Server neu gestartet oder erweitert wird

Nachteil: Ein Netzwerkaufruf wird hinzugefügt und der Anwendungscode muss geändert werden

Für Datenbankspeicher oder Cache, ich persönlich empfehle Letzteres: Häufigkeit des Sitzungslesens Es wird sehr hoch sein und der Datenbankdruck wird relativ hoch sein. Wenn eine hohe Sitzungsverfügbarkeit erforderlich ist, kann der Cache hochverfügbar gemacht werden. In den meisten Fällen kann die Sitzung jedoch verloren gehen, und im Allgemeinen besteht keine Notwendigkeit, eine hohe Verfügbarkeit in Betracht zu ziehen.

Zusammenfassung

Gemeinsame Methoden des Architekturdesigns zur Gewährleistung der Sitzungskonsistenz:

  • Sitzungssynchronisationsmethode: Mehrere Server synchronisieren Daten miteinander.
  • Client-Speichermethode. Ein Benutzer speichert nur seine eigenen Daten.
  • Reverse-Proxy-Hash Konsistenz kann sowohl mit vierschichtigem Hashing als auch mit siebenschichtigem Hashing erreicht werden, um sicherzustellen, dass die Anfrage eines Benutzers auf einem Server landet
  • Der Back-End-Unified-Storage-Server wird neu gestartet und erweitert, und die Sitzung geht nicht verloren (dies wird empfohlen). Vereinheitlichen Sie den Back-End-Cache) Speicher)

6. Fallpraxis: SpringSession + Redis löst das Problem der Inkonsistenz verteilter Sitzungen

Schritt 1: Fügen Sie das Abhängigkeitspaket von SpringSession und Redis hinzu

<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>

Schritt 2: Konfigurationsdatei

# 为某个包目录下 设置日志
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=

Schritt 3: Interceptor konfigurieren

@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: wird aufgerufen, bevor der Geschäftsprozessor die Anfrage verarbeitet. Die Vorverarbeitung kann Codierung, Sicherheitskontrolle, Berechtigungsüberprüfung usw. durchführen.
    • postHandle: Wird ausgeführt, nachdem der Geschäftsprozessor die Verarbeitung der Anforderung abgeschlossen hat und bevor die Ansicht generiert wird. Nachbearbeitung (Dienst wird aufgerufen und ModelAndView zurückgegeben, aber die Seite wird nicht gerendert), es besteht die Möglichkeit, ModelAndView zu ändern
    • afterCompletion: Wird aufgerufen, nachdem DispatcherServlet die Anforderung vollständig verarbeitet hat, und kann zum Bereinigen von Ressourcen usw. verwendet werden . Zurück zur Verarbeitung (die Seite wurde gerendert)

Schritt 4: Controller

@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 "成功退出!!";
    }

}

Schritt 5: Entitätsklasse

@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;
    }

}

Schritt 6: Zugriffstest

Protokoll zuerst: http://127.0.0.1:8080/user/login?username=user1&password=user1

Erneut abfragenhttp://127.0.0.1:8080/user/find/user1

7 . Analyse Das Redis-Prinzip von SpringSession

Schritt 1: Analysieren Sie die Redis-Datenstruktur von 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"

Der gemeinsame Punkt: Die drei Schlüssel beginnen alle mit spring:session:, was die Redis-Daten von SpringSession darstellt .

Abfragetyp

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"

Schritt 2: Analysieren Sie die Redis-Ablaufstrategie von SpringSession

Für abgelaufene Daten gibt es im Allgemeinen drei Löschstrategien:

  • Geplantes Löschen, dh beim Festlegen der Ablaufzeit von Erstellen Sie für den Schlüssel einen Timer, der den Schlüssel sofort löscht, wenn seine Ablaufzeit erreicht ist.

  • Verzögertes Löschen, dh beim Zugriff auf einen Schlüssel wird festgestellt, ob der Schlüssel abgelaufen ist. Wenn er abläuft, wird er gelöscht. Andernfalls wird der Schlüsselwert zurückgegeben.

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

  • 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"

Was tun bei Inkonsistenzen verteilter Sitzungen in 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"

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

Das obige ist der detaillierte Inhalt vonWas tun bei Inkonsistenzen verteilter Sitzungen in Redis?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen