vertx的http服务实现分布式session

article/2025/8/21 5:56:53

vetrx是基于netty封装的java网络编程框架,比netty开发较为简单,在其基础上提供很多有用功能,能在较短的时间内开发一个http服务器,或其他网络服务。今天我们展示下如何为vertx开发http网关实现分布式session,实现参考spring session。底层使用redis 存贮session

1.SessionStore

vertx读写session 都是通过实现SessionStore实现的,我们可以实现自己的RedisStore 和RedisSession,代码如下

/** Copyright 2014 Red Hat, Inc.**  All rights reserved. This program and the accompanying materials*  are made available under the terms of the Eclipse Public License v1.0*  and Apache License v2.0 which accompanies this distribution.**  The Eclipse Public License is available at*  http://www.eclipse.org/legal/epl-v10.html**  The Apache License v2.0 is available at*  http://www.opensource.org/licenses/apache2.0.php**  You may elect to redistribute this code under either of these licenses.*/package com.ly.session;import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.VertxContextPRNG;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.sstore.AbstractSession;
import io.vertx.ext.web.sstore.SessionStore;
import org.springframework.data.redis.core.RedisTemplate;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;/*** @author <a href="http://tfox.org">Tim Fox</a>*/
public class RedisSessionStore implements SessionStore {/*** Default of how often, in ms, to check for expired sessions*/private static final long DEFAULT_REAPER_INTERVAL = 1000;/*** Default name for map used to store sessions*/private static final String DEFAULT_SESSION_MAP_NAME = "vertx-web.sessions";private VertxContextPRNG random;protected Vertx vertx;private SaveMode saveMode = SaveMode.ON_IMMEDIATE;public void setSaveMode(SaveMode saveMode) {this.saveMode = saveMode;}private RedisTemplate<String,Object> redisTemplate;public RedisTemplate<String, Object> getRedisTemplate() {return redisTemplate;}public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}@Overridepublic Session createSession(long timeout) {return new RedisDataSessionImpl(random, timeout, DEFAULT_SESSIONID_LENGTH);}@Overridepublic Session createSession(long timeout, int length) {return new RedisDataSessionImpl(random, timeout, length);}@Overridepublic SessionStore init(Vertx vertx, JsonObject options) {// initialize a secure randomthis.random = VertxContextPRNG.current(vertx);this.vertx = vertx;return this;}@Overridepublic long retryTimeout() {return 0;}@Overridepublic void get(String id, Handler<AsyncResult<Session>> resultHandler) {Map<Object,Object> sessionInfoMap = redisTemplate.boundHashOps(id).entries();if(sessionInfoMap != null && !sessionInfoMap.isEmpty()){RedisDataSessionImpl redisDataSession = new RedisDataSessionImpl(sessionInfoMap);resultHandler.handle(Future.succeededFuture(redisDataSession));}else{resultHandler.handle(Future.succeededFuture());}}@Overridepublic void delete(String id, Handler<AsyncResult<Void>> resultHandler) {redisTemplate.delete(id);resultHandler.handle(Future.succeededFuture());}@Overridepublic void put(Session session, Handler<AsyncResult<Void>> resultHandler) {Map<Object,Object> sessionMap =  ((RedisDataSessionImpl)session).writeToMap();redisTemplate.boundHashOps(session.id()).putAll(sessionMap);redisTemplate.expire(session.id(),session.timeout(),TimeUnit.SECONDS);resultHandler.handle(Future.succeededFuture());}@Overridepublic void clear(Handler<AsyncResult<Void>> resultHandler) {}@Overridepublic void size(Handler<AsyncResult<Integer>> resultHandler) {}@Overridepublic synchronized void close() {}public class RedisDataSessionImpl extends AbstractSession {private final String ID_FIELD_NAME= "id";private final String EXPIRED_NAME_TIME_NAME = "expiredAtTime";private final String LAST_ACCESS_TIME_NAME = "lastAccessTime";private final String CREATE_TIME_NAME = "createTime";/*** 其他session 属性 key的前缀*/private final String ATTR_PREFIX = "attr_";private final String TIME_OUT = "timeout";/*** 保存session中更改的属性*/private Map<String, Object> deltaMap = new HashMap<>();public RedisDataSessionImpl(Map<Object, Object> sessionInfoMap) {super();Map<String,Object> data = new HashMap<>();for(Map.Entry<Object,Object> entry:sessionInfoMap.entrySet()){String key = (String) entry.getKey();Object val = entry.getValue();if(key.equals(ID_FIELD_NAME)){setId((String) val);}else if(key.equals(TIME_OUT)){setTimeout((Long) val);}else if(key.startsWith(ATTR_PREFIX)){data.put(key.substring(ATTR_PREFIX.length()),val);}}deltaMap.put(LAST_ACCESS_TIME_NAME,System.currentTimeMillis());setLastAccessed(System.currentTimeMillis());setData(data);}@Overrideprotected void setId(String id) {super.setId(id);}@Overrideprotected void setData(Map<String, Object> data) {super.setData(data);}@Overrideprotected void setData(JsonObject data) {super.setData(data);}/*** Important note: This constructor (even though not referenced anywhere) is required for serialization purposes. Do* not remove.*/public RedisDataSessionImpl() {super();}public RedisDataSessionImpl(VertxContextPRNG random) {super(random);}public RedisDataSessionImpl(VertxContextPRNG random, long timeout, int length) {super(random, timeout, length);}@Overridepublic <T> T get(String key) {return super.get(key);}/*** put 是否立即刷新到redis中** @param key* @param obj* @return*/@Overridepublic Session put(String key, Object obj) {Session result = super.put(key, obj);putAndFlush(key,obj);return result;}void putAndFlush(String key,Object val){deltaMap.put(ATTR_PREFIX+key, val);saveIfRequired();}private void saveIfRequired() {if (saveMode.equals(SaveMode.ON_IMMEDIATE)){redisTemplate.boundHashOps(this.id()).putAll(deltaMap);deltaMap = new HashMap<>();}}@Overridepublic Session putIfAbsent(String key, Object obj) {return super.putIfAbsent(key, obj);}@Overridepublic Session computeIfAbsent(String key, Function<String, Object> mappingFunction) {return super.computeIfAbsent(key, mappingFunction);}@Overridepublic <T> T remove(String key) {putAndFlush(key,null);return super.remove(key);}public Map<Object, Object> writeToMap() {Map<Object,Object> sessionMap = new HashMap<>();sessionMap.put(ID_FIELD_NAME,id());sessionMap.put(LAST_ACCESS_TIME_NAME,lastAccessed());sessionMap.put(TIME_OUT,timeout());Map<String,Object> data = data();for(Map.Entry<String,Object> entry:data.entrySet()){sessionMap.put(ATTR_PREFIX+entry.getKey(),entry.getValue());}return sessionMap;}}
}
enum SaveMode{ON_IMMEDIATE,ON_SAVE
}

在实例化handler是,传入RedisStore

   package com.ly;import com.ly.entity.Good;
import com.ly.session.RedisStore;
import com.ly.session.SessionEntity;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.http.*;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.handler.SessionHandler;
import io.vertx.ext.web.sstore.SessionStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;@Component
public class VertxServer implements ApplicationListener<ContextRefreshedEvent> {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {if(event.getApplicationContext().getParent() == null){VertxOptions vertxOptions = new VertxOptions();vertxOptions.setMaxEventLoopExecuteTime(10000*60);vertxOptions.setMaxEventLoopExecuteTimeUnit(TimeUnit.MILLISECONDS);Vertx vertx = Vertx.vertx(vertxOptions);HttpServerOptions options = new HttpServerOptions();options.setIdleTimeout(3600);options.setTcpKeepAlive(true);HttpServer server = vertx.createHttpServer(options);Router router = Router.router(vertx);SessionStore sessionStore = RedisStore.create(vertx,this.redisTemplate);//SessionStore sessionStore = SessionStore.create(vertx);router.routeWithRegex(".*service.*").handler(SessionHandler.create(sessionStore)).handler(ctx->{if(ctx.request().path().contains("initSession")){ctx.request().response().setStatusCode(401).putHeader("Content-Type","application/json").end((new JsonObject().put("msg","illegal request").encode()));}else{ctx.next();}}).handler(ctx->{HttpServerResponse response = ctx.response();Session session =ctx.session();if(session.get("wow") ==null){session.put("wow","wow");System.err.println((String) session.get("wow"));ctx.request().remoteAddress();List<Good> goodList = new ArrayList<>();goodList.add(Good.builder().id(1).stockNumber(100).build());session.put("goods",goodList);}else{System.out.println("goods:"+session.get("goods"));}SessionEntity sessionEntity = new SessionEntity();sessionEntity.setId("28947208947");session.put("typeTest",sessionEntity);String path = ctx.request().path();if(ctx.request().path().contains("service/removeCookie")){Cookie cookie = Cookie.cookie("vertx-web.session",new Date().getTime()+"");cookie.setPath("/");cookie.setSameSite(CookieSameSite.NONE);ctx.addCookie(cookie);ctx.request().response().addCookie(cookie);}else if(path.contains("clearSession")){ctx.session().destroy();}response.putHeader("content-type","text/plain");response.end("hello word!");}).failureHandler((ctx)->{System.out.println(ctx.failure());});server.requestHandler(router).listen(8081);System.err.println("vertxServer start success");}}
}

详细代码可以去github上下载
https://github.com/haozhi-ly/spring-boot-tutorial/tree/master/spring-boot-vertx
启动只需替换redis集群地址
在这里插入图片描述
vertx 也有自己官方的分布式session的实现,也包括redis的版本,大家有兴趣的可以了解下。
https://vertx.io/docs/vertx-web/java/#_handling_sessions


http://chatgpt.dhexx.cn/article/UMRpm9sv.shtml

相关文章

Vert.x入门1 —— 《基础概念》

一、什么是Vertx Vert.x 在JVM上构建响应式应用程序的工具包&#xff0c;Vert.x不是框架而是工具包。Vert.x基于Netty项目&#xff0c;该项目是JVM的高性能异步网络库。 此描述中有三个重要点&#xff1a;工具包&#xff0c;响应式和“在JVM上”。 首先&#xff0c;Vert.x是…

Vertx快速入门参考

Vertx学习 什么是vertx&#xff1f; Vert.x最大的特点就在于异步&#xff08;底层基于Netty&#xff09;&#xff0c;通过事件循环&#xff08;EventLoop&#xff09;来调起存储在异步任务队列&#xff08;CallBackQueue&#xff09;中的任务&#xff0c;大大降低了传统阻塞模…

Vertx入门学习(含代码)

Vertx入门学习 一、Vertx是什么&#xff1f;二、Vertx基本概念三、Vertx能干什么&#xff1f;四、Vertx的技术体系五、快速体验&#xff1a;搭建一个简单的Vertx项目并输出Hello World六、单元测试总结 一、Vertx是什么&#xff1f; github: https://github.com/vert-x3 官网&…

Vert.x(vertx) 简明介绍

摘要 Vert.x最大的特点就在于异步&#xff08;底层基于Netty&#xff09;&#xff0c;通过事件循环&#xff08;EventLoop&#xff09;来调起存储在异步任务队列&#xff08;CallBackQueue&#xff09;中的任务&#xff0c;大大降低了传统阻塞模型中线程对于操作系统的开销。因…

Vertx学习一:这玩意是到底是个啥

Vertx&#xff0c;融合Java、Ruby、Python等语言的高性能架构&#xff0c;架构师必读 原文链接&#xff1a; http://www.360doc.com/content/18/0203/14/39530679_727432611.shtml 目录&#xff1a; 一、Vert.x简介 二、Vert.x原理解析 三、Vert牛刀小试 四、Vert应用实践 五…

【java】vertx从入门到放弃——入门(四)Codec

什么是Codec&#xff1f;概念这个玩意我是真不想说&#xff0c;毕竟我没有找到具体的概念&#xff0c;我自己大致的理解就是&#xff0c;用EventBus传输类的时候&#xff0c;对这个类进行序列化和反序列化的东西&#xff0c;因为vertx使用json进行传输&#xff0c;所以这个玩意…

Android thinker

国家虽安&#xff0c;忘战必危&#xff0c; Android虽爽&#xff0c;不学则忙&#xff0c;老夫纵横江湖数十载&#xff0c;深谙世事之难料&#xff0c;顾。。 ok&#xff0c;ok 不装比了&#xff0c;进入正题&#xff0c;今天要讲的是Android 热修之 thinker。 在研究这个之前…

ESP32-CAM AI THINKER 引脚排列:GPIO 用法说明

ESP32-CAM 是一款开发板,带有一个 ESP32-S 芯片、一个 OV2640 摄像头、microSD 卡插槽和几个用于连接外设的 GPIO。在本指南中,我们将介绍 ESP32-CAM GPIO 以及如何使用它们。 引脚排列图 下图显示了 ESP32-CAM AI-Thinker 的引脚排列图。 电路原理示意图 下图显示了 ESP…

Thinkpad 各系列简介

ThinkPad 各系列简介 如果提到商务笔记本&#xff0c;大家一定会想到凭借坚固和可靠的特性在业界享有很高声誉ThinkPad笔记本电脑&#xff0c;以及ThinkPad那经典的键盘红点设计和纯黑低调的外表。 在这里我就简单介绍一下ThinkPad的各系列产品。 Thinkpad名称来历 “ThinkP…

python thinker canvas create_arc 使用详解

版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。 本文链接&#xff1a;https://blog.csdn.net/A757291228/article/details/106739556 ———————————————— 版权声明&#xff1a;本文…

二、让界面动起来,Python基于thinker实现的简单的俄罗斯方块小游戏

文章目录 界面动起来定时刷新页面移动俄罗斯方块 界面动起来 **功能&#xff1a;**如何让方块「动起来」? 分析过程&#xff1a; 要想让方块动起来&#xff0c;需要实现以下两点&#xff1a; 定时刷新页面移动页面内部的俄罗斯方块 定时刷新页面 实现定时刷新页面&#xff…

三、生成随机方块,Python基于thinker实现的简单的俄罗斯方块小游戏

文章目录 生成第一步&#xff1a;随机生成方块对象第二步&#xff1a;修改游戏运行方法 固定第一步&#xff1a;记录固定的方块第二步&#xff1a;修改check_move方法 左右移动第一步&#xff1a;左右移动方块第二步&#xff1a;绑定键盘 旋转第一步&#xff1a;添加旋转方法第…

【G-thinker】G-thinker部分源码解析

一、main 子图匹配程序run.cpp中主要使用到worker.h和comper.h分别对应线程和进程单位&#xff0c;接下来我们从main函数入手解析源码 从主函数可以看到&#xff0c;子图匹配程序中GMatchWorker继承了worker&#xff0c;主函数声明了workerparams并且传入了路径和线程参数&am…

python用thinker库制作一个进制转换器(可打包exe)

进制类型分为&#xff1a; 二进制 字母B表示 八进制 字母O表示 十进制 字母D表示 十六机制 字母H表示 进制转换之间很麻烦&#xff0c;还得计算&#xff0c;如果可以做一个进制转换器多nice&#xff0c;其实也不难&#xff0c;就利用一个tkinter库就能制作&#xff0c;废话不多…

Thinker Board 2开发板上使用奥比中光 astra 深度相机

Thinker Board 2 国产开发板 arm架构 上使用奥比中光 astra 深度相机 准备工作 1、下载astraSDK 选择linux_arm 下载 https://developer.orbbec.com.cn/download.html?id53 2、下载openNI https://dl.orbbec3d.com/dist/openni2/OpenNI_2.3.0.66.zip 开始安装 1、安装fre…

think

我167&#xff0c;97斤&#xff0c;胸d&#xff0c;腰很细&#xff0c;反手轻松摸肚脐那种&#xff0c;骨架小&#xff0c;总结就是身高刚好人瘦胸大屁股大腿细腰细肤白。走路腰杆能挺到天上&#xff0c;气质挺好。漂亮女生太多了&#xff0c;想取胜只能从气质下手了。脸呢&…

Python基于thinker实现的简单的俄罗斯方块小游戏源代码:一、搭建界面

文章目录 一、搭建界面搭建基础窗体第一步&#xff1a;创建画板大小第二步&#xff1a;在画板上作画 绘制俄罗斯方块第一步&#xff1a;绘制o形俄罗斯方块第二步&#xff1a;绘制其他形状方块 记录俄罗斯方块小游戏的游戏思路和过程 一、搭建界面 **目标&#xff1a;**搭建基础…

thinker board s debian系统安装配置

thinker board s debian系统安装配置 好多年前买了一块thinker board s&#xff0c;装安卓系统来用&#xff0c;结果经常性卡死不知道为啥&#xff0c;可能是供电问题&#xff0c;也可能是系统bug&#xff0c;之后就吃灰不怎么用了&#xff0c;最近想装一个计算服务器&#xff…

python利用thinker制作多页面切换的桌面应用实例教程

本篇文章主要讲解&#xff0c;python利用thinker制作多页面的桌面实例教程。可以在一个主界面中相互进行切换和设置相关的表单信息&#xff0c;布局等方法 代码片段的实际效果 视频演示&#xff1a; python使用thinker制作多窗口代码片段实例效果 gif动态图&#xff1a; 构建…

模型思维-THE MODEL THINKER

序言这本书是怎样写成的 Part1为什么需要模型思维 01做一个多模型思考者 使用模型来思考能够带给你的&#xff0c;远远不仅仅是工作绩效的提高。它还会使你成为一个更优秀的人&#xff0c;让你拥有更强的思考能力。你将更擅长评估层出不穷的经济事件和政治事件&#xff0c;更能…