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!
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.
3. Wie erfolgt das Service-Clustering im Allgemeinen?
4. Der Unterschied zwischen Nginx-Lastausgleich und Ribbon-Lastausgleich
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
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-KonsistenzOption 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 landen
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:
Nachteile:
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
Vorteile:
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:
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; } }
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"
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 3) "spring:session:expirations:1635413520000" 6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"
惰性删除
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>
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!