首页 文章 精选 留言 我的

精选列表

搜索[机器学习系统],共10000篇文章
优秀的个人博客,低调大师

从构建分布式秒杀系统聊聊WebSocket推送通知

前言 秒杀架构到后期,我们采用了消息队列的形式实现抢购逻辑,那么之前抛出过这样一个问题:消息队列异步处理完每个用户请求后,如何通知给相应用户秒杀成功? 场景映射 首先,我们举一个生活中比较常见的例子:我们去银行办理业务,一般会选择相关业务打印一个排号纸,然后就可以坐在小板凳上玩着手机,等待被小喇叭报号。当小喇叭喊到你所持有的号码,就可以拿着排号纸去柜台办理自己的业务。 这里,假设当我们取排号纸的时候,银行根据时间段内的排队情况,比较人性化的提示用户:排队人数较多,您是否继续等待?否的话我们可以换个时间段再来办理。 由此我们把生活场景映射到真实的秒杀业务逻辑中来: 我们可以把柜台比喻成商品下单处理逻辑单元 拿到排号纸说明你进入相应商品处理队列 拿到排号纸的请求直接返回前台,提示用户抢购进行中 排号纸进入队列后,等待商品业务处理逻辑 小喇叭叫到自己的排号相当于服务端通知用户秒杀成功,这时候可以进行支付逻辑 那些拿不到票号的同学,相当于队列已满直接返回秒杀失败 解决方案 通过上面的场景,我们很容易能够想到一种方案就是服务端通知,那么如何做到服务端异步通知的呢?下面,主角开始登场了,就是我们的Websocket。 WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。 特点: 异步、事件触发 可以发送文本,图片等流文件 数据格式比较轻量,性能开销小,通信高效 使用ws或者wss协议的客户端socket,能够实现真正意义上的推送功能 缺点: 部分浏览器不支持,浏览器支持的程度与方式有区别,需要各种兼容写法。 集成案例 由于我们的秒杀架构项目案例中使用了SpringBoot,因此集成webSocket也是相对比较简单的。 首先pom.xml引入以下依赖: <!-- webSocket 秒杀通知--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> WebSocketConfig 配置: /** * WebSocket配置 * 创建者 爪哇笔记 * 创建时间 2018年5月29日 */ @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } WebSocketServer 配置: @ServerEndpoint("/websocket/{userId}") @Component public class WebSocketServer { private final static Logger log = LoggerFactory.getLogger(WebSocketServer.class); //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 private static int onlineCount = 0; //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); //与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; //接收userId private String userId=""; /** * 连接建立成功调用的方法*/ @OnOpen public void onOpen(Session session,@PathParam("userId") String userId) { this.session = session; webSocketSet.add(this); //加入set中 addOnlineCount(); //在线数加1 log.info("有新窗口开始监听:"+userId+",当前在线人数为" + getOnlineCount()); this.userId=userId; try { sendMessage("连接成功"); } catch (IOException e) { log.error("websocket IO异常"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); //从set中删除 subOnlineCount(); //在线数减1 log.info("有一连接关闭!当前在线人数为" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { log.info("收到来自窗口"+userId+"的信息:"+message); //群发消息 for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("发生错误"); error.printStackTrace(); } /** * 实现服务器主动推送 */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * 群发自定义消息 * */ public static void sendInfo(String message,@PathParam("userId") String userId){ log.info("推送消息到窗口"+userId+",推送内容:"+message); for (WebSocketServer item : webSocketSet) { try { //这里可以设定只推送给这个userId的,为null则全部推送 if(userId==null) { item.sendMessage(message); }else if(item.userId.equals(userId)){ item.sendMessage(message); } } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } } KafkaConsumer 消费配置,通知用户是否秒杀成功: /** * 消费者 spring-kafka 2.0 + 依赖JDK8 * @author 科帮网 By https://blog.52itstyle.com */ @Component public class KafkaConsumer { @Autowired private ISeckillService seckillService; private static RedisUtil redisUtil = new RedisUtil(); /** * 监听seckill主题,有消息就读取 * @param message */ @KafkaListener(topics = {"seckill"}) public void receiveMessage(String message){ //收到通道的消息之后执行秒杀操作 String[] array = message.split(";"); if(redisUtil.getValue(array[0])!=null){//control层已经判断了,其实这里不需要再判断了 Result result = seckillService.startSeckil(Long.parseLong(array[0]), Long.parseLong(array[1])); if(result.equals(Result.ok())){ WebSocketServer.sendInfo(array[0].toString(), "秒杀成功");//推送给前台 }else{ WebSocketServer.sendInfo(array[0].toString(), "秒杀失败");//推送给前台 redisUtil.cacheValue(array[0], "ok");//秒杀结束 } }else{ WebSocketServer.sendInfo(array[0].toString(), "秒杀失败");//推送给前台 } } } webSocket.js 前台通知逻辑: $(function(){ socket.init(); }); var basePath = "ws://localhost:8080/seckill/"; socket = { webSocket : "", init : function() { //userId:自行追加 if ('WebSocket' in window) { webSocket = new WebSocket(basePath+'websocket/1'); } else if ('MozWebSocket' in window) { webSocket = new MozWebSocket(basePath+"websocket/1"); } else { webSocket = new SockJS(basePath+"sockjs/websocket"); } webSocket.onerror = function(event) { alert("websockt连接发生错误,请刷新页面重试!") }; webSocket.onopen = function(event) { }; webSocket.onmessage = function(event) { var message = event.data; alert(message)//判断秒杀是否成功、自行处理逻辑 }; } } 客户端API 客户端与服务器通信 send() 向远程服务器发送数据 close() 关闭该websocket链接 监听函数 onopen 当网络连接建立时触发该事件 onerror 当网络发生错误时触发该事件 onclose 当websocket被关闭时触发该事件 onmessage 当websocket接收到服务器发来的消息的时触发的事件,也是通信中最重要的一个监听事件。msg.data readyState属性 这个属性可以返回websocket所处的状态。 CONNECTING(0) websocket正尝试与服务器建立连接 OPEN(1) websocket与服务器已经建立连接 CLOSING(2) websocket正在关闭与服务器的连接 CLOSED(3) websocket已经关闭了与服务器的连接 开源方案 goeasy GoEasy实时Web推送,支持后台推送和前台推送两种:后台推送可以选择Java SDK、 Restful API支持所有开发语言;前台推送:JS推送。无论选择哪种方式推送代码都十分简单(10分钟可搞定)。由于它支持websocket 和polling两种连接方式所以兼顾大多数主流浏览器,低版本的IE浏览器也是支持的。 地址:http://goeasy.io/ Pushlets Pushlets 是通过长连接方式实现“推”消息的。推送模式分为:Poll(轮询)、Pull(拉)。 地址:http://www.pushlets.com/ Pushlet Pushlet 是一个开源的 Comet 框架,Pushlet 使用了观察者模型:客户端发送请求,订阅感兴趣的事件;服务器端为每个客户端分配一个会话 ID 作为标记,事件源会把新产生的事件以多播的方式发送到订阅者的事件队列里。 地址:https://github.com/wjw465150/Pushlet 总结 其实前面有提过,尽管WebSocket有诸多优点,但是,如果服务端维护很多长连接也是挺耗费资源的,服务器集群以及览器或者客户端兼容性问题,也会带来了一些不确定性因素。大体了解了一下各大厂的做法,大多数都还是基于轮询的方式实现的,比如:腾讯PC端微信扫码登录、京东商城支付成功通知等等。 有些小伙伴可能会问了,轮询岂不是会更耗费资源?其实在我看来,有些轮询是不可能穿透到后端数据库查询服务的,比如秒杀,一个缓存标记位就可以判定是否秒杀成功。相对于WS的长连接以及其不确定因素,在秒杀场景下,轮询还是相对比较合适的。 思考 最后,思考一个问题:100件商品,假如有一万人进行抢购,该如何设置队列长度? 秒杀案例:https://gitee.com/52itstyle/spring-boot-seckill 参考 https://blog.52itstyle.com/archives/736/ https://www.xoriant.com/blog/mobility/websocket-web-stateful-now.html

优秀的个人博客,低调大师

Confluence 6 的系统配置信息的示例

awt.toolkit sun.awt.X11.XToolkit file.encoding.pkg sun.io java.specification.version 1.8 sun.cpu.isalist sun.jnu.encoding ANSI_X3.4-1968 com.sun.jndi.ldap.connect.pool.maxsize 0 java.class.path /opt/atlassian/confluence/bin/bootstrap.jar/opt/atlassian/confluence/bin/tomcat-juli.jar java.vm.vendor Oracle Corporation plugin.webresource.javascript.try.catch.wrapping true sun.arch.data.model 64 sun.font.fontmanager sun.awt.X11FontManager catalina.useNaming true java.vendor.url http://java.oracle.com/ user.timezone America/New_York os.name Linux java.vm.specification.version 1.8 sun.java.launcher SUN_STANDARD user.country US atlassian.org.osgi.framework.bootdelegation.extra org.apache.lucene.* sun.boot.library.path /opt/atlassian/confluence/jre/lib/amd64 sun.java.command org.apache.catalina.startup.Bootstrap start sun.cpu.endian little user.home /home/confluence user.language en java.specification.vendor Oracle Corporation java.naming.factory.url.pkgs org.apache.naming java.home /opt/atlassian/confluence/jre ignore.endorsed.dirs atlassian.plugins.startup.options com.sun.jndi.ldap.connect.pool.protocol plain ssl file.separator / com.sun.jndi.ldap.connect.pool.initsize 1 atlassian.plugins.enable.wait 300 line.separator java.vm.specification.vendor Oracle Corporation java.specification.name Java Platform API Specification java.awt.graphicsenv sun.awt.X11GraphicsEnvironment java.awt.headless true package.access sun.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.tomcat. package.definition sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.naming.,org.apache.tomcat. sun.boot.class.path /opt/atlassian/confluence/jre/lib/resources.jar/opt/atlassian/confluence/jre/lib/rt.jar/opt/atlassian/confluence/jre/lib/sunrsasign.jar/opt/atlassian/confluence/jre/lib/jsse.jar/opt/atlassian/confluence/jre/lib/jce.jar/opt/atlassian/confluence/jre/lib/charsets.jar/opt/atlassian/confluence/jre/lib/jfr.jar/opt/atlassian/confluence/jre/classes jdk.tls.ephemeralDHKeySize 2048 server.loader java.util.logging.config.file /opt/atlassian/confluence/conf/logging.properties java.protocol.handler.pkgs org.apache.catalina.webresources sun.management.compiler HotSpot 64-Bit Tiered Compilers com.sun.jndi.ldap.connect.pool.authentication simple com.sun.jndi.ldap.connect.pool.prefsize 10 java.runtime.version 1.8.0_162-b12 java.naming.factory.initial org.apache.naming.java.javaURLContextFactory user.name confluence path.separator : common.loader "${catalina.base}/lib""${catalina.base}/lib/*.jar""${catalina.home}/lib""${catalina.home}/lib/*.jar" os.version 3.14.32-xxxx-grs-ipv6-64 java.endorsed.dirs /opt/atlassian/confluence/jre/lib/endorsed java.runtime.name Java(TM) SE Runtime Environment file.encoding ANSI_X3.4-1968 org.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE 32768 confluence.context.path java.vm.name Java HotSpot(TM) 64-Bit Server VM synchrony.enable.xhr.fallback true java.vendor.url.bug http://bugreport.sun.com/bugreport/ java.io.tmpdir /opt/atlassian/confluence/temp tomcat.util.scan.StandardJarScanFilter.jarsToScan log4j-web*.jar,log4j-taglib*.jar,log4javascript*.jar,slf4j-taglib*.jar catalina.home /opt/atlassian/confluence com.sun.jndi.ldap.connect.pool.timeout 30000 java.version 1.8.0_162 atlassian.enable.spring.strong.cache.bean.metadata.flush true tomcat.util.scan.StandardJarScanFilter.jarsToSkip bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,websocket-api.jar,catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-storeconfig.jar,catalina-tribes.jar,jasper.jar,jasper-el.jar,ecj-*.jar,tomcat-api.jar,tomcat-util.jar,tomcat-util-scan.jar,tomcat-coyote.jar,tomcat-dbcp.jar,tomcat-jni.jar,tomcat-websocket.jar,tomcat-i18n-en.jar,tomcat-i18n-es.jar,tomcat-i18n-fr.jar,tomcat-i18n-ja.jar,tomcat-juli-adapters.jar,catalina-jmx-remote.jar,catalina-ws.jar,tomcat-jdbc.jar,tools.jar,commons-beanutils*.jar,commons-codec*.jar,commons-collections*.jar,commons-dbcp*.jar,commons-digester*.jar,commons-fileupload*.jar,commons-httpclient*.jar,commons-io*.jar,commons-lang*.jar,commons-logging*.jar,commons-math*.jar,commons-pool*.jar,jstl.jar,taglibs-standard-spec-*.jar,geronimo-spec-jaxrpc*.jar,wsdl4j*.jar,ant.jar,ant-junit*.jar,aspectj*.jar,jmx.jar,h2*.jar,hibernate*.jar,httpclient*.jar,jmx-tools.jar,jta*.jar,log4j*.jar,mail*.jar,slf4j*.jar,xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,junit.jar,junit-*.jar,ant-launcher.jar,cobertura-*.jar,asm-*.jar,dom4j-*.jar,icu4j-*.jar,jaxen-*.jar,jdom-*.jar,jetty-*.jar,oro-*.jar,servlet-api-*.jar,tagsoup-*.jar,xmlParserAPIs-*.jar,xom-*.jar user.dir /opt/atlassian/confluence/bin atlassian.enable.spring.strong.cache.bean.metadata true os.arch amd64 java.vm.specification.name Java Virtual Machine Specification java.awt.printerjob sun.print.PSPrinterJob sun.os.patch.level unknown catalina.base /opt/atlassian/confluence shared.loader java.util.logging.manager org.apache.juli.ClassLoaderLogManager java.library.path /usr/java/packages/lib/amd64/usr/lib64/lib64/lib/usr/lib java.vendor Oracle Corporation java.vm.info mixed mode java.vm.version 25.162-b12 hibernate.bytecode.use_reflection_optimizer true sun.java2d.opengl true sun.io.unicode.encoding UnicodeLittle java.ext.dirs /opt/atlassian/confluence/jre/lib/ext/usr/java/packages/lib/ext tomcat.util.buf.StringCache.byte.enabled true java.class.version 52.0 https://www.cwiki.us/display/CONF6ZH/Viewing+Site+Statistics

优秀的个人博客,低调大师

CentOS 7系统,Docker想启用userns-remap,傻了吧?

今天想启用这个功能, 这样,docker里的root用户,就不用是宿主机的root用户, 安全性可以提高一个层次。 于是找了文档,对比了配置,default用了,自定义用了, 结果呢? 傻眼了~~~~全都失败。 ~~~~~~~~~~~~~~~~~~~~ 花了几小时,再想找找为何失败, 结果,原来在centos/rhel中, user namespaces只是一个预览功能。 要用命令才能开启!!!! OMG,不知敢不敢用了。 http://cache.baiducontent.com/c?m=9d78d513d9951cfe01bad4690d6790274e1497624c8b91027ea48448e5735a310731bce8653657448dca262147ed084beb832b6f675d7de28cc8ff1b9cedce3f2fff7b633601d31453985eaaca44389260d601b8f14efaeca774c0f58c92c25158ce50067f87f3&p=c27ec54ad5c516f30be296605055&newp=8b2a970e8ed505fd11bd9b7d0b4092695803ed633fd6d301298ffe0cc4241a1a1a3aecbf2025110fd7c2786703a44b58ecf03273330834f1f689df08d2ecce7e64d031702255&user=baidu&fm=sc&query=docker+userns-remap&qid=a68e3d2a000023c9&p1=11 https://segmentfault.com/q/1010000010909708 https://bugzilla.redhat.com/show_bug.cgi?id=1349704 ======================== grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)" reboot If you're using CentOS 7, there are two things you need to know. 1.UserNamespaces is not enabled on the kernel by default. You can enable it by executing the following command and restart the system. sudo grubby --args="user_namespace.enable=1" \ --update-kernel=/boot/vmlinuz-3.10.0-XXX.XX.X.el7.x86_64 The user namespaces are "Technology Preview" and disabled by default, see kernel implementation https://bugzilla.redhat.com/show_bug.cgi?id=1138782 If I good remember you need to enable it on kernel command line (something like grubby --args=user_namespace.enable=1 + reboot).

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册