关于标题,我也开始附庸文雅了。哈哈!话说软件架构是本人这些年的一些积累,在此做一点分享,希望对即将做架构或者是正在架构的 ITer 一些参考,也许见解浅薄,还望大家多多包涵,如有异议,可私聊,禁止拍砖。
说起架构可不是一件简单的事情,他是一个很庞大的系统骨架,但是面对到手的软件需求,你是如何设计一个合理的架构呢?原来架构这玩意也是分第一步、第二步、第三步 ........ 以此类推,不能急于求成,这样容易扯着蛋。要做到手中无剑,心中有剑的境界,并非一朝一夕的努力,需要长期项目的实践,经历无数次的失败与重构之后再能叩开“软件架构”这个大门的一丝门缝,从来一窥汪洋。
拿到一个软件的合同或者是用户的最原始的需求,很多架构师的第一直觉就是关注软件本身的功能,有哪些功能,适合采用哪些技术选型。这对于大多数人而言,的确是没有错误。但是在这之前还一点需要去做的事情 ...... 不知道大家想到了吗?架构是基于什么环境呢?这一点在架构之前不知道读者现在是否思考过这个问题。所谓的架构首先要了解软件运行的软硬件以及网络环境。之后明确了这一点才能考虑下一步的事情,否则就有点闭门造车的感觉了。
好了,说到运行环境,这是就要从硬件环境与软件环境分别对待了,例如:
1、硬件是采用几层架构啊,是采用集群还是单机部署呢? 典型的网络架构是Http服务器 + Web服务器 + 数据库服务器 。比较厉害的就是这些都放在一个机器上 哈哈~~~
2、软件运行的网络环境如何,是基于局域网还是公网呢?是否存在隔离的情况
3、硬件是什么机器呢?IBM刀片 还是 AIX系列呢
4、系统的灾备设备是什么,如何运作
......... 等等诸如此类的硬件环境首先要了解。
硬件明确了,下来考虑软件环境
1、软件运行的环境是 Window 、Linux、Unix ,是32位 /还是 64位
2、软件运行的Web服务器神码 WebSphere?Weblogic、Jboss、Tomcat、GlashFish还是别的?版本号是多少,支持的J2EE规范是多少。
3、JVM是什么版本,是全新部署 还是复用甲方已经存在的软件环境呢?
4、软件是否与其他软件有数据交互,交互采用什么介质
5、采用什么数据库,版本是多少?
.......... 这些是作为软件环境需要考虑的一些基础性的问题
通过上述简单的文字,好像大家有点明白了吧,其实这种事情,很简单的,只要平时多注意点就OK了,否则等开发后去现场实施才发现什么都不配套啊。如果在架构开始之前,你已经关注这一点了,恭喜了 说明你已经具备作为架构师最起码的嗅觉了。
上述的那些问题或多或少影响架构本身,架构是基于单机还是基于集群,这是两种迥然不同的架构模式,很多天真的童鞋认为 单机与集群架构差不多,复制一套在部署一台机器就OK了。其实则不然。二者存在太多的不同了,我举一些简单的例子哈:
在Java行业,很多开源软件使用很频繁,这些开源软件本身集成了Cache,用于提高性能,单系统是单机运行的时候,一切都正常,做了集群出现问题了。Cache 惹得祸,因为你的集群并没有把框架内部自带的缓存集群化,还是保持各自独立的状态,一用户通过A服务器修改了内容,并且刷新了Cache,但是作为集群的B服务器感知不到A对Cache的变化,依旧从自身的缓存中获取“脏数据”,这时其他用访问B服务器读到的值,其实是A用户通过A服务器修改之前的值。很绕吧。希望能理解。瓦咔咔~~~这是架构如果采用cache 需要重写自己的Cache框架,必须屏蔽开源项目中自带的Cached,否则出了问题都让你莫名其妙?
数据库部署是单机还是双机,是热备还是冷备,数据访问需要读写分离吗?如果硬件不支持数据库热切换,如何采用程序实现数据库的热切换?这些都是架构层次的需要直接面对的问题? 架构师们 你们心里有底吗?
与甲方内部的其他系统是采用什么通信方式?Socket、WS、RMI,访问的系统之间是否有防火墙隔离,采用什么技术可以穿透防火墙阻拦呢?
以后的系统如何快速部署到对甲方的真实环境中,是手动部署还是Ant自动部署?如何做到持续集成?发送错误如何回退?
......................... 现在是不是觉得有点复杂呢?其实没关系,成长有两种,一种是汲取别人成功的经验转为为自己的能力,另外一种是尝试失败,从经历失败中成长,本人呢二者兼有,前几年都是从失败中成长,但是随着时间的推移,发现这样的代价真的是很大。开始虚心学习前人的经验,从而转化为自己的知识。
掌握软件运行的软硬件环境是作为架构的第一步,切记切记!
上述文字,仅仅是作为软件架构之前需要考虑的一些大概,还有很多细节其实我也不是很了解,如有补充请回帖瓦咔咔~~~~ 软件架构 是一件艺术品~~~ 这是我对架构的感性认识。
每次架构完我都会在一段时间把它当作是完美的艺术品去欣赏,过了新鲜期后,发现很多问题,直接重构 这样循环不已 ............. 每次的迭代 都让自己收获很多!
tomcat 出现 OutOfMemoryError : PermGen space
最近在把在 tomcat 5.5 上开发的项目 deploy 到 JBoss 4.2 上时,在操作一段时间就会出现 java.lang.OutOfMemoryError: PermGen space,开始以为是代码中存在死循环的地方造成这样的问题,但是后来发现,出问题的地方都是随机的,并不是某一处造成这样的问题出现,怀疑是内存泄 露,通过增大 heap 内存的方法来尝试,依然不行,但是同样的问题却并没有在 tomcat 中出现过,难道是 JBoss 的问题?
在网上做了一番搜索得到一些相关的内容。
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。
改正方法,在 run.bat 中加入:-Xms256m -Xmx512m -XX:MaxNewSize=256m -XX:MaxPermSize=256m
因为项目中引用了很多的 jar 包,而这些 jar 包中的 class 信息会被 JBoss 的 class loader 加载到 PermGen space 区域,
在 JVM 默认的情况下,该部分空间的大小只有 4M,在 jar 包非常多的情况下,显然是不够用的,所以通过 -XX:MaxPermSize=256m 指定最大值后即可解决问题。
另外,如果 heap 内存不足出现 java.lang.OutOfMemoryError: Java heap space 时,可以通过 -Xmx512m 指定最大 heap 内存来解决这样的问题。
每个人都期待职位的提升和别人的认可
在国内,项目经理应该是大多数程序员比较想要的职位
这篇文章将会告诉你,要成为项目经理的一些技巧。或者说,从另外的一个角度来看项目经理这个职位。
项目经理要懂技术这个问题,在iteye上被讨论了很久。各有各的观点,我在这边不做评论。
技术做为一个人可以掌握的技能,当然是越多越好,所以从技能方面说,懂点技术当然比啥都不懂要好
另外一方面,项目经理要和技术人员沟通,而技术人员的难以沟通是出了名的。如果脱离一些基本层面的术语,沟通到一些具体的解决方案的时候,往往和技术人员的沟通有更好的效果。而这里就会用到很多技术。
所以,从这方面说,项目经理掌握一些技术,可以说是必须的。。。
当然,类似外资公司的项目经理,完全只考虑项目进度的,那就另外说。。。
项目经理要有口才项目经理需要在团队士气低迷的时候,鼓舞士气
项目经理需要在客户沟通的时候,保持不卑不亢的气势
项目经理需要说服客户和领导给予更多的资源
项目经理要有谋略办公室政治我就不在这边罗嗦了
我想要说的是,作为项目经理,你经常面临很多办公室政治的挑战。
你至少要做到团结可以团结的力量。包括你的领导,你的下属,你的战友,销售,客户等等
你可以把你的泡妞经历和技术人员分享,来换取他们的支持
你也可以把你赚钱的股票和他们分享
你甚至什么都不用付出,只要时不时的体现一下你对他们的关心。
光说不练,这边整个练习题
做项目的人经常碰到的,客户要在10天之内,完成一个功能模块,在技术人员开看基本上是不可能完成的任务
那做为项目经理,需要怎么样处理这件事??
我们都知道处理结果,要么增加周期,把10天换成20天,要么删减功能。那我们采取什么样的手段来实现这个结果??一方面不能得罪甲方,另外一方面要实现我们的预期。
方法:把技术人员和客户负责人叫到一起沟通,名义上解决一些gap,实际上是让有冲突的双方直接面对。作为甲方,你是不能得罪的,做为技术人员,你可以站在甲方的立场和技术人员争吵。这个时候,你要把甲方的负责人凉在一边,让他看你们争吵,你们在争吵的过程中最好要用到解决方案的具体细节,让他听不懂。然后时不时的回过神来,用甲方的口气和技术人员说,这个时间已经决定了的。你和技术人员可以事先沟通好,要尽量顶,甚至发火。然后你们的争吵就会让负责人很尴尬,退也不是不退也不是。
结果:一般1-2个小时以后,负责人就会让步,一般是延长周期,如果周期他决定不了,那一般是删减功能。
思考:我们没有得罪甲方,但是甲方顺利让步。但是我们得罪了技术人员,所以在事后,一定要诚恳道歉,除非你们已经很娴熟
另外,这个方法是考虑到如下因素
很可能这边提出的10天,不是最终的时间点,而是那个负责人为了自己的performance提出的,如果技术人员反对很严重,这个防线肯定会崩溃
还有可能是负责人不清楚最终的实现,时间也不是他定的,他也需要和上级沟通。如果他看到技术人员如此抵制,考虑到这个功能计算在自己的performance下面,他会帮你争取更多的资源。这个时候,让负责人和他的领导去谈,会取得更好的效果。
上面都是一些基本素质,如果具备下面的几点,会事半功倍哦!!!
幽默感:团队粘合剂,也是化解尴尬的最好的东西
真诚:如果你做不到欺骗一辈子,那你就坦率点
尝试用对方的角度思考:让你发现自己的问题,发现别人的弱点。
最后,留下一个问题,如果一个团队成功的做完了一个项目,各方面评价都很好,你猜测一下,谁在这个成功的项目中,获利最大?
从1.2版本开始,Ehcache可以使用分布式的缓存了。
分布式这个特性是以plugin的方式实现的。Ehcache自带了一些默认的分布式缓存插件实现,这些插件可以满足大部分应用的需要。如果需要使用其他的插件那就需要自己开发了,开发者可以通过查看distribution包里的源代码及JavaDoc来实现它。
尽管不是必须的,在使用分布式缓存时理解一些ehcahce的设计思想也是有帮助的。这可以参看分布式缓存设计的页面。
以下的部分将展示如何让分布式插件同ehcache一起工作。
下面列出的是一些分布式缓存中比较重要的方面:
你如何知道集群环境中的其他缓存?分布式传送的消息是什么形式?什么情况需要进行复制?增加(Puts),更新(Updates)或是失效(Expiries)?采用什么方式进行复制?同步还是异步方式?
为了安装分布式缓存,你需要配置一个PeerProvider、一个CacheManagerPeerListener,它们对于一个CacheManager来说是全局的。每个进行分布式操作的cache都要添加一个cacheEventListener来传送消息。
正确的元素类型
只有可序列化的元素可以进行复制。
一些操作,比如移除,只需要元素的键值而不用整个元素;在这样的操作中即使元素不是可序列化的但键值是可序列化的也可以被复制,
成员发现(Peer Discovery)
Ehcache进行集群的时候有一个cache组的概念。每个cache都是其他cache的一个peer,没有主cache的存在。刚才我们问了一个问题:你如何知道集群环境中的其他缓存?这个问题可以命名为成员发现(Peer Discovery)。
Ehcache提供了两种机制用来进行成员发现,就像一辆汽车:手动档和自动档。
要使用一个内置的成员发现机制要在ehcache的配置文件中指定cacheManagerPeerProviderFactory元素的class属性为net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory。
自动的成员发现
自动的发现方式用TCP广播机制来确定和维持一个广播组。它只需要一个简单的配置可以自动的在组中添加和移除成员。在集群中也不需要什么优化服务器的知识,这是默认推荐的。
成员每秒向群组发送一个“心跳”。如果一个成员 5秒种都没有发出信号它将被群组移除。如果一个新的成员发送了一个“心跳”它将被添加进群组。
任何一个用这个配置安装了复制功能的cache都将被其他的成员发现并标识为可用状态。
要设置自动的成员发现,需要指定ehcache配置文件中cacheManagerPeerProviderFactory元素的properties属性,就像下面这样:
peerDiscovery=automatic multicastGroupAddress=multicast address | multicast host name multicastGroupPort=port
# (timeToLive属性详见常见问题部分的描述)
timeToLive=0-255
示例
假设你在集群中有两台服务器。你希望同步sampleCache1和sampleCache2。每台独立的服务器都要有这样的配置:
配置server1和server2
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=32"/>
手动进行成员发现
进行手动成员配置要知道每个监听器的IP地址和端口。成员不能在运行时动态地添加和移除。在技术上很难使用广播的情况下就可以手动成员发现,例如在集群的服务器之间有一个不能传送广播报文的路由器。你也可以用手动成员发现进行单向的数据复制,只让server2知道server1而server1不知道server2。
配置手动成员发现,需要指定ehcache配置文件中cacheManagerPeerProviderFactory的properties属性,像下面这样:
peerDiscovery=manual rmiUrls=//server:port/cacheName, 
rmiUrls配置的是服务器cache peers的列表。注意不要重复配置。
示例
假设你在集群中有两台服务器。你要同步sampleCache1和sampleCache2。下面是每个服务器需要的配置:
配置server1
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server2:40001/sampleCache11|//server2:40001/sampleCache12"/>
配置server2
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server1:40001/sampleCache11|//server1:40001/sampleCache12"/>
配置CacheManagerPeerListener
每个CacheManagerPeerListener监听成员们发向当前CacheManager的消息。
配置CacheManagerPeerListener需要指定一个CacheManagerPeerListenerFactory,它以插件的机制实现,用来创建CacheManagerPeerListener。
cacheManagerPeerListenerFactory的属性有:
class – 一个完整的工厂类名。
properties – 只对这个工厂有意义的属性,使用逗吃分隔。
Ehcache有一个内置的基于RMI的分布系统。它的监听器是RMICacheManagerPeerListener,这个监听器可以用RMICacheManagerPeerListenerFactory来配置。
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=localhost, port=40001,
socketTimeoutMillis=2000"/>
有效的属性是:
hostname (可选) – 运行监听器的服务器名称。标明了做为集群群组的成员的地址,同时也是你想要控制的从集群中接收消息的接口。
在CacheManager初始化的时候会检查hostname是否可用。
如果hostName不可用,CacheManager将拒绝启动并抛出一个连接被拒绝的异常。
如果指定,hostname将使用InetAddress.getLocalHost().getHostAddress()来得到。
警告:不要将localhost配置为本地地址127.0.0.1,因为它在网络中不可见将会导致不能从远程服务器接收信息从而不能复制。在同一台机器上有多个CacheManager的时候,你应该只用localhost来配置。
port – 监听器监听的端口。
socketTimeoutMillis (可选) – Socket超时的时间。默认是2000ms。
配置CacheReplicators
每个要进行同步的cache都需要设置一个用来向CacheManagerr的成员复制消息的缓存事件监听器。这个工作要通过为每个cache的配置增加一个cacheEventListenerFactory元素来完成。
<!-- Sample cache named sampleCache2. -->
<cache name="sampleCache2"
maxElementsInMemory="10"
eternal="false"
timeToIdleSeconds="100"
timeToLiveSeconds="100"
overflowToDisk="false">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true "/>
</cache>
name:缓存名称。通常为缓存对象的类名(非严格标准)。
maxElementsInMemory:设置基于内存的缓存可存放对象的最大数目。
maxElementsOnDisk:设置基于硬盘的缓存可存放对象的最大数目。
eternal:如果为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false;
timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期。当对象过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态。
timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期。当对象过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义。
overflowToDisk:如果为true,表示当基于内存的缓存中的对象数目达到了maxElementsInMemory界限后,会把益出的对象写到基于硬盘的缓存中。
class – 使用net.sf.ehcache.distribution.RMICacheReplicatorFactory
这个工厂支持以下属性:
replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers. 默认是true。
replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。
replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。
replicateAsynchronously=true | false – 复制方式是异步的(指定为true时)还是同步的(指定为false时)。默认是true。
replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制(指定为true时为复制),默认是true。
你可以使用ehcache的默认行为从而减少配置的工作量,默认的行为是以异步的方式复制每件事;你可以像下面的例子一样减少RMICacheReplicatorFactory的属性配置:
<!-- Sample cache named sampleCache4. All missing RMICacheReplicatorFactory properties default to true -->
<cache name="sampleCache4"
maxElementsInMemory="10"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
</cache>
常见的问题Windows上的Tomcat
有一个Tomcat或者是JDK的bug,在tomcat启动时如果tomcat的安装路径中有空格的话,在启动时RMI监听器会失败。参见http://archives.java.sun.com/cgi-bin/wa?A2=ind0205&L=rmi-users&P=797和http://www.ontotext.com/kim/doc/sys-doc/faq-howto-bugs/known-bugs.html。
由于在Windows上安装Tomcat默认是装在“Program Files”文件夹里的,所以这个问题经常发生。
广播阻断
自动的peer discovery与广播息息相关。广播可能被路由阻拦,像Xen和VMWare这种虚拟化的技术也可以阻拦广播。如果这些都打开了,你可能还在要将你的网卡的相关配置打开。
一个简单的办法可以告诉广播是否有效,那就是使用ehcache remote debugger来看“心跳”是否可用。
广播传播的不够远或是传得太远
你可以通过设置badly misnamed time to live来控制广播传播的距离。用广播IP协议时,timeToLive的值指的是数据包可以传递的域或是范围。约定如下:
0是限制在同一个服务器
1是限制在同一个子网
32是限制在同一个网站
64是限制在同一个region
128是限制在同一个大洲
255是不限制
译者按:上面这些资料翻译的不够准确,请读者自行寻找原文理解吧。
在Java实现中默认值是1,也就是在同一个子网中传播。改变timeToLive属性可以限制或是扩展传播的范围。
原文地址为 http://ehcache.sourceforge.net/documentation/distributed_caching.html