文档章节

基于xmpp openfire smack开发之Android消息推送技术原理分析和实践[4]

wangjie142
 wangjie142
发布于 2015/02/22 14:34
字数 3181
阅读 55
收藏 1
点赞 1
评论 0

顺便也一起回顾下xmpp的历程

xmpp协议起源于著名的Linux即时通讯服务服务器jabber,有时候我们会把xmpp协议也叫jabber协议,其实这是不规范的,xmpp是个协议,而jabber是个服务器,因为jabber开源,设计精良,安全,稳定,跨语言,跨平台,封装开发简便,越来越多人开始使用它,并且逐步完善,不久它便形成了一个强大的标准化体系,Google GTalk、Pidgin、PSI、Spark、Pandion、MSN、Yahoo、ICQ..诸如此类一些软件在这个强大的标准体系下实现了互联.那么XMPP到底是什么意思,用通俗的话讲它和基于xml格式的一些协议原理差不多,只不过是个针对服务器的软件协议罢了。

那么在java领域是否存在一个类似jabber那么强大开源稳定的也完美支持xmpp协议的服务器呢?答案有的,那便是openfire,openfire是纯java开发的基于XMPP的协议,目前最终版本锁定在了2011年openfire 3.7,它一共有linux windows mac 三个版本,安装也非常简单,openfire这个服务器是个开放式的平台,它内部集成的服务包括即时通讯服务,会议室服务,用户安全验证和管理服务,搜索服务,组织机构服务,会话服务,这几大服务都有相应的管理类和对外接口,它的二次开发和扩展都是在插件基础上直接嫁接进去的,早期有很多第三方为他做了插件,有语音服务,red5视频服务,邮件服务等等,语音和视频在openfire上一直是个鸡肋,没有非常好的解决方案,而做这些插件的大部分都停止更新,大家如果选用openfire做视频和语音还要慎重!抛开这些插件,openfire在IM及时通讯上还是相当强大稳定的,不少公司拿它来做二次开发!但即便如此openfire的二次开发成本还是比较高昂的,笔者曾经成功费了九牛二虎之力将源码环境搭建起来,并成功将它与我们JAVAEE 经典架构SSH成功组装,用openfire的桌面客户端spark软件和android开源xmpp客户端Beam软件,web端聊天软件Claros Chat享受了一把在自己服务器上“随时随地聊天”,不过这些都是实验阶段,距离成熟可用还很远!研究技术可以这么勾兑尝试,真的给人用可不能这么随意,我们还是要挖掘真正对我们有用的价值!

openfire过于庞大繁复,许多对我们来说都是没什么用的,甚至要砍掉改造,能不能有精简的xmpp服务器呢?答案是有的,androidpn,笔者认真比对过openfire和androidpn的源码,最后惊奇的发现,原来它就是从openfire里面庖丁解牛出来的一部分,做这件事的人非常的了不起,为我们省了很大力气,在此感谢他的开源和共享精神,那么androidpn分离出来的是消息推送服务,简言之就是从服务端向android客户端推送消息的服务,因为openfire的源码架构是在jetty基础上建立的,它的启动和部署方式和我们传统的服务器tomcat和weblogic等有点区别,所以androidpn也有jetty的影子,在和我们传统架构组合的时候还要再把它和jetty拆开, androidpn的搭建和使用网上的教程很多,大家可以发现大部分千篇一律,出现一个OK界面就没了,堂而皇之的写上原创,有的只是改了下hello world,如此糊弄,实在难为所用!

androidpn消息推送采用的是apache的mina框架做的,服务端和客户端两边都有监听,也就是我们所说的socket编程,有人说socket编程有什么难的,就那么回事,其实不然,我们平时写的socket聊天都只是在局域网的,但是要穿透路由和防火墙,让信息安全及时的传送到另一个网关的局域网电脑中,就不是一件简单的活了,其中涉及到在nat上打洞,还有线程,断网重连,安全加密等等,那么androidpn配合mina相当于把这些活都干了,那么我们要的干活就相对比较精细了,第一学习mina的安装配置的规则,第二学习xmpp协议组装和解析的规则,第三学习androidpn推和收消息的核心代码,如此三点我们便能灵活驾驭住androidpn出现再大的问题自己也能动手去调了。

   

在和spring整合的时候大家要注意不要让mina服务启动2次,笔者整合时候无意发现在linux64位系统,weblogic上启动时候总是报5222已经被占用,反复查看代码发现mina在随web容器启动过一次5222端口后,xmppserver类中的start方法中ClassPathXmlApplicationContext类又加载了一次spring配置,导致端口被重复开启两次,最后终于发现问题所在:

[html] view plaincopy

  1. <?xml version="1.0" encoding="UTF-8"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  

  4.     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"  

  5.     xmlns:util="http://www.springframework.org/schema/util"  

  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  

  7.         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  

  8.         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  

  9.         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd  

  10.         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">  

  11.     <context:component-scan base-package="org.androidpn.server.*" /><!-- 自动装配 -->    

  12.   

  13.     <!-- =============================================================== -->  

  14.     <!-- Resources                                                       -->  

  15.     <!-- =============================================================== -->  

  16.     <bean id="propertyConfigurer"  

  17.         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  

  18.         <property name="locations">  

  19.             <list>  

  20.                 <value>classpath:jdbc.properties</value>  

  21.             </list>  

  22.         </property>  

  23.     </bean>  

  24.   

  25.     <!-- =============================================================== -->  

  26.     <!-- Data Source                                                     -->  

  27.     <!-- =============================================================== -->  

  28.   

  29.     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"  

  30.         destroy-method="close">  

  31.         <property name="driverClassName" value="${jdbcDriverClassName}" />  

  32.         <property name="url" value="${jdbcUrl}" />  

  33.         <property name="username" value="${jdbcUsername}" />  

  34.         <property name="password" value="${jdbcPassword}" />  

  35.         <property name="maxActive" value="${jdbcMaxActive}" />  

  36.         <property name="maxIdle" value="${jdbcMaxIdle}" />  

  37.         <property name="maxWait" value="${jdbcMaxWait}" />  

  38.         <property name="defaultAutoCommit" value="true" />  

  39.     </bean>   

  40.       

  41.     <!-- sessionFactory -->  

  42.     <bean id="sessionFactory"  

  43.         class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  

  44.         <property name="dataSource" ref="dataSource" />  

  45.         <property name="configLocation" value="classpath:hibernate.cfg.xml" />  

  46.     </bean>  

  47.   

  48.     <!-- 配置事务管理器 -->  

  49.     <bean id="txManager"  

  50.         class="org.springframework.orm.hibernate3.HibernateTransactionManager">  

  51.         <property name="sessionFactory" ref="sessionFactory" />  

  52.         <property name="dataSource" ref="dataSource" />  

  53.     </bean>  

  54.       

  55.     <!-- 采用注解来管理事务-->  

  56.     <tx:annotation-driven transaction-manager="txManager" />   

  57.       

  58.     <!-- spring hibernate工具类模板 -->  

  59.     <bean id="hibernateTemplate"  

  60.         class="org.springframework.orm.hibernate3.HibernateTemplate">  

  61.         <property name="sessionFactory" ref="sessionFactory"></property>  

  62.     </bean>  

  63.     <!-- spring jdbc 工具类模板 -->  

  64.     <bean id="jdbcTemplate"  

  65.         class="org.springframework.jdbc.core.JdbcTemplate">  

  66.         <property name="dataSource">  

  67.             <ref bean="dataSource" />  

  68.         </property>  

  69.     </bean>      

  70.       

  71.     <!-- =============================================================== -->  

  72.     <!-- SSL                                                             -->  

  73.     <!-- =============================================================== -->  

  74.   

  75.     <!--  

  76.     <bean id="tlsContextFactory"  

  77.         class="org.androidpn.server.ssl2.ResourceBasedTLSContextFactory">  

  78.         <constructor-arg value="classpath:bogus_mina_tls.cert" />  

  79.         <property name="password" value="boguspw" />  

  80.         <property name="trustManagerFactory">  

  81.             <bean class="org.androidpn.server.ssl2.BogusTrustManagerFactory" />  

  82.         </property>  

  83.     </bean>  

  84.     -->  

  85.     <!-- MINA  -->   

  86.     <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">  

  87.         <property name="customEditors">  

  88.             <map>  

  89.                 <entry key="java.net.SocketAddress">  

  90.                     <bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" />  

  91.                 </entry>  

  92.             </map>  

  93.         </property>  

  94.     </bean>  

  95.   

  96.     <bean id="xmppHandler" class="org.androidpn.server.xmpp.net.XmppIoHandler" />  

  97.   

  98.     <bean id="filterChainBuilder"  

  99.         class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">  

  100.         <property name="filters">  

  101.             <map>  

  102.                 <entry key="executor">  

  103.                     <bean class="org.apache.mina.filter.executor.ExecutorFilter" />  

  104.                 </entry>  

  105.                 <entry key="codec">  

  106.                     <bean class="org.apache.mina.filter.codec.ProtocolCodecFilter">  

  107.                         <constructor-arg>  

  108.                             <bean class="org.androidpn.server.xmpp.codec.XmppCodecFactory" />  

  109.                         </constructor-arg>  

  110.                     </bean>  

  111.                 </entry>  

  112.                 <!--  

  113.                 <entry key="logging">  

  114.                     <bean class="org.apache.mina.filter.logging.LoggingFilter" />  

  115.                 </entry>  

  116.                 -->  

  117.             </map>  

  118.         </property>  

  119.     </bean>  

  120.   

  121.     <bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor"  

  122.         init-method="bind" destroy-method="unbind" scope="singleton">  

  123.         <property name="defaultLocalAddress" value=":5222" />  

  124.         <property name="handler" ref="xmppHandler" />  

  125.         <property name="filterChainBuilder" ref="filterChainBuilder" />  

  126.         <property name="reuseAddress" value="true" />  

  127.     </bean>  

  128.        

  129.       

  130.     <bean id="serviceLocator" class="org.androidpn.server.service.ServiceLocator" scope="singleton" />   

  131.   

  132.       

  133.     <!-- Services-->   

  134.       

  135.     <bean id="userService" class="org.androidpn.server.service.impl.UserServiceImpl"/>  

  136.       

  137.     <bean id="notificationService" class="org.androidpn.server.service.impl.NotificationServiceImpl"/>  

  138.        

  139. </beans>  

配置serviceLocator是为了保证spring容器只能由一个上下文,也就是spring容器只被启动一次,我们将BeanFactory交给了serviceLocator,这样一来有什么好处呢?

控制层,服务层,数据库操作层都受spring管理,在他们中去跟spring要资源,一定是要什么有什么想怎么拿就怎么拿,都很方便,但是如果想在没有被spring所管理的类中去拿spring的资源,动作就不那么优雅了,有人建议用ClassPath加载器初始化spring工厂来获取资源,问题就处在这里,这种做法必定会产生2个spring上下文,一个是web容器所启动的,一个是java类加载器所启动的,我们的MINA服务器也就被启动了2次,其实资源被重复多次实例化除了影响性能外,对程序影响可能并不大,但是MINA被启动2次,肯定会出问题的。为保证spring只有一个上下文,我们将容器上下文交给了serviceLocator,脱离spring管控的环境可以面向serviceLocator来调度spring中的资源操作MINA服务器。

[java] view plaincopy

  1. package org.androidpn.server.service;  

  2.   

  3. import org.springframework.beans.BeansException;  

  4. import org.springframework.beans.factory.BeanFactory;  

  5. import org.springframework.beans.factory.BeanFactoryAware;  

  6.   

  7.    

  8. public class ServiceLocator implements BeanFactoryAware {  

  9.     private static BeanFactory beanFactory = null;  

  10.   

  11.     private static ServiceLocator servlocator = null;  

  12.   

  13.     public static String USER_SERVICE = "userService";  

  14.   

  15.     public static String NOTIFICATION_SERVICE = "notificationService";  

  16.   

  17.     public void setBeanFactory(BeanFactory factory) throws BeansException {  

  18.     this.beanFactory = factory;  

  19.     }  

  20.   

  21.     public BeanFactory getBeanFactory() {  

  22.     return beanFactory;  

  23.     }  

  24.   

  25.     public static ServiceLocator getInstance() {  

  26.     if (servlocator == null)  

  27.         servlocator = (ServiceLocator) beanFactory.getBean("serviceLocator");  

  28.     return servlocator;  

  29.     }  

  30.   

  31.     /** 

  32.      * 根据提供的bean名称得到相应的服务类 

  33.      *  

  34.      * @param servName 

  35.      *            bean名称 

  36.      */  

  37.     public static Object getService(String servName) {  

  38.     return beanFactory.getBean(servName);  

  39.     }  

  40.   

  41.     /** 

  42.      * 根据提供的bean名称得到对应于指定类型的服务类 

  43.      *  

  44.      * @param servName 

  45.      *            bean名称 

  46.      * @param clazz 

  47.      *            返回的bean类型,若类型不匹配,将抛出异常 

  48.      */  

  49.     public static Object getService(String servName, Class clazz) {  

  50.     return beanFactory.getBean(servName, clazz);  

  51.     }  

  52.   

  53.     /** 

  54.      * Obtains the user service. 

  55.      *  

  56.      * @return the user service 

  57.      */  

  58.     public static UserService getUserService() {  

  59.     return (UserService) getService(USER_SERVICE);  

  60.     }  

  61.   

  62.     public static NotificationService getNotificationService() {  

  63.     return (NotificationService) getService(NOTIFICATION_SERVICE);  

  64.     }  

  65. }  

在config.properties中还要特别注意xmpp.resourceName必须跟客户端中XmppManager的private static final String XMPP_RESOURCE_NAME = "AndroidpnClient";保持一致,否则连不上服务器,还xmpp.session.maxInactiveInterval=-1表示永不中断,如果设定了时间超过这个时间范围没有任何活动就会自动断开,这里的时间单位全部是毫秒。

[plain] view plaincopy

  1. apiKey=1234567890  

  2. xmpp.ssl.storeType=JKS  

  3. xmpp.ssl.keystore=conf/security/keystore  

  4. xmpp.ssl.keypass=changeit  

  5. xmpp.ssl.truststore=conf/security/truststore  

  6. xmpp.ssl.trustpass=changeit  

  7. xmpp.resourceName=AndroidpnClient  

  8.   

  9. ##Added by ken  

  10. username=admin  

  11. password=admin  

  12.   

  13. #资源名称  

  14. resource_name=AndroidpnClient  

  15.   

  16. #校验超时时间间隔  

  17. xmpp.session.checkTimeoutInterval=10000  

  18.   

  19. #Session timeout最大非活动时间间隔  

  20. xmpp.session.maxInactiveInterval=1000000  

在androidpn.properties中端口和IP不要写错,有人喜欢写localhost,在手机上是无法识别的,必须写绝对IP地址。

apiey=1234567890

xmppHost=192.168.1.78

xmppPort=5222

 

运行结果如下:


离线消息也支持,先给离线用户发个消息,效果如下:

在数据库中我们看到有一条离线消息是发给用户4aa50dde313f4b63907c2430bf00b413,status为0标记为离线

这时我们再上线,大约等待20秒左右,查看系统控制台打印:

查看android端看看用户4aa50dde313f4b63907c2430bf00b413上线情况:

这时候数据库记录发生了变化,status变成了2,表示已经接收,用户点击OK的时候,它又变成了3表示已经查看

离线消息的原理相对比较简单,当系统给指定用户发送消息时候,会首先判断用户是够在线,如果在线就直接发送,如果没有在线就暂时标记保存,等用户上线时候先查离线消息然后弹出,其实整个项目都是开源的,可能唯一的难点就是对MINA和XMPP协议的不了解,再加上本身对socket和多线程的畏惧,如果这些全部都掌握,驾驭好这套源码还是很有信心的,了解其基本原理以后,我们就可以放心的做更多的扩展。

         网上现在也有不少androidpn版本,五花八门什么都有,里面到底有没问题,改了什么没改什么都不知道,基本上已经追溯不到原创到底是谁了,索性就只能从国外的一个网站上下了一个比较可靠的版本自己动手去量身改造,终于出了一个比较稳定版本。对于消息提醒来说,它仅仅是个notification,许多人非要把业务数据也做进去,更有夸张好几兆的xml数据就这么硬塞提醒过去,这种做法本身就背离了设计的初衷,非要把跑车当牛车使能不出问题吗?其实业务数据还是用http拉比较好,xmpp及时的前提是用资源消耗作为代价的,我们能适度就适度用,用好用稳就行!






项目源码下载: Androidpn威力加强版(4月17日更新)





搭建步骤:
1.android端找到res/raw/androidpn.properties文件修改服务器ip地址,不要写localhost,写绝对ip地址
2.服务端找到resources/jdbc.properties 在mysql中新建一个数据库apn,并将连接指向该库,设置用户名和密码,库表会随服务启动的时候自动创建
3.先启动服务,再打开android客户端,点击连接即可



参阅文献
Openfirehttp://www.igniterealtime.org/
push-notificationhttp://www.push-notification.org/
Claros chathttp://www.claros.org/
androidpnsourceforgehttp://sourceforge.net/projects/androidpn/
android消息推送解决方案http://www.cnblogs.com/hanyonglu/archive/2012/03/04/2378971.html
xmpp协议实现原理介绍 http://www.cnblogs.com/hanyonglu/archive/2012/03/04/2378956.html


本文转载自:http://blog.csdn.net/shimiso/article/details/8156439

共有 人打赏支持
wangjie142
粉丝 5
博文 12
码字总数 7731
作品 0
无锡
程序员
腾讯技术分享:Android版手机QQ的缓存监控与优化实践

本文内容整理自公众号腾讯Bugly,感谢原作者的分享。 1、问题背景 对于Android应用来说,内存向来是比较重要的性能指标。内存占用过高,会影响应用的流畅度,甚至引发OOM,非常影响用户体验。...

JackJiang2011 ⋅ 04/08 ⋅ 0

京东京麦商家开放平台的消息推送架构演进之路

本文来自京东商城京麦平台组开发工程师曹德然的技术分享,感谢作者。 1、前言 京麦实时消息推送是京东的京麦商家开放平台的核心组成部分。从消息源到消息中心再到触达用户,以及最终根据消息...

JackJiang2011 ⋅ 01/10 ⋅ 0

Netty干货分享:京东京麦的生产级TCP网关技术实践总结

1、引言 京东的京麦商家后台2014年构建网关,从HTTP网关发展到TCP网关。在2016年重构完成基于Netty4.x+Protobuf3.x实现对接PC和App上下行通信的高可用、高性能、高稳定的TCP长连接网关。 早期...

JackJiang2011 ⋅ 2017/12/01 ⋅ 0

移动端IM中大规模群消息的推送如何保证效率、实时性?

本文原题为“大规模群消息推送如何保证实时性?”,来自瓜子二手车IM负责人:封宇,本次内容有修订,感谢原作者(原文链接在文末)。 1、编者注 众所周之,群聊是移动端IM的服务端技术难点所...

JackJiang2011 ⋅ 2017/11/20 ⋅ 0

移动端IM中大规模群消息的推送如何保证效率、实时性?

本文原题为“大规模群消息推送如何保证实时性?”,来自瓜子二手车IM负责人:封宇,本次内容有修订,感谢原作者(原文链接在文末)。 1、编者注 众所周之,群聊是移动端IM的服务端技术难点所...

JackJiang- ⋅ 2017/11/20 ⋅ 0

现代IM系统中聊天消息的同步和存储方案探讨

本文原作者:木洛,阿里云高级技术专家,内容有删减和修订,感谢原作者。 1、前言 IM全称是『Instant Messaging』,中文名是即时通讯。在这个高度信息化的移动互联网时代,生活中IM类产品已经...

JackJiang2011 ⋅ 2017/11/24 ⋅ 0

Androidpn 简单实现及分析

(文中部分内容来自网络) XMPP协议: XMPP : The Extensible Messaging andPresence Protocol. 中文全称:可扩展通讯和表示协议. 简介:可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯、表示...

明舞 ⋅ 2014/08/09 ⋅ 0

三分钟帮你集成极光推送——和那些你可能不知道的事

本文简介:本文前篇,可以帮助朋友们快速集成极光推送。本文后篇,是我自己项目实践的一些总结和心得,应该对读者们还是很有参考价值的,相信读完这篇文章,你会对极光推送有更加深入的理解,...

一只懂音乐的码虫 ⋅ 05/09 ⋅ 0

Android动画:献上一份详细 & 全面的动画知识学习攻略

前言 动画的使用 是 开发中常用的知识 可是动画的种类繁多、使用复杂,每当需要 采用自定义动画 实现 复杂的动画效果时,很多开发者就显得束手无策 本文将献上一份动画的全面介绍攻略,包括动...

Carson_Ho ⋅ 06/06 ⋅ 0

2018 Android 面试心得,已拿到 offer

code小生,一个专注于 Android 领域的技术分享平台 作者:huisonma 链接:https://www.jianshu.com/p/855ff21e0a13 声明:本文是 huisonma 原创,转发等请联系原作者授权。 从 16 年毕业至今...

h176nhx7 ⋅ 04/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

大数据工程师需要精通算法吗,要达到一个什么程度呢?

机器学习是人工智能的一个重要分支,而机器学习下最重要的就是算法,本文讲述归纳了入门级的几个机器学习算法,加大数据学习群:716581014一起加入AI技术大本营。 1、监督学习算法 这个算法由...

董黎明 ⋅ 36分钟前 ⋅ 0

Kylin 对维度表的的要求

1.要具有数据一致性,主键值必须是唯一的;Kylin 会进行检查,如果有两行的主键值相同则会报错。 2.维度表越小越好,因为 Kylin 会将维度表加载到内存中供查询;过大的表不适合作为维度表,默...

无精疯 ⋅ 40分钟前 ⋅ 0

58到家数据库30条军规解读

军规适用场景:并发量大、数据量大的互联网业务 军规:介绍内容 解读:讲解原因,解读比军规更重要 一、基础规范 (1)必须使用InnoDB存储引擎 解读:支持事务、行级锁、并发性能更好、CPU及...

kim_o ⋅ 43分钟前 ⋅ 0

代码注释中顺序更改 文件读写换行

`package ssh; import com.xxx.common.log.LogFactory; import com.xxx.common.log.LoggerUtil; import org.apache.commons.lang3.StringUtils; import java.io.*; public class DirErgodic ......

林伟琨 ⋅ 51分钟前 ⋅ 0

linux实用操作命令

参考 http://blog.csdn.net/qwe6112071/article/details/50806734 ls [选项] [目录名 | 列出相关目录下的所有目录和文件 -a 列出包括.a开头的隐藏文件的所有文件-A 同-a,但不列出"."和"...

简心 ⋅ 今天 ⋅ 0

preg_match处理中文符号 url编码方法

之前想过直接用符号来替换,但失败了,或者用其他方式,但有有些复杂,这个是一个新的思路,亲测可用 <?php$str='637朗逸·超速新风王(300)(白光)'; $str=iconv("UTF-8","GBK",$s...

大灰狼wow ⋅ 今天 ⋅ 0

DevOps 资讯 | PostgreSQL 的时代到来了吗 ?

PostgreSQL是对象-关系型数据库,BSD 许可证。拼读为"post-gress-Q-L"。 作者: Tony Baer 原文: Has the time finally come for PostgreSQL?(有删节) 近30年来 PostgreSQL 无疑是您从未听...

RiboseYim ⋅ 今天 ⋅ 0

github太慢

1:用浏览器访问 IPAddress.com or http://tool.chinaz.com 使用 IP Lookup 工具获得github.com和github.global.ssl.fastly.net域名的ip地址 2:/etc/hosts文件中添加如下格式(IP最好自己查一...

whoisliang ⋅ 今天 ⋅ 0

非阻塞同步之 CAS

为解决线程安全问题,互斥同步相当于以时间换空间。多线程情况下,只有一个线程可以访问同步代码。这种同步也叫阻塞同步(Blocking Synchronization). 这种同步属于一种悲观并发策略。认为只...

长安一梦 ⋅ 今天 ⋅ 0

云计算的选择悖论如何对待?

人们都希望在工作和生活中有所选择。但心理学家的调查研究表明,在多种选项中进行选择并不一定会使人们更快乐,甚至不会产生更好的决策。心理学家Barry Schwartz称之为“选择悖论”。云计算为...

linux-tao ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部