栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring websocket集群

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring websocket集群

做项目使用websocket网上的方案很多,但是如果是产品或者平台,采用的是微服务架构,而每个微服务都可能有异步消息处理,想采用websocket,对于前端处理就会产生一个问题,那就是前端vue是SPA应用,它与后台建立一个websocket连接,如果每个微服务都建立一个连接,前端代码岂不是非常复杂。
从下图可以看到前端应用只需要跟消息微服务建立websocket连接即可,后台业务逻辑处理,调用消息服务提供的dubbo接口,再通过消息服务将响应结果推送给前端应用,整个流程就完整了。

接下只需要考虑消息微服务的集群了。

1 spring websocket
代码参考websocket-springboot-starter
websocket-demo

@Slf4j
@Component
@ServerEndpoint("/busi/{source}/{identifier}")
public class WebSocketEndpoint extends AbstractWebSocketEndpoint  {

    private static WebSocketManager webSocketManager;

    @Autowired
    public void setWebSocketManager(WebSocketManager webSocketManager) {
        WebSocketEndpoint.webSocketManager = webSocketManager;
    }

    @OnOpen
    public void onOpen(Session session,@PathParam("source") String source, @PathParam(value="identifier")String identifier ){
        log.info("用户连接成功,连接来源为{},连接用户为:{}",source, identifier);
        connect(session, source, identifier);
    }

    @OnMessage
    public void onMessage(String message,Session session,  @PathParam(value="identifier")String identifier ){
        log.info("接受到的消息是{}",message);
        recieveMessage(message, session,identifier );
    }

    @OnClose
    public void onClose(Session session, @PathParam(value="identifier")String identifier ){
        log.info("用户断开连接");
        disconnect(session, identifier);
    }

    @OnError
    public void onError(Session session,Throwable t, @PathParam("identifier") String identifier){
        log.info("发生异常:, identifier = " + identifier);
        log.error(t.getMessage() , t);
        disconnect(session, identifier);
    }

    @Override
    public WebSocketManager getWebSocketManager() {
        return WebSocketEndpoint.webSocketManager ;
    }

}

这里重点关注的是applicationContext的赋值

@Configuration
public class MemoryWebSocketManagerConfig implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Bean(WebSocketManager.WEBSOCKET_MANAGER_NAME)
    public WebSocketManager webSocketManager() {
        return new MemoryWebsocketManager(this.applicationContext);
    }


}
@Slf4j
@RestController
@RequestMapping("imBusi")
public class ImBusiController {

    @Autowired
    private WebSocketManager webSocketManager;

    @GetMapping("call/{id}")
    public String call(@PathVariable("id") String id){
        WebSocketConn webSocketConn = webSocketManager.get(id);
        WebsocketUtil.sendText(webSocketConn.getSession(), "hi," +id);
        return "ok";
    }
}

@Data
public class WebSocketConn {

    
    private Session session;

    
    private WebSocketSource source;

    
    private String id;
}

2 gateway websocket
spring gateway自身就带了WebsocketRoutingFilter,网上有sockjs方案,但我的工程并不需要做什么

路由增加配置,这里看到uri只需要调整下增加ws协议前缀就可以了,gateway本身不用做什么

{
    "id": "im-server",
    "predicates": [{
        "args": {
            "pattern": "/api/im/**"
        },
        "name": "Path"
    }],
	"filters": [{
		"name": "StripPrefix",
		"args": {
            "_genkey_0": "2"
        }
	}],
    "uri": "lb:ws://im-server"
}

3 jmeter验证
看到别人写的jmx脚本,自己不亲自动手写,感觉还是差很多,连jmeter的菜单都熟练。掌握jmeter并不是为了做测试,而是会让你更有全局思维。
jmeter之websocket压测
这个方案比较简单JMeterWebSocketSamplers-1.2.8.jar放到/lib/ext目录下

WebSocket协议插件
jmeter 04jmeter做websocket协议的接口测试,这个有些复杂
jmeter下载配置压测

执行任务前,设置动态参数

int size_full_thread = ${size_full_thread};
String phonenum_prefix = "${phonenum_prefix}"; 

String thead = String.valueOf(${__threadNum});
int size_thead = thead.length();

String pading_thead = "";
for(i = 0; i< size_full_thread - size_thead; ++i) {
	pading_thead = "0" + pading_thead;
	}
thead = pading_thead + thead;


String username = phonenum_prefix + thead;
vars.put("username", username);
log.info("###############################username#############################:" + username);

3 jprofiler性能监控
3.1 单例
从jprofiler中可以看到MemoryWebsocketManager、ImBusiController、MemoryWebSocketManagerConfig均是单例

执行jcmd 19060 GC.run,多例的实例从内存中回收掉了,但单例还存在。

线程安全问题:

  • 静态常量
    常量是只读的,线程安全
  • 局部变量
    方法的参数变量和方法内变量不是共享资源,局部变量是线程安全的。局部变量存于栈内存,方法执行完毕后,释放掉内存。
    JVM的栈内存
  • 成员变量
    • 类变量
      类中通过static修饰的静态变量,类变量是所有实例共一个,是线程不安全的,可以通过final修饰,在一定程度上保障了线程安全。final 静态变量和线程安全,比如在配置加载的时候给类变量做一次赋值,同时防止其他人在其他地方误操作改变了它的值,那么定义为final static变量即可。
    • 实例变量
      多例情况下对象与对象之间的实例变量修改互不影响,是线程安全的,@ServerEndpoint中websocket即是多例;但是单例模式下,因实例变量共享一个实例变量,故而存在线程安全的问题。spring默认都是单例模式,线程安全如何保障的呢?
      如何看待Spring下单例模式与线程安全的矛盾,spring单例模式下肯定有线程安全的问题,只要涉及成员变量都存在这个问题。对我们接触的Controller层,如果只有读,那么也不会有线程安全的问题。
      高并发的情况下,多线程由tomcat或者netty等容器管理,多线程可以访问同一个实例对象,进入到方法区,因为方法的局部变量又是线程安全的,故而不用担心此问题,只有没有设置共有的成员属性即可。
      3.2 多例
      从上图可以看到WebSocketEndpoint、WebSocketConn这些是多例,websocket是线程安全,采用的是多例模式。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/389681.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号