15年8月,离开原有公司,开始了跟几个并不合适创业的人一起创业,个中酸甜苦辣,难为人知。创业方向是城际定制化出行,我整体负责技术相关,项目从单一的系统满足唯一需求,慢慢演化到了多个系统之间的协作,也开始引入了一些中间件.在这里记录下,也算是补充下自己的知识库.
最初
刚开始诉求很简单,就只是单系统,然后提供一系列简单的http restful接口给APP端,所以采用了最基础一些框架配置:
配置文件:
-
WEB-INF下的配置,除了web.xml外,就只有一个springmvc对应的servlet.xml
-
resources下的配置文件,根据功能拆分了spring相关的配置(先暂时忽略ini及properties文件哈,后续会有说明).
web.xml
主要配置了spring及springmvc相关信息,如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring.xml</param-value>
</context-param>
<!-- Reads request input using UTF-8 encoding -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc-servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
spring
核心框架spring,用于管理所有的bean及其之间的注入,并处理与数据库的事务(使用注解).
最初采用spring2.5.6,比较老也比较稳定的版本.
事务采用@Transactional注解来完成,避免配置一堆xml文件.
在spring配置文件中配置扫描路径:
<context:component-scan base-package="com.yxhl"/>
springmvc
对外提供的接口采用springmvc,一方面是与spring的无缝集成,另一方面确实很简单,提供了注解,也支持json等数据格式的自动映射及处理.
springmvc配置比较简单,只需要一个-servlet.xml即可,也支持controller及方法级别的注解.servlet.xml文件如上图所示,放在web.xml相同路径下。
不过需要注意的是,这里的注解扫描包的设置会与上面spring注解扫描包的配置产生冲突,造成事务不生效的情况,所以需要排除事务控制层。在servlet.xml中配置如下:
<context:component-scan base-package="com.yxhl">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
也就是排除对@Service注解文件的扫描。相应的,事务控制层的bean,全都使用@Service来标注。框架本身就支持集成jackson,不需要自己处理json与model的转换,这个相当方便。
jackson框架支持Model的注解,对于不需要转换的属性及空值属性,均可通过注解来完成,另外jackson在处理json序列化与反序列化时的性能很好,推荐使用.
持久化层
ORM层采用ibatis,sql可以自己控制,也相对灵活.数据库连接池采用了bonecp.
- 由于ibatis默认的配置就开启了缓存,其他的一些配置其实也并不需要,故省掉了ibatis的配置文件,以及dao层,直接使用mapper.xml与对应的mapper.java来实现与DB结构的映射.(注意mapper.xml与Mapper.java一定要在相同的包名下,而且要相同名称,比如Mapper.java所在路径:
mapper.xml所在路径
)
对应的spring配置如下:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.yxhl.persistence.mapper"/>
</bean>bonecp号称最快的数据库连接池,当然后面发现其实并不然,且存在一些其他问题,而且很早之前就停止更新了,这个后面详细说下。数据库连接池的配置网上一堆,这里就不贴出来了,O(∩_∩)O~
数据库用了免费强大的mysql V5.6版本,UTF-8字符集,innoDB引擎(支持事务,且只锁行,而不像myIsam不支持事务,且会锁整张表,而且也不支持外键;关于innoDB和myIsam的对比可以参考这里:http://www.dedecms.com/knowledge/data-base/mysql/2012/0819/7217.html ),单库存储.
其他
- 与APP端的交互采用json数据格式,无论是客户端,还是服务端,或者原生支持,或者存在比较好的第三方框架.在spring-servlet.xml中配置如下:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html; charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean> - 部分数据的处理(比如订单未付款超时)采用基于quartz1.8.6框架的定时任务.
- 使用logback日志框架,log4j作者的新作,性能更好,并发场景下做了不少日志相关的优化。关于logback和log4j的对比可以参考这里:http://www.cnblogs.com/james1207/archive/2013/09/15/3323106.html
- 基于maven3做依赖管理.Git做代码版本控制.(coding.net还是不错的,推荐下,国内基本上没找到更易用的)
- 基于Ubuntu14.04-PC搭建了一个测试服务器,线上采用2台阿里云服务器(CentOS6.3).
- 使用nginx1.4.6做负载均衡及端口反向代理,后端采用双tomcat7做Web服务器.
第一个版本基本上就是如此,两个人快速开发,耗时一个半月,加上与客户端的调试时间,在双十一前后正式上线,慢慢的客户端开始推广,也开始有些数据过来,初始版本算是基本满足需要。
慢慢的发现第一版是存在一些问题的,包含代码层面和环境问题:
- CentOS自带了openJDK,Ubuntu则是安装了JDK1.7,部分功能在测试环境OK,部署到线上就存在问题.
- 代码全都在同一个系统中,业务发展要求有一个管理后台,包含部分调度相关的处理,不管是管理后台还是前端接口,哪个部分发生变动都会影响到另一端.
- 某些场景下事务回滚失败,造成了部分脏数据。
- 定时任务的代码同时在线上两台服务器上运行,本身就造成了并发导致的脏数据.
- 数据库一直是单库操作,无备份,中间出现一次DB所在服务器宕机的情况,造成将近1个小时的服务不可用,对数据安全的要求提上了日程.
- 部分接口需要APP端频繁轮询调用,对用户手机的资源占用和后台HTTP接口都是一种并不友好的处理方式.
针对以上问题,产生了项目2.0版本,主要改动如下:
- 统一设置线上及测试环境下的JDK为1.8版本,废弃OpenJDK.
- 使用nexus搭建maven私服。
- @Transactional注解设置rollback条件,并针对业务异常做回滚操作.另外注意,此注解在非public方法上并不生效.
- 做系统拆分,将原有系统拆分为management及platform两个系统,并有一个独立存在的common模块(后又合并到platform系统中),用于承载数据库domain及mapper文件,以及一些工具类和model
- quartz加入集群配置,数据库加入qrtz库并设置定时任务的集群配置.
详细的改动会在下面的章节一一给出。