まず第一に:
プロジェクトをデプロイするとき、単一のプラットフォームがオフラインになった後も単一のプラットフォームがユーザーにサービスを提供し続けることができるように、クラスターのデプロイが必要になることがあります。これにより、オンラインビジネスを確保できるため、nginxクラスターをデプロイする必要があります。このようにして、サーバーがダウンした後、オンラインビジネスが私たちに損失を引き起こさないことを保証することができます。
私たちのデモンストレーションでは、3台のマシンが最適であり、2台のマシンも可能です。さらに2台の仮想マシンのみを使用して、各サーバーにプロジェクトをデプロイしてから、nginxlクラスターで構成し、各サーバーの重み情報またはそれに対応するルールを構成します。構成後、セッション共有を構成する必要があります。異なるサービス間のセッションは共有できないため、セッションをシリアル化してredisに保存できます。
使用する最初のシステムは次のとおりです。windows10、nginxはより高いバージョンを選択しようとしますが、より低いバージョンがサポートしていないものが多すぎます。
プロジェクトで採用されたフレームワークは、SpringBoot、Shiroです。
ShiroがSessionMangerでSession(session)を管理していることを知っているので、sessionMangerにsessionDaoを挿入するので、sessionDaoを書き直す必要があります。
言うまでもありませんが、コードは次のとおりです。
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import java.io.Serializable;
import java.util.Collection;
/**
* created with IntelliJ IDEA
*
* @author: create by limu
* Date: 2020/12/14
* Time:21:47
*/
public class RedisSessionDao extends AbstractSessionDAO {
@Override
protected Serializable doCreate(Session session) {
return null;
}
@Override
protected Session doReadSession(Serializable sessionId) {
return null;
}
@Override
public void update(Session session) throws UnknownSessionException {
}
@Override
public void delete(Session session) {
}
@Override
public Collection<Session> getActiveSessions() {
return null;
}
}
doCreate(セッションの作成)、doReadSession(セッションの読み取り)、update(セッションの変更)、delete(セッションの削除)、getActiveSession(セッションコレクションの取得)の5つのメソッドが書き換えられていることがわかります。
実際、これはデータベースの追加、削除、変更、およびチェックと同じです。このメソッドは、クライアントとサーバーがセッション操作を行うたびに呼び出されます。redisを選択する理由は、redisの読み取りと書き込みが高速になるためです。
全体のコードは次のとおりです。
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zjxf.base.customer.SysConst;
import com.zjxf.cache.CacheKeys;
import com.zjxf.common.ShiroConst;
import com.zjxf.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* created with IntelliJ IDEA
*
* @author: create by limu
* Date: 2020/7/20
* Time:11:58
*/
@Slf4j
@Service
public class RedisSessionDao extends AbstractSessionDAO {
@Autowired
private RedisService redisService;
public RedisSessionDao() {
super();
}
public RedisSessionDao(RedisService redisService) {
super();
this.redisService = redisService;
}
@Override
protected Serializable doCreate(Session session) {
log.debug("建立会话");
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
JSONObject redisJson = new JSONObject();
redisJson.put("session", sessionToByte(session));
redisService.setContainExpire(CacheKeys.getSystemColonyRedisSession(String.valueOf(session.getId())), redisJson, ShiroConst.SHIRO_SESSION_SESSION_MAX_AGE, TimeUnit.SECONDS);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if (Objects.nonNull(sessionId)) {
Optional<JSONObject> redisJsonOptional = redisService.get(CacheKeys.getSystemColonyRedisSession(String.valueOf(sessionId)));
return redisJsonOptional.map(item -> byteToSession(item.getBytes("session"))).orElse(null);
} else {
return null;
}
}
@Override
public void update(Session session) throws UnknownSessionException {
if (Objects.nonNull(session) && Objects.nonNull(session.getId())) {
JSONObject redisJson = new JSONObject();
redisJson.put("session", sessionToByte(session));
redisService.setContainExpire(CacheKeys.getSystemColonyRedisSession(String.valueOf(session.getId())), redisJson, ShiroConst.SHIRO_SESSION_SESSION_MAX_AGE, TimeUnit.SECONDS);
}
}
@Override
public void delete(Session session) {
log.debug("删除会话");
if (Objects.nonNull(session) && Objects.nonNull(session.getId())) {
redisService.delete(CacheKeys.getSystemColonyRedisSession(String.valueOf(session.getId())));
}
}
@Override
public Collection<Session> getActiveSessions() {
return redisService.keysValue(CacheKeys.getSystemColonyRedisSession(SysConst.EMPTY)).stream()
.map(item -> byteToSession(item.getBytes("session"))).collect(Collectors.toCollection(Lists::newArrayList));
}
/**
* session序列化问题处理
*
* @param session 会话
* @return sessionToByte
*/
public byte[] sessionToByte(Session session) {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
byte[] bytes = null;
try {
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(session);
bytes = bo.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
/**
* 字节转session
*
* @param bytes 字节流
* @return Session
*/
public Session byteToSession(byte[] bytes) {
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
ObjectInputStream in;
SimpleSession session = null;
try {
in = new ObjectInputStream(bi);
session = (SimpleSession) in.readObject();
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
return session;
}
}
上記のセッションをredisにキャッシュしました。上記のシリアル化の問題がある場合は、プライベートメッセージを送信できるため、サービスを再起動すると、サーバーを再起動しても、redisにキャッシュされたセッションの有効期限は切れません。再度ログインする必要はありません。