国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

vertx實(shí)現(xiàn)redis版session共享

fancyLuo / 2878人閱讀

摘要:今天分享的是里的共享問(wèn)題。主要考慮到清除的時(shí)候使用,因?yàn)閿?shù)據(jù)主要以保存在為主,本地保存是輔助作用。

現(xiàn)在越來(lái)越流行微服務(wù)架構(gòu)了,提到微服務(wù)架構(gòu)的話,大家能想到的是spring boot和vertx吧!前者大家聽的比交多些,但是今天我給大家分享的是后者vertx。想要了解更多請(qǐng)閱讀vertx官網(wǎng)http://vertx.io/docs/vertx-we...

廢話不多說(shuō)了,直接進(jìn)主題。今天分享的是vertx web里的session共享問(wèn)題。在公司我用vertx開發(fā)了一個(gè)web平臺(tái),但是需要防止宕機(jī)無(wú)法繼續(xù)提供服務(wù)這種情況,所以部署了兩臺(tái)機(jī)器,這里就開始涉及到了session共享了。為了性能考慮,我就想把session放入redis里來(lái)達(dá)到目的,可是在vertx官網(wǎng)沒(méi)有這種實(shí)現(xiàn),當(dāng)時(shí)我就用Hazelcast(網(wǎng)友說(shuō),性能不怎么好)將就先用著。前幾天我抽時(shí)間看了底層代碼,自己動(dòng)手封裝了下,將session放入redis里。github地址: https://github.com/robin0909/...

原生vertx session 設(shè)計(jì)

下面給出 LocalSessionStoreImpl 和 ClusteredSessionStoreImpl 的結(jié)構(gòu)關(guān)系:

LocalSession:

ClusteredSession:

從上面的結(jié)構(gòu)中我們能找到一個(gè)繼承實(shí)現(xiàn)關(guān)系,頂級(jí)接口是SessionStore,
而SessionStore是什么接口呢?在vertx里,session有一個(gè)專門的設(shè)計(jì),這里的SessionStore就是專門為存儲(chǔ)session而定義接口,看看這個(gè)接口里定義了哪些方法吧!

public interface SessionStore {
  //主要在分布式session共享時(shí)會(huì)用到的屬性,從store里獲取session的重試時(shí)間
  long retryTimeout();
    
  Session createSession(long timeout);

  //根據(jù)sessionId從store里獲取Session
  void get(String id, Handler> resultHandler);

  //刪除
  void delete(String id, Handler> resultHandler);

  //增加session
  void put(Session session, Handler> resultHandler);

  //清空
  void clear(Handler> resultHandler);

  //store的size
  void size(Handler> resultHandler);

  //關(guān)閉,釋放資源操作
  void close();
}

上面很多會(huì)用到有一個(gè)屬性,就是sessionId(id)。在session機(jī)制里,還需要依靠瀏覽器端的cookie。當(dāng)服務(wù)器端session生成后,服務(wù)器會(huì)在cookie里設(shè)置一個(gè)vertx-web.session=4d9db69d-7577-4b17-8a66-4d6a2472cd33 返回給瀏覽器。想必大家也看出來(lái)了,就是一個(gè)uuid碼,也就是sessionId。

接下來(lái),我們可以看下二級(jí)子接口。二級(jí)子接口的作用,其實(shí)很簡(jiǎn)單,直接上代碼,大家就懂了。

public interface LocalSessionStore extends SessionStore {

  long DEFAULT_REAPER_INTERVAL = 1000;

  String DEFAULT_SESSION_MAP_NAME = "vertx-web.sessions";

  static LocalSessionStore create(Vertx vertx) {
    return new LocalSessionStoreImpl(vertx, DEFAULT_SESSION_MAP_NAME, DEFAULT_REAPER_INTERVAL);
  }

  static LocalSessionStore create(Vertx vertx, String sessionMapName) {
    return new LocalSessionStoreImpl(vertx, sessionMapName, DEFAULT_REAPER_INTERVAL);
  }

  static LocalSessionStore create(Vertx vertx, String sessionMapName, long reaperInterval) {
    return new LocalSessionStoreImpl(vertx, sessionMapName, reaperInterval);
  }
}

這里主要為了方面在使用和構(gòu)造時(shí)很優(yōu)雅,router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx))); 有點(diǎn)類似工廠,創(chuàng)造對(duì)象。在這個(gè)接口里,也可以初始化一些專有參數(shù)。所以沒(méi)有什么難度。

對(duì)官方代碼我們也理解的差不多了,接下來(lái)開始動(dòng)手封裝自己的RedisSessionStore吧!

自己的RedisSessionStore封裝

首先我們定義一個(gè)RedisSessionStore接口, 接口繼承SessionStore接口。

/**
 * Created by robinyang on 2017/3/13.
 */
public interface RedisSessionStore extends SessionStore {

    long DEFAULT_RETRY_TIMEOUT = 2 * 1000;

    String DEFAULT_SESSION_MAP_NAME = "vertx-web.sessions";

    static RedisSessionStore create(Vertx vertx) {
        return new RedisSessionStoreImpl(vertx, DEFAULT_SESSION_MAP_NAME, DEFAULT_RETRY_TIMEOUT);
    }

    static RedisSessionStore create(Vertx vertx, String sessionMapName) {
        return new RedisSessionStoreImpl(vertx, sessionMapName, DEFAULT_RETRY_TIMEOUT);
    }

    static RedisSessionStore create(Vertx vertx, String sessionMapName, long reaperInterval) {
        return new RedisSessionStoreImpl(vertx, sessionMapName, reaperInterval);
    }

    RedisSessionStore host(String host);

    RedisSessionStore port(int port);
    
    RedisSessionStore auth(String pwd);
}

接著創(chuàng)建一個(gè)RedisSessionStoreImpl類, 這里我先給出一個(gè)已經(jīng)寫好的RedisSessionStoreImpl, 稍后解釋。

public class RedisSessionStoreImpl implements RedisSessionStore {

    private static final Logger logger = LoggerFactory.getLogger(RedisSessionStoreImpl.class);

    private final Vertx vertx;
    private final String sessionMapName;
    private final long retryTimeout;
    private final LocalMap localMap;

    //默認(rèn)值
    private String host = "localhost";
    private int port = 6379;
    private String auth;

    RedisClient redisClient;

    // 清除所有時(shí)使用
    private List localSessionIds;


    public RedisSessionStoreImpl(Vertx vertx, String defaultSessionMapName, long retryTimeout) {
        this.vertx = vertx;
        this.sessionMapName = defaultSessionMapName;
        this.retryTimeout = retryTimeout;

        localMap = vertx.sharedData().getLocalMap(sessionMapName);
        localSessionIds = new Vector<>();
        redisManager();
    }

    @Override
    public long retryTimeout() {
        return retryTimeout;
    }

    @Override
    public Session createSession(long timeout) {
        return new SessionImpl(new PRNG(vertx), timeout, DEFAULT_SESSIONID_LENGTH);
    }

    @Override
    public Session createSession(long timeout, int length) {
        return new SessionImpl(new PRNG(vertx), timeout, length);
    }

    @Override
    public void get(String id, Handler> resultHandler) {
        redisClient.getBinary(id, res->{
            if(res.succeeded()) {
                Buffer buffer = res.result();
                if(buffer != null) {
                    SessionImpl session = new SessionImpl(new PRNG(vertx));
                    session.readFromBuffer(0, buffer);
                    resultHandler.handle(Future.succeededFuture(session));
                } else {
                    resultHandler.handle(Future.succeededFuture(localMap.get(id)));
                }
            } else {
                resultHandler.handle(Future.failedFuture(res.cause()));
            }
        });
    }

    @Override
    public void delete(String id, Handler> resultHandler) {
        redisClient.del(id, res->{
            if (res.succeeded()) {
                localSessionIds.remove(id);
                resultHandler.handle(Future.succeededFuture(true));
            } else {
                resultHandler.handle(Future.failedFuture(res.cause()));
                logger.error("redis里刪除sessionId: {} 失敗", id, res.cause());
            }
        });
    }

    @Override
    public void put(Session session, Handler> resultHandler) {
        //put 之前判斷session是否存在,如果存在的話,校驗(yàn)下
        redisClient.getBinary(session.id(), res1->{
            if (res1.succeeded()) {
                //存在數(shù)據(jù)
                if(res1.result()!=null) {
                    Buffer buffer = res1.result();
                    SessionImpl oldSession = new SessionImpl(new PRNG(vertx));
                    oldSession.readFromBuffer(0, buffer);
                    SessionImpl newSession = (SessionImpl)session;
                    if(oldSession.version() != newSession.version()) {
                        resultHandler.handle(Future.failedFuture("Version mismatch"));
                        return;
                    }
                    newSession.incrementVersion();
                    writeSession(session, resultHandler);
                } else {
                    //不存在數(shù)據(jù)
                    SessionImpl newSession = (SessionImpl)session;
                    newSession.incrementVersion();
                    writeSession(session, resultHandler);
                }
            } else {
                resultHandler.handle(Future.failedFuture(res1.cause()));
            }
        });
    }

    private void writeSession(Session session, Handler> resultHandler) {

        Buffer buffer = Buffer.buffer();
        SessionImpl sessionImpl = (SessionImpl)session;
        //將session序列化到 buffer里
        sessionImpl.writeToBuffer(buffer);

        SetOptions setOptions = new SetOptions().setPX(session.timeout());
        redisClient.setBinaryWithOptions(session.id(), buffer, setOptions, res->{
            if (res.succeeded()) {
                logger.debug("set key: {} ", session.data());
                localSessionIds.add(session.id());
                resultHandler.handle(Future.succeededFuture(true));
            } else {
                resultHandler.handle(Future.failedFuture(res.cause()));
            }
        });
    }

    @Override
    public void clear(Handler> resultHandler) {
        localSessionIds.stream().forEach(id->{
            redisClient.del(id, res->{
                //如果在localSessionIds里存在,但是在redis里過(guò)期不存在了, 只要通知下就行
                localSessionIds.remove(id);
            });
        });
        resultHandler.handle(Future.succeededFuture(true));
    }

    @Override
    public void size(Handler> resultHandler) {
        resultHandler.handle(Future.succeededFuture(localSessionIds.size()));
    }

    @Override
    public void close() {
        redisClient.close(res->{
            logger.debug("關(guān)閉 redisClient ");
        });
    }

    private void redisManager() {
        RedisOptions redisOptions = new RedisOptions();
        redisOptions.setHost(host).setPort(port).setAuth(auth);

        redisClient = RedisClient.create(vertx, redisOptions);
    }

    @Override
    public RedisSessionStore host(String host) {
        this.host = host;
        return this;
    }

    @Override
    public RedisSessionStore port(int port) {
        this.port = port;
        return this;
    }

    @Override
    public RedisSessionStore auth(String pwd) {
        this.auth = pwd;
        return this;
    }
}

首先,從get()和put()這兩個(gè)方法開始,這兩方法比較核心。

get(), 創(chuàng)建Cookie的時(shí)候會(huì)生成一個(gè)uuid,用這個(gè)id取session,第一次我們發(fā)現(xiàn)無(wú)法取到, 第56行代碼就會(huì)根據(jù)這個(gè)id去生成一個(gè)session。

每次發(fā)送請(qǐng)求的時(shí)候,我們都會(huì)重置session過(guò)期時(shí)間,所以每次get完后,返回給瀏覽器之前都會(huì)有一個(gè)put操作,也就是更新數(shù)據(jù)。這里的put就稍微復(fù)雜一點(diǎn)點(diǎn),在put之前,我們需要先根據(jù)傳過(guò)來(lái)的session里的id從redis里取到session。如果獲取不到,說(shuō)明之前通過(guò)get獲取的session不是同一個(gè)對(duì)象,就出異常,這就相當(dāng)于設(shè)置了一道安全的門檻吧!當(dāng)獲取到了,再比較兩個(gè)session的版本是不是一致的,如果不一致,說(shuō)明session被破環(huán)了,算是第二個(gè)安全門檻設(shè)置吧!都沒(méi)有問(wèn)題了,就可以put session了,并且重新設(shè)置時(shí)間。

這里依賴vertx提供的redisClient來(lái)操作數(shù)據(jù)的,所以我們必須引入這個(gè)依賴:io.vertx:vertx-redis-client:3.4.1 。

接下來(lái)還有一點(diǎn)需要提的是序列化問(wèn)題。這里我使用的是vertx封裝的一種序列化,將數(shù)據(jù)序列化到Buffer里,而SessiomImpl類里又已經(jīng)實(shí)現(xiàn)好了序列化,從SessionImple序列化成Buffer和Buffer反序列化。

public class SessionImpl implements Session, ClusterSerializable, Shareable {
    //...
    
    @Override
  public void writeToBuffer(Buffer buff) {
    byte[] bytes = id.getBytes(UTF8);
    buff.appendInt(bytes.length).appendBytes(bytes);
    buff.appendLong(timeout);
    buff.appendLong(lastAccessed);
    buff.appendInt(version);
    Buffer dataBuf = writeDataToBuffer();
    buff.appendBuffer(dataBuf);
  }

  @Override
  public int readFromBuffer(int pos, Buffer buffer) {
    int len = buffer.getInt(pos);
    pos += 4;
    byte[] bytes = buffer.getBytes(pos, pos + len);
    pos += len;
    id = new String(bytes, UTF8);
    timeout = buffer.getLong(pos);
    pos += 8;
    lastAccessed = buffer.getLong(pos);
    pos += 8;
    version = buffer.getInt(pos);
    pos += 4;
    pos = readDataFromBuffer(pos, buffer);
    return pos;
  }
    
    //...
}

以上就是序列化和反序列化的實(shí)現(xiàn)。

localSessionIds 主要考慮到清除session的時(shí)候使用,因?yàn)閿?shù)據(jù)主要以保存在session為主,本地localSessionIds 保存sessionId是輔助作用。

用法

用法很簡(jiǎn)單,一行代碼就說(shuō)明。

router.route().handler(SessionHandler.create(RedisSessionStore.create(vertx).host("127.0.0.1").port(6349)));

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70057.html

相關(guān)文章

  • Vert.x Blueprint 系列教程(一) | 待辦事項(xiàng)服務(wù)開發(fā)教程

    摘要:本文章是藍(lán)圖系列的第一篇教程。是事件驅(qū)動(dòng)的,同時(shí)也是非阻塞的。是一組負(fù)責(zé)分發(fā)和處理事件的線程。注意,我們絕對(duì)不能去阻塞線程,否則事件的處理過(guò)程會(huì)被阻塞,我們的應(yīng)用就失去了響應(yīng)能力。每個(gè)負(fù)責(zé)處理請(qǐng)求并且寫入回應(yīng)結(jié)果。 本文章是 Vert.x 藍(lán)圖系列 的第一篇教程。全系列: Vert.x Blueprint 系列教程(一) | 待辦事項(xiàng)服務(wù)開發(fā)教程 Vert.x Blueprint 系...

    frank_fun 評(píng)論0 收藏0
  • Vert.x Blueprint 系列教程(二) | 開發(fā)基于消息的應(yīng)用 - Vert.x Kue

    摘要:本文章是藍(lán)圖系列的第二篇教程。這就是請(qǐng)求回應(yīng)模式。好多屬性我們一個(gè)一個(gè)地解釋一個(gè)序列,作為的地址任務(wù)的編號(hào)任務(wù)的類型任務(wù)攜帶的數(shù)據(jù),以類型表示任務(wù)優(yōu)先級(jí),以枚舉類型表示。默認(rèn)優(yōu)先級(jí)為正常任務(wù)的延遲時(shí)間,默認(rèn)是任務(wù)狀態(tài),以枚舉類型表示。 本文章是 Vert.x 藍(lán)圖系列 的第二篇教程。全系列: Vert.x Blueprint 系列教程(一) | 待辦事項(xiàng)服務(wù)開發(fā)教程 Vert.x B...

    elina 評(píng)論0 收藏0
  • vertx的一些問(wèn)題

    摘要:但經(jīng)過(guò)一段使用后,發(fā)現(xiàn)的一些問(wèn)題。這樣產(chǎn)生了一系列問(wèn)題。部署的是異步的多線程環(huán)境,這個(gè)方法必須是線程安全的。小結(jié)的體系結(jié)構(gòu)無(wú)疑是非常先進(jìn)的,多線程異步結(jié)構(gòu),內(nèi)置,支持,支持高可用度,這些都不是輕易能夠提供的。 最近想選高效,簡(jiǎn)潔,擴(kuò)充性強(qiáng)的web框做為移動(dòng)平臺(tái)后臺(tái),在對(duì)一系列框架對(duì)比后,選擇了vertx。但經(jīng)過(guò)一段使用后,發(fā)現(xiàn)vertx的一些問(wèn)題。 1.vertx使用共享資源產(chǎn)生的重復(fù)...

    MRZYD 評(píng)論0 收藏0
  • 【小項(xiàng)目】全棧開發(fā)培訓(xùn)手冊(cè) | 后端(1) vert.x框架理解

    摘要:二來(lái),給大家新開坑的項(xiàng)目一個(gè)參考。因此,本系列以主要以官方文檔為基礎(chǔ),將盡可能多的特性融入本項(xiàng)目,并標(biāo)注官網(wǎng)原文出處,有興趣的小伙伴可點(diǎn)擊深入了解。可以通過(guò)一些特殊協(xié)議例如將消息作為統(tǒng)一消息服務(wù)導(dǎo)出。下載完成后自行修改和。 開坑前言 我給這個(gè)專欄的名氣取名叫做小項(xiàng)目,聽名字就知道,這個(gè)專題最終的目的是帶領(lǐng)大家完成一個(gè)項(xiàng)目。為什么要開這么大一個(gè)坑呢,一來(lái),雖然網(wǎng)上講IT知識(shí)點(diǎn)的書籍鋪天蓋...

    hightopo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<