软件设计

软件设计

  1. 一定是创建订单的时候填充market字段,我曾经一度打算在回调的时候再根据回调方来填充Market,但是如果没有回调呢?Market这样的标志性字段一定要依赖于靠谱的操作;
  2. 对于重载方法要注意,尤其套调用的重载方法,对于某些核心校验必须要放置在里层方法调用,否则因为重载都是public出去的,都可以被外界调用,如果在外层方法实现校验,里层重载方法被外界直接调用,校验会被跳过;考虑CheckMarket是放在CreateOrder(String encryptedString)还是CreateOrder(OrderInfo orderInfo)中实现?前者解析加密字符串调用后者,最后想通了是要放在后者中进行校验;
  3. 设计的时候一定要在设定的场景中多走几遍,这样做的目的是跳出实现,要把实现放在应用中进行验证;本质:任何实现都是一个流程的一个环节,其意义在于上下游,而不是实现本身;
  4. 获取玩家信息其实使用旧有的接口即可,但是我却搞混了玩家信息表和订单表;分析问题就是要极端:向上谋求大的方向,向下到根(表,字段级别),谋求准确;
  5. 腾讯之所以二次验证是因为:担心回调返回信息没有接收到,没有扣款;这个机制不同于其他平台,其他平台如果没有收到,将会续发;腾讯这样处理其实是把球抛给了游戏服务器;
  6. OpenId已经修改为String类型真的是百搭啊,作为众多市场使用的字段,int的范围还是太小了;这个让我想到了CreateOrder的返回值,对于适配众多情况的字段、方法类型一定要考虑百搭的类型;Factory入口参数已经由枚举改为了字符串类型,因为我发现了一个问题,需要枚举和显示字符的互转,这样每增加一个市场,同时需要维护枚举和常量,太不靠谱了;这样看来单独一个类里面封装常量其实是比枚举具有更大的描述能力;CreateOrder之初设计的返回int,这个返回值太小了,包含的信息量远远不够,尤其是对于支持众多市场;比如面对腾讯的支付机制,返回的就是一个JSON串,所以对于通用的接口设计返回值的格式要复杂一些,描述能力强一些;
  7. 支付市场流程中,支付服务器回调游戏服务器,其实主要的用意是通知游戏服务器添加金币,增加道具;
  8. 我已经修改为统一使用BasePaymentInfo作为参数,这样的替换大大减少了代码,之前需要字典对参数进行接收,然后再PaymentService在取出参数进行处理;
  9. 其实很多时候一个空的基类,作用就是用于实现开闭原则的闭,BasePaymentInfo里面什么都没有;但是BaiduPaymentInfo以及QihuPayemntInfo都是继承自它;
  10. 前天我在和Steven在谈论是否需要把支付拿成一个dll的时候,Steven讲到他可以保证部署的时候不出错;但是这样上纲上线有意义吗,因为是否就是保证修改代码的人一定能够取到最新的代码,是否部署的人一定就是Steven?最小的部署就是能够尽量不要依赖于人的因素,或者尽量减小人的因素;
  11. PaymentService的价值在于对于支付这个领域提供了一层的封装,对于外界的调用而言只需要知道一个Pay接口就可以了,而不需要知道还有一个Factory,还有一个Manager
  12. 今天测试发现了一个Bug,更新Bill表之后直接操作更新了PlayerData表,这里有个问题:如果Bill表查到的数据为空,是不需要更新后者的,结合另外一个Bug,支付失败了是不需要更新PlayerData的;其实这两步操作不是代码关联,其实是:两步操作,牵涉到了两部分业务(逻辑),这种跨域的操作一定是要有一个分析和校验过程,是否具备关联性;这两个问题提供了一个视角:你再来做项目不要只是看到代码,而是要抽象到一个更高的角度,从"模块化"(业务、逻辑)以及全流程来看待开发本身;
  13. 创建订单等操作还是要放置在BasePaymentManager,而不是Service,因为创建订单逻辑可能是会变得,你需要让PaymentManager的实现类能够重写;如果放置在Service里面就不好处理了;其实开闭原则,讲的是对变化开放,对稳定关闭,换句话讲,就是对于开放接口是关闭,内部实现是开放,比如对于Service(领域入口)必须是关闭的,需要设计参数都是具有一定的伸缩性,但是对于内部实现,manager是哪个,应该具有扩展性;
  14. 对于接口的伸缩性,比如服务器增加了一种情况为,有的客户端可能需要改代码,享受新增加的功能,有的客户端就不需要改代码,旧有的功能就可以了,这个时候接口标准是不变的,不需要享受的也不需要改动;保证了请求多样性;
  15. 对于sessionKey的保存最开始打算保存在redis中,但是动物快跑,水浒传等牙根就没有Redis,另外,sessionKey是一个很重要的变量(Confirm环节需要拿去和支付服务器做支付确认的),不是那种丢了就丢的重要信息,一定是要保存在数据库中的;我添加了一个EXT_INFO字段用于存放sessionKey信息;
  16. 微信支付Demo中的WXPayData是键值对形式(封装了toXmltoJson等方法),里面包了一个SortDictionary方法;这种数据结构扩展性很好;很大程度上可以和空基类相提并论;缺点是无法进行编译时报错;
  17. 微信平台公共平台的开发因为无法调用外网;于是我把获得路径封装为一个方法,如果是测试环境(根据配置文件数据),我将会返回一个模拟功能的ashx文件进行数据返回;这样可以测试下去了;
  18. 在非Web环境下,Controllersession对象为空;这个时候为了能够进行单元测试,我设计了一个CaseBase类,这个类会根据配置文件配置,测试环境下(Test工程跑步),就采用内置Diction进行数据保存;正式Web环境下就采用session进行保存;为了保证全局可访问,不随着controller消亡而消失,Diction对象需要声明为static变量;
  19. 说到了单元测试,我觉得在biz层的设计一定要和前端的神马(Web技术)技术解耦,就是一个biz框架,提供了那些功能,Web也好remoting也好,还是Webservice都是可以直接调用;这个很独立的第三方;
  20. 作为类库,可以考虑返回值为OperationResult,内部发生异常不要抛出来,让调用方判断是抛出还是过去;
  21. 单元测试如果测试的机制和待测试代码机制一样也就失去了测试了意义;
  22. 梳理清楚业务:1.调用的场景;2.操作了什么表;3.过得了什么信息(字段)4.整体流程是怎样的;
  23. 掌握一门技术,要首先搞懂几个问题:1.这个技术解决了什么问题;2.优势是什么,适合什么场景;3.缺点是什么,不适合什么场景;4.提供功能的原理是怎样的;才算是了解了一门技术;
  24. 架构师的世界:一切尽在掌握,对于各个生命周期阶段都能够切入和控制;这样便于扩展,这就需要两种工具:封装和隔离(比如MVC很大的好处就是可以控制view,比如我想要把view缓存,就可以通过在contrroller里面做,但是传统的ASP.Net如果想要对view缓存就不是很直观;这也是为什么要封装日志接口;Session接口;比如serverlet是被tomcatstandwrapper封装而不是直接使用;归拢,核心系统套一个壳子隔离和第三方;为什么过期自动删除redis很少的应用场景,因为删除很多时候需要业务逻辑判断以及处理;所以很多时候采用的判断时间戳,到期了再如何如何;
  25. 今天在使用印象笔记发现一个问题,搜索之后,用户不知道怎么回到初始状态,其实我们做系统设计很多时候都会忽略行为的回朔,就是任何动作之后,都要让用户能够很容易回到某种状态,至少应该是回到初始状态,最后还好,在左侧菜单找到了"笔记"的图标,返回了初始页面,但是这个图标并不明显,不论怎样,evernote还是考虑到了这一点,搞了一个总回头的图标恢复状态;
  26. 需求驱动架构,架构驱动架构,架构驱动测试,测试驱动开发;
  27. 测试用例的设计,做到一个方法只测试一个点;
  28. 对入口参数校验真有意义,比如buildcommand方法,如果传入的cebidentifiercommand不一致,报错,问题一下清晰了;
  29. 刚才纠结于getdatabuf是否要如果null返回一个new对象,后来发现不需要:就像c++遭人骂就是因为行为里面做了太多的事情,职责清晰,尽量简单;
  30. 框架,是自成体系,只不过有些缺失部分,需要进行填充,然后框架就成为了系统,系统,是流程,生命周期完备的东西;
  31. 开发每天至少应该有39%的时间是在思考,学习;
  32. 架构/框架的本质是定义行为,定义行为的前提是定义好模块以及模块功能,让这些模块通过组合能够完成系统的功能;
  33. 关系,关系,关系!做设计很重要的一点就是理清楚关系,从session池的处理(连接到同端的连个session怎么区分),到通知参数(多个文件情况如何通知应用),都在说明,设计就是要捋顺对象间关系,面向对象就是构建世界,貌似简单,但是世界对象间很多关系是隐含的,并不显式,有些关系可以忽略不用构建,考虑,但是有些确实要挖掘。
  34. 框架思维,何为大者,就是看到的点是大的方面,对于系统的理解和开发过程都要放到一个框架思维来进行设计
  35. 什么是框架?框架就是一种机制,保证某些维度功能的机制(比如开发效率,性能,隔离原则等);
  36. 每当你在设计缓存的时候,考虑数据丢失是否可以,是否有机制重新获取,这种机制是否实现;
  37. 其实一件事情没有做完就开始下一件事情,寄托于未来完善,可以,但是一定是一个成品,而不是概念品,因为,你未来再做这件事情最大的成本就是,你需要重新进行梳理思路。续传就是这样,单元测试做完了,没有结合测试,回过头来再完善,发现思路全忘了,还要重头来;
  38. 每当你引入一个新的对象,比如任务的临时ID,都要考虑他的生命周期,以及他的生命周期和别的对象生命周期之间的影响,其次要把生命周期和事件捋顺了,比如任务的生命周期是INIT-RECEIVING-REVCOMPLETE-SENDING-SENDCOMPLETE,其中INIT是发生在DATA2HandlerReceving是发生在DATA指令等。
  39. 认证,用意就是证明这个用户存在,授权,就是这个用户可以干这个事情。
  40. 都是提供功能,和业务逻辑相关的称之为服务,和业务逻辑无关的称为基础设施
  41. 做架构设计一定要从重要的,重点的地方设计,否则你的设计很可能就卡在这些地方。比如当时对于断点续传考的不全面,直接导致了开发到了后面的问题;
  42. switch有一个缺点,容易忘记break, 而且如果多了,其实结构并不清晰,还不如else if;
  43. string有一个巨大的问题,就是他其实是一个弱类型,首先比较容易出错,是用==还是用equals,其次复制了一个常量,忘记改名字了,除非运行时测试,否则不自知。这一点是枚举的好处。
  44. PraCommand处理的时候需要知道发送方是否就是目的端。可以有两个地方进行处理,目的端发现自己是目的端,则更新PraCommandisTail字段;第二个地方是接收方来判断,发送方是否和路由的最后一个设备一致。后来我选择的是前者,这是因为这样能够有效的减少判断;比如作为转发端,转发十个端那么判断十次;但是作为目的端而言其实只需要判断一次即可。这是一种分压/分流的机制。
  45. 想好了,一气呵成写,减少手敲,增加拷贝,那个写perl的哥们,c++代码写的时候就是绝大多数是拷贝
  46. 今天发现了一个方法竟然套了五层方法,taskfilestorage里面竟然判断任务状态的逻辑在里面,想想也是醉了,当初这个类定位就是将任务信息放置到文本文件而已。作为设计,一定要划分好类职责,这种实现方法的机制不要超过三层。
  47. 传输项目做到现在,我最大的心得就是1.控制异常抛出范围;2.并发控制3.设计的重要性,设计的重要性在于重点难点的设计,全局的考量。
  48. 今天发现以为写入进度信息到任务文件的异常,导致后续的将文件片进行保存的核心操作都没有进行处理,在写代码的时候一定要将功能进行划分域,域和域之间不要有本质影响。
  49. 一定要使用自定义的异常,并且尝试抛出他,其实在日志中很容易发现异常的信息;不处理的异常其实并不是坏的事情,吞咽异常很多时候是隐藏问题。所以通过自定义的异常,并让他抛出来并且catch打印出来,其实很方便跟踪问题。
  50. 线程一定要考虑到异常处理,否则这个线程将会挂掉,特别是那些定时任务的。
  51. 抽象成服务/组件的一个巨大好处就是封装性,比如断点续传,现在就是逻辑散落在各个类中,与之相反,如果有专门的一个或者几个类处理,那么就可以看到续传类的全貌了。
  52. 考虑设计上面的闭环,比如向monitor队列中放入一个元素就要考虑如何撤掉,哪几种可能需要撤掉。
  53. 对于类似于发送反馈的处理,内部异常就处理掉,不要外抛,不要因为这种无关主业务的操作导致后续操作无法进行。
  54. handler是事件,service是服务,事件组装服务,服务要拥有比较好的封装性,比如所有的和断点续传相关的操作都要封装在一个类中。服务封装1.代码集中度高,内部高耦合成型;2.好测试,前提保证服务入参简单,不要让外部业务污染服务,比如传入了一个session,就意味着测试的时候需要构建一个session。我突然觉得自己这种设计和领域驱动有些接近。
  55. 我发现在方法体的开始把重要的入参打印出来是一个很好的习惯。
  56. 生态,掌握一门技术,其实本身并不难,难得是掌握它的生态。
  57. 传输的多线程,异常抖动情况下还是有问题。其实最核心的代码就是在于client.put, client.executeNext两个方法,分明就应该好好的规划线路分析如何来处理并发、异常抖动等情况,但是我却没有很好地树立这个地方,你看到不是一端代码,一个函数,看到的应该是一个流程,分支,以及这个分支的各个节点。
  58. 对象中的属性和方法的存取也是有权限的,比如对于taskgetclient,其实只有发端任务才有权限,收端任务无权访问此属性,否则将会报异常,所以,设计属性和方法的时候一定要考虑成员权限问题,无论是设计还是使用。
  59. 重新下发,这个策略很好,刚才我就在纠结发现一份报没有有效性怎么处理,其实就让运维人员删除后重新下发就结了。其实很多时候,不要纯技术思维,当发现技术无法很好的解决一些问题,考虑一下是否可以采用手工的方式来进行处理;
  60. 做项目设计,首先是识别对象,比如三部的项目,包括单位,节点,文件等等,在识别对象的时候同步要把核心属性识别出来;然后是识别行为和关系,二者可以同步交替进行,OO三剑客之后,项目搞掂了。
  61. 不要纠结底层区别,因为区别不大,占用内存之争不是很有意义;
    实例和静态的根本区别在于概念;面向过程年代,大家都是静态函数,单例模式是面向对象提出之后的设计模式,如果一个类里面的函数是和这个类有机的一体的,则是单例,如果类只是作为容器(比如工具类),那么就是静态。
    网上一则比喻很恰当,一个人的胳膊腿,面容是和一个人息息相关的,而且因人而异,这个时候,需要实例,如果说人类所属的纲目,这些泛华的内容则是静态的,不因为每个人的不同而不同,那么就是静态的。
  62. 什么样的类是单例,什么样的类不是呢?如果实例千篇一律,比如配置(文件)类,整个应用程序都是使用一个配置文件,那么他的映射的ConfigProperties类就应该是单例;如果说不一样,比如文件类File,每个文件都是不同的,那么就是一个实例;
  63. 无论是加密机,传输机,还是组装机,他们最好不要知道任务内容,这样,每个机的业务逻辑最纯洁,至于任务情况,由总控来调度任务管理处理,总控只是负责调度,任务只是维护任务信息,各种机只是负责对文件粒度的处理即可。
  64. 架构一个很重要的意义在于将复杂问题分解。所以架构的价值在于有效合理识别,归类和分解。明白此处,系统分析以及架构基本成了。
  65. 做开发要想明白一些事情,比如数据的处理,数据要放在内存,还是放在可以持久化的缓存,还是放在数据库中?这些都是在设计的时候要进行设计的。
    再比如,controller中的返回数据是封装在个类里面,还是要散列的存放;这种数据的组织形式也是要考虑
  66. 今天充分体会到了设置类型为List<T>的实惠了;本来在HiveController中调用historyService.findList()返回的是arrayList;但是后来发现数据结构其实应该是Queue,于是改为了LinkedList,但是这样的改动其实对于Controller毫无影响。所以这种泛型在封装向上面有很好的应用,可以使用类的函数返回值,本类函数返回值中应用反应,作为屏蔽封装部分的复杂性。
  67. 前者是非等幂操作;需要进行判断是否有重复的;但是对于后者则是几乎是等幂的;不过可能还有一个点需要check就是并发问题,你所改的是不是脏数据。这个在数据库层面可以进行判断;但是如果是修改配置,这个就要看配置是否支持。总之每个操作之前都要首先想想是否有需要校验的地方。安全是大事,其实技术到了高手之后,就是看谁的安全意识和解决方案更合适了。
  68. 之前调试结果有点问题,苦于没有找到原因;后来看到原来code中这个地方有一个print,一下子就定位到了是因为loaddataset的问题(书中代码和github代码不一致)。这里说明了输出日志文件的重要性。日志一定要具有其可跟踪性,可追溯性。

技术

  1. 自定义异常很大程度上解决了测试异常的问题;比如现在使用resultCode来判断错误类型,如果使用异常的话完全可以利用Exception进行处理;另外.netExceptedException真的好弱啊,只能判断类型,Message都指定不了,难道只能try...catch中增加assert吗?
  2. 多线程开发,对于每一个线程而言,代码都是独立的,他们不过是按照顺序去执行附加的代码;但是代码段本身是共享的,这里系统会为每个线程分配自己的参数堆栈,如果是代码段内部的变量(局部变量),各自堆栈维护,不会有任何问题;操作有交集,但是操作数据都是自己的数据;这个就像过去的合厨做饭一样,炉子是公用的,但是锅碗瓢盆,菜果都是自己的,及时彼此做饭有交集,用的都是公用的炉子,空间,但是做出来的都是自己的饭菜;但是,对于共享的变量就不一样的:因为他会变,而且不会因为你的操作而变,比如合厨的液化气,你用完之后,第二次再用存量就会少,因为别人(别的线程)也会用;所以,当你想要对这种公共变量操作的时候,为了保证操作间原子性,需要为操作加锁(锁上合厨的门);
  3. Buddy描述的关系,并不一点是要好友才使用SFS中的Buddy,能够用Buddy的关系来描述的都可以使用它;
  4. 8bit = 1byte;但是位(bit)是计算概念,二进制运算;但是byte是表示概念,一个byte表示可以容纳一个ascii码值,2byte可以表示为容纳一个汉字;
  5. 1KB = 1024byte1GB = 1024KBbitbyte8进制,但是BKBKBGB则是10+3E进制(1024);
  6. USE_FLG价格索引后,查询效率立即提高了;
  7. 证书加密的本质:服务器交给客户端一把钥匙一把锁;客户端将会使用这把钥匙来打开服务器端发送的"黑匣子",客户端会用这把锁来锁住"黑匣子"然后寄给服务器端;客户端同样也会把一把钥匙和锁头给服务器端;
  8. 构建一个性能解决方案列表:insert而不用updateDB迁移到memcacheEFSaveChange导致性能下降,那么就采用存储过程;查询昵称导致性能问题,那么就一次性把ID导入到内存中来;
  9. ASCII中有空"",代码是0,这也是为什么字母排序机制中,"逐位比较"机制中,"aaa"是要小于"aaaa"的原因,到第四位进行比较的时候,前者是空,ASCII值为0,后者是"a",值为65;备注,0ASCII值为48
  10. MIMEMultipurpose Internet Mail Extensions)最初是应用在电子邮件上面,可以根据MIME信息进行指定应用程序打开传输内容;后来这种技术被浏览器扩展,早期的浏览器是不支持传输非ASCII内容的;后来扩展了MIME技术到浏览器上面,这样就可以通过HTTP协议进行传输各种应用类型文件,浏览器将会根据MIME信息安排指定的应用程序打开/运行;比如文本文件text/htmlpdf文件application/pdf,浏览器看到了application/pdf就会使用内嵌的pdf插件进行打开;调查MIME的由头是application/octet-stream类型,后来这种MIME类型是任意二进制文件;
  11. SCSI: Small Computer System Interface,定义了系统和设备交流的标准,比如和硬盘,CD-ROM,另外一种常用的接口常见的接口是IDE
  12. cpu版本x8632位,x86_64则是intel为了兼容AMD64位指令而设计的;x64则是AMD的架构了;IA-64Intel输惨了;Windows2008本来答应开发基于IA-64的操作系统,结果很久都没有完成;
  13. 负载均衡有两种,均衡操作,集群中的读写分离就是这种均衡(基于主从服务器);还有一种是访问压力均衡,比如一致性算法,NginxIP_Hash等;
  14. 设置为cache-controlmax-age以及Expire关键字之后,在各个浏览器张行为一致的是通过页面点击链接二次链接会缓存页面,以及新的Tab页面输入同样地址后发送请求,都将不会触发二次请求,直接用本地缓存文件;对于F5刷新,各个浏览器都是每次都从服务器取值;对于IEfirefox而言,在地址栏敲回车不会因为二次请求,chrome依然会进行二次请求;使用firefox之前各种情况都会向服务器发送请求,后来诊断确定是因为配置中将缓存空间设置为0导致;
  15. 另外一种相关的配置是LastModified,这个指令是response的时候,服务器给客户端的,这样下次向同样的资源发出请求的时候,将可以直接返回304告知客户端什么都没有改变,这样就可以放心大胆的使用客户端缓存内容了,但是在和业务后台相关的处理中,需要代码来判断是否有改变,需要controller实现LastModified接口,并实现方法进行业务判断;
  16. Ctrl+F5,强制重服务器取出新的内容;其实就是在Request头中添加了Programa:no-cacheCache-Control:no-cache;其实你如果使用FF就会发现,如果设置缓存空间为0,那么无论你是地址栏敲回车,还是页面链接,F12里面的"网络"面板都会感应出请求信息,但是一旦你设置了空间,就会在该面板中显式要你刷新;这就说明了前面的情况是因为根本就没有向服务器发出请求,走的本地缓存;
  17. Keey-alive允许了客户端发送请求后,并不马上断开Http通道(TCP通道),多个Http请求公用一个TCP通道很大程度上减轻了服务器为每个请求创建进程处理的成本(另外还有三次握手,Http是基于TCP的);因为一个Html,除了页面元素,还有cssjs都是要通过http进行多次请求完成的;但是keep-alive还有一个缺点,就是如果不能及时释放将会导致资源浪费;所以Keep-alive一般都是指定timeoutmaxmax指的是一个通道最多能够接受的请求;timeout指的是断开连接的时长;如果在timeout时长内请求数达到了max上限,将自动断开连接;
  18. hosts文件中看都有一个注释掉的::1,这个其实是代表ipv6地址,ipv6采用128位进行表示,格式为8个四位16进制组成,1234:5678:90abc:def0:1234:5678:90ab:cdef,为了兼容Ipv4地址,ipv4地址将被表示为0000:0000:0000:0000:0000:0000:192.168.0.1ipv4地址占据两段,这意味着ipv432位的);对于前面的六段四位0,可以简单表示为::192.168.0.1;所以你看到的hosts文件中的::1其实就是为ipv6地址做准备的;
  19. "()"在正则表达式中代表模式,前面可以是一堆^*.,但是真正闪亮登场的确是"模式",模式代表着真正要匹配的部分,对于replace方法而言,要替换的就是模式部分,如果表达就是模式可以省略()
  20. Java里面小心转意需要使用\\,因为字符串里面内容才是表达式,比如替换掉所有括号里面内容replaceall("\\([^(]*\\)",""),或者在表达式中前后添加()也可以,语义更加准确,表示模式
  21. 使用filebuffer来封装hasHMAP<string, queue<commandexchangbuffer>>,但是后来发现这种封装损失了对于key的遍历好处,判断某个是否存在还需要遍历,而不是contains,十分不优雅;
  22. xmlns:xsi是指web.xml遵守xml规范;xsi全名:xml schema instance
  23. xsi:schemaLocation是指具体用到的schema资源, 是命名空间和xsd文档配对出现,校验xML是否合法,就是到此获得xsd文件,对节点属性进行check的;如果你把xml:context删掉了,spring将会做xML文档校验,对于<context>节点就无法进行解析,编译将会出错;
  24. spring多做了一点,如果你的xsd文件没有指定版本号,那么就不从网址下载,而是从本地的springjar文件中,找相应的文件进行处理;避免因为网络原因无法获得xsd文件而启动失败
  25. spring拦截器,通过mapping以及exclude-mapping,能够指定那些请求需要拦截,那些请求不需要拦截;拦截器需要指定处理bean,对于拦截的请求,交给bean进行处理。
  26. spring的配置文件头中指定了很多命名空间;
  27. 事务的acid

    原子性,账户转移,A账户转到B账户,a转了10元,B一定会接收到10元,要么成功,要么全失败;

    一致性,A少了10元,B多了10元;

    独立性,原子操作未完成,B在操作过程中是不会发现事务过程;

    持久性,对于修改是持续可见的。

    1. 我现在更加发现抽象美妙之处,使用niosession,完美解决tcpsession以及datagRAMsession问题。
    2. 对于有中文的地方,无论是序列成流,还是反序列化,都需要进行编码设置。
    3. eclipse引用web工程

a) web工程的deployment assembly中将deploy修改为/,这样处理是为了打包的时候外面不需要套一个WEB-INF。

b) 主工程引用该web工程,保证开发时引用工程的类可见,在主工程的deploy assembly中add->project->选择web工程,默认是将工程打包成war,然后可以手工修改为jar,这保证了运行是类能被加载。

c) 切记要把被引用的工程要用到的jar包拷贝到主工程的lib下面

  1. 调错于其他
  1. 今天使用log4Net,每条记录都被打了两条,我的第一反应是代码执行了两边,但是其实是因为log4Net配置的问题,配置了两个logger对象(root以及一个自定义的logger对象);
  1. 状态,当初不应该图省事采用commanidentifier,应该重新定义一套定义,语义还是不太一样;
    1. 刚才看到一个问题,总控调用传输失败,看到的异常是初始化失败,后来才知道,传输是通过一个线程起来的,在初始化过程中报了异常,只是throw了,并没有记log,导致后续再调用传输类,报初始化错误,所以多线程起来的异常一定要记录,它抛出的异常并没有主线程来捕获;

posted on 2016-08-31 21:00  下士闻道  阅读(267)  评论(0编辑  收藏  举报

导航