1 汇通国际物流项目介绍

汇通国际商贸公司是一家经营玻璃器皿的贸易公司,由于最近业务的发展以及规模的不断扩大导致现有的系统不能满足公司的发展,这样他们决定做这么一个平台我们这个项目采用的框架是 ssh 架构,数据库是选择了 Oracle我们采用现在比较流行的 maven 项目管理工具,来构建和管理项目(maven 有不同的项目骨架(maven 建立项目目录结构,包括 java 和 test 可以有效的讲 java 代码跟测试代码进行分割),可以进行项目的一键构建,可以有效的对 jar 包进行管理通过配置文件来对 jar 包进行管理可以从网上直接下载不用自己再去找了(主要是编写 pom 文件,写入需要的 jar 的坐标 maven 可以直接从中央仓库中下载,common 项目是单独封装工具类,方便其他项目使用),他也集成了 tomcat 组件,操作起来更方便,然后 maven 也可以做项目的继承和聚合(说明详细一点,建立 parent 项目,这个项目没有业务处理只负责 jar 包的依赖和关联和版本管理)
我们建立了一个父项目用来管理 jar 包,然后有建立了很多子项目包括订单,前台系统,后台系统然后父子关联,这个时候我们的jar 包管理就很方便,还用到了了项目的聚合,jt 做后台的时候进行了水平切分,切分成了 po 层,server 层 mapper 层,controller 层和还有前端,这样切分以后人员配合开发起来的话效率会比较好)前期在项目组中我主要负责需求调研、部分设计、核心业务开发。
项目组成立后,我跟着项目经理去客户公司进行实地调研(主要是开会,形成需求文档)。我还做的部分模块的设计《详细设计说明书》UML 图(用例图、类图、序列图、状态图)我做了些简单的模块的设计。生产厂家的类图,货运管理用例图。)

2 权限安全框架 Shiro

项目开发我主要负责:系统管理和货运管理模块(货运管理模块是核心模块)其中系统管理,主要的内容就是部门管理,用户管理,登录验证和权限控制,我们的登录验证和权限管理是用 shiro 框架做得。(Shiro 是阿帕奇出得一个安全框架,它与 spring security 比较我们的项目在 注册,验证登录,授权用到了 shiro 框架注册的时候主要用到了自定义加密算法 Shiro md5hash 算法(先说明 md5 的不安全),他得好处是不容易被暴力破解,他得实现原理是加盐(这个盐的设计是可以自定义的,比如可以用用户名),而且可以混淆多次,这个次数可以配置的,默认的是加密两次,这样在不容易被暴力破解。登录验证的时候,他会自动回调我们的自定义 realm,他调用了底层的 aop 到数据库查询,shiro 获取数据库中对应的用户信息,将它和密码传递给验证方法,验证的方法去调用自定义的密码校验。通过对比将用户录入的密码(默认用加密算法处理明文密码)加密后和数据库的密码匹配。这样做得好处是可以防止 sql 注入攻击。Shiro 也可以登录验证,传统的方式是直接按用户名密码到数据库查询,这种方式容易造成 sql 注入,我们将他改成按用户名来查获取到用户的密码,跟前台传入的密码进行加密后比较。Shiro 也是采用了这种方式,但是他比传统的方式更加强大,他引入了很多更好的机制,其中包括自定义的 realm,和自定义的加密算法。用户登录的时候 shiro 会进行拦截调用,将用户名密码封装成 usernamepasswordtoken,在我们调用 subject.login方法的时候将他的信息传递给我们自定义的 reaml 的认证方法。这个认证方法当中可以调用我们 spring 框架中的业务层的方法,查询数据库获取当前用户的信息,从用户信息中取出用户的密码,这个密码是经过加密的,与我们从 usernamepasswordtoken 当中获取的password 并调用我们自定义的加密算法以后进行比较。如果相同放行。转向欢迎页面,如果不同转向登录页面。授权,shiro 提供了一些自定义标签,对我们要控制的这些标题啊或者链接啊我们都要加 shiro 标签,这时候当我们解析页面的时候遇到 shiro 标签,就会调用后台的我们的一个自定义 realm,通过这个 realm 去后台数据库获取给当前用户所分配的权限,然后我们将这个权限写入到一个 list 中,shiro 标签中会定义权限的名称,如果这个名称在 list中放行,不存在就不放行,在页面中不展现标签中的 html 代码(用户在每次登录的时候都需要去查询当前用户的权限信息放入 list 中这样效率很低,为了提高效率我引入了ehcache 这样查询效率会很高,不用每次都到数据库中查询(shiro 天然集成了 Ehcache缓存)中,不用每次都查询))(ehcache 中的数据会在用户注销或者重新登录的时候更新)

3 货运管理核心业务模块

购销合同
客户下订单后汇通公司与生产厂家去签订购销合同,购销合同的内容主要由三部分组成,
购销合同的主信息,和多个货物的信息,和多个附件的信息。(附件实际就是货物)
包括出口报运单
货物在出口的时候需要向海关报关。一个出口报运包括多个合同的信息和合同下的货物
信息构成,货物信息中还要多个信息。包括毛重、净重、长、宽、高、出口单价、含税等等。
购销合同和出口报运单,这两个报表比较复杂,以前都是他们的业务人员自己制作的,现在
是需要通过程序自动打印,因为他们需要 excel 表,所以我们用了阿帕奇的 POI(与 jxl
进行比较他比简单,它只能操作 excel2003 版本的,这样对数据的量有严格限制数据最多
到 65536 条数据,相比来说 POI 它可以操作 office 的所有产品,例如 excel word ppt
等等,同时它也支持 excel 2010)来解决这问题,(采用了模板开发提高了效率问题,模
板好处,POI 有缺陷在打印的时候每一个单元格有样式变化都需要但需要单独创建,代码量
特别大不易维护我们使用模板开发,实现定义很多样式这样打印的时候可以读取单元格中事
先定义好的样式,这样开发的效率就非常高了,当样式改变的时候只需要改变模板不需要改
代码了)
为了方便领导的决策制定,和公司的发展的,甲方要求我们制作一个厂家出货情况的图
形报表,还有公司的月销售额的同比环比曲线图,我们开始用的是 jFreeChar
由于用户觉得台丑了,后来我们经过研究用了 amCharts(很多大公司都在用,微软,红牛
等等),amCharts 是一个比较成熟的图形报表组件,基于 Flash 所以很精美,我们只需要
通过查询数据库获取数据,并组织成 xml 文件,它可以自动解析 xml 来生成图形报表

4 货物跟踪 webservice

随着系统使用的时间增加,购销合同信息线性增长。数据库表中的记录越来越多。系统购
销合同模块会越用越慢。我们优化法案是进行分表处理,建立历史表,进行手动批量归档
(为了提高批量归档执行的效率。我们没有采用 mybatis,而是直接采用了 spring 的
JDBCtemplate,直接批量执行 6 条 sql 语句)
客户下了订单以后,由于报关,装箱,装船,等流程比较繁琐,时间比较长,客户要
实时追踪货物情况。客户的系统比较复杂异构(有 php 的 .net 的)的直接接入我们的系
统进行查询不能的,所以我们考虑创建一个 WebService 服务,来向客户提供进度查询服务,
我们采用的技术是阿帕奇的 apacheCXF(开始采用 jdk 直接提供的 webService 执行命令
wsimport 来生成客户端调用代码,这种方式比较笨拙功能太弱,原生态的 webServic 只
支持 SOAP 协议,我们最终选用了 ApacheCXF 他封装了 JDK 提供的 webService 操作更
加方便,功能更加强大,出了支持 SOAP1.1 和 SOAP1.2 还支持 SMTP 协议等等)
1 首先创建 webService 服务 采用注解方式标注该类为 webService 类
2 公开我们得业务调用方法,在 webService 中调用 spring 中的业务方法,这样实现
客户不能直接访问业务层方法。只能通过 webServcie 调用我们公开的方法,保证了
业务系统信息的安全。
3 在我们得系统中,CXF 是跟 spring 进行整合是通过 CXF 的 servlet,在系统加载的
时候来创建 CXF 的上下文,使之跟 spring 框架的上下文进行绑定,他们之间就可以
互相访问
4 客户可以通过我们发布的 URL 实际上是 WDSL 的说明书,在客户的系统中发出 SOAP
XML 请求,在请求中包含他的请求参数,调用我们系统中的 webService 接口,我们
系统中查询数据 返回 SOAP 协议 XML 响应,在返回响应中包含客户想要获取的信息。
他的系统只需要解析 SOAP 的内容在页面上展示。

5 中环电商项目介绍

我们的这个项目,采用时下比较流行的 SSM 框架来搭建的。
前端呢采用 nginx(由于他是由 C 语言开发的所以速度比较快)技术实现负载均衡
采用时下比较流行的分布式架构,把相关的功能部署到不同的服务器上,这样就是分散了单
一服务器大压力
数据库的话,我们采用了 mysql
DAO 层我们引入了通用 mapper,和基于 mybatis 拦截器的通用分页组件
传统的 mybatis 需要在映射文件中写 sql,由于业务的不同造成 sql 语句不同,而无
法造成通用,开发人员工作量巨大,也不易维护。通用 mapper 可以自动生成增删改查等一
系列常用的 sql 语句,省去了 mybatis 的大量映射文件的配置

6 redis 缓存

我主要负责的有
1)商品管理的开发以及商品详情页开发
由于项目的访问量非常大,频繁查询商品信息,为了提高他的查询效率,我们引入了缓
存技术redis 和 memcache,oscache,ehcache 的区别?
它们都是主流的缓存框架。redis 底层实现是单线程的。memcache 底层是多线程实现
的。单纯从缓存功能讲是 memcache 高,从功能讲是 redis 高。Memcache 只有 string 类
型。Redis 可以存放 string、list、hash、set、sortset。所以就综合能力来讲 redis
高。在项目中根据它们不同的优缺点结合使用。Redis 中的数据可以持久化
其次是radis支持分布式,我们的系统就配置了三台radis服务器,radis内部是通过hash
一致性来决定数据存放在具体哪个服务器上,所以对于开发人员这个集群是透明的无需关心
数据存在那一台服务器上,读和取都是一台服务器上
用到缓存就涉及到一个数据更新的问题,后台修改了商品信息不能即使在页面上进行展示,
开始我们用的 httpClient,利用 spring 的 quartz()定时调用前台方法来删除缓存中相
关产品信息,但是甲方业务人员觉得更新的时间间隔过长。再有大量用户请求同一时间访问
时,httpClient 方式会造成用户的请求无法响应甚至会造成用户请求丢失,还有
httpClient 它是个紧耦合的结构,当代码需要修改时候维护不方便。我经过跟项目经理的
讨论决定使用 RabbitMQ 的消息队列来决绝这个问题,首先在后台修改商品信息的同时在
MQ 中加入一条记录,设置一个 updata 的 key 放入 itemID 的一个字段带表更新了,MQ 的
路由器会根据 key 值绑定不同的消息队列,在前台设置一个监控程序来监控这个消息队列,
发现修改会立刻把该商品从 radis 缓存中删除。这样就实现了商品跟新的实时性

7 SSO 单点登录

sso(包括用的登录,验证,注册)
电商系统比较庞杂,包括后台子系统、商品的管理、CMS 内容管理、订单子系统、购物车子系统、前台子系统。这样就会造成一个问题,我们系统之间访问时候就产生了一个session 共享问题。
分布部署时,有多个 tomcat,假设用户从 tomcat1 登录,那它的登录信息是记录在tomcat1 上的。如果用户请求业务,有可能就被分发到 tomcat2 上,这时 tomcat2 上并未登录,这时应该如何处理呢?我们要做到,不管用户在哪个 tomcat 上登录。
京东就是用了很多二级域名和不同的 ip 的 web 服务器来部署不同的服务,他就是做到了一次登录不同的域名和 ip 下可以不用再登录。
如何保证两次访问拿到的是同一个 session 呢?
用户登录时将用户的信息保存到 radis 缓存中,这样各个子系统就不需要将用户信息放到 session 中,而是统一到 radis 缓存中获取。
我们的单点登录服务提供接口,外界是通过 RESTFul 来请求服务的,登录服务的接口就是一个用户名一个密码。
在用户登录时候,我们会先经过验证,如果验证通过,我们将 User 对象 ,用用户 ID加上时间的毫秒值作为 key:ticket 存入 radis 缓存,并返回 ticket,同时登录端将ticket 值写入 cookie,为了防止 ticket 被恶意获取,我们对他进行了 MD5Hash 进行加密
然后会有一个拦截器,会对 url 进行过滤,如果有需要登录操作的 url,通过 cookie中的值,以此作为 key 值去缓存中查找,这样就完成了多个 web 服务器的免登录,当然可以通过对缓存设置,来设置 ticket 的失效时间,因为 cookie 跟浏览器相关,cookie 信息是保存在本地的。跟后台访问到哪个 tomcat是没有关系的。就形成了一个单点登录系统。

8 订单系统 RabbitMQ

订单系统是一个独立的子系统,对外提供接口,可以通过这些接口来操作订单。例如:
用户要下单,用户在前台系统点购物车,点击立即购买。一点击,它会请求后台的controller,后台的 controller 会调用 service,service 就会调用订单系统的接口。去下单。下单成功就返回一些数据。最后就提示用户下单成功。
前台子系统是通过 httpClient 的方式来访问订单系统的接口,使用这种方式有两个严重的问题,第一会造成用户的请求无法响应甚至会造成用户请求丢失,第二会造成代码的耦合性过高,不利于后期代码维护。我经过跟项目经理的讨论决定在项目中引入队列的方式,现在主流消息队列有 ActiveMQ 和 RabbitMQ 因为我们得框架主要采用了 spring 的集成,因为 rabbitMQ 跟 spring 直接有插件支持所以我们项目中采用了 rabbitMQ,MQ 使用了队列这种数据结构,将用户的请求存放在队列中这样就不会造成用户请求的丢失,这样的好处就是代码的耦合性降低了,利于维护。

9 商品查询模块(全文检索)

我们的商品查询模块也是采用了单独系统向外界提供接口来提供服务的,我们的查询模块采用的是 solr 服务,我们也有单独的 solr 服务器,我们采用的中文分词组件式IKAnalyZer(我们可以自己设置停止词,对一些敏感词进行屏蔽)
我们传统的检索方式是直接到数据库的方式,采用的是 like 模糊查询,这种方式有缺陷会造数据库表中字段的索引时效,造成查询数据库的时候进行全表检索,这样当数据量达到几十万上百万的时候检索速度会相当的慢。我们使用 lucene 全文检索的方式他改变了数据的这种关系型的方式,而是直接是对每个字段进行倒排索引。这样在查询的时候就可以利用这种索引进行高效查询。
我们在系统中你没有直接使用 lucene,而使用的是 solr。Solr 实际上是基于lucene 基础上进行了一次封装。但是 solr 远比 lucene 强大。它实现了 lucene 的集群。而且将 lucece 中的复杂 API 进行了封装,开发起来更加简洁方便。
我们在商品检索的时候就直接到 solr 中检索,但是商品信息在后台系统中获取,我们需要先将后台商品信息建立索引存入 solr 中。当时测试时我们得商品信息有 1 万多条数据我们调用 solr 的 API 可以直接将 java 对象存入到 solr 中,大概时间是半分钟。事先我们配置好了schema.xml文件,在这里我们对solr进行了优化,在这个文件中我们将ITEM类的字段以 field 的形式存入 solr,并对其中的商品详情,买点,title 等字段做分词处理建立索引,对其他字段不进行分词整体建立索引,比如价格和数量等。在前台查询时,通过httpClient 访问 solr 服务器,solr 将查询结果以 json 串的形式返回.前台经过转换就可使展示了。使用 solr 时候还有一个问题,就是在服务器启动的时候需要设置 solr 服务的内存大小,防止数据过多造成 solr 崩溃。

10 MYSQL 读写分离,主从复制

在我们得系统当中,我们使用 nginx 负载均衡提高用户的访问量将用户的请求转向
到各个子系统。在前台系统当中我们使用了 redis 缓存极大的提高用户的访问效率,在个
子系统中间我们采用松耦合的 rabbitMQ 消息队列来传递信息,使我们的系统框架已经可以
满足高并发的要求。但是在我们后端数据库访问的时候一台 mysql 数据库已经成为了系统
访问的瓶颈。
一旦 mysql 的服务出现问题例如数据库查询过多造成数据库响应缓慢,我们决定采
用读写分离的方式。这是我们采用了 springAOP 的面向切面的方式自定义了一个切面,在
我们所有数据库操作时先被我们得切面拦截,在切面当中我们实现了动态切换数据源,将读
写的操作进行分离,写操作配置了一个数据源,有配置了两个读的数据源因为读的操作远远
大于写的操作。一般读的操作占整个操作的 80%-90%。我们利用面向切面可以获取到访问
数据库请求的方法名,通过方法名判断他是读操作还是写操作。我们是有一个方法名的约定,
例如 query select get 操作都是查询操作 ,insert update delete 操作都是写操作。
这养就可以通过方法名转向到数据源。(两台从 利用 hash 一致性来判断写在那一台)。
如果访问量再大的话会造成 mysql 宕机无法为用户提供服务,这将造成无法挽回的
后果甚至重大的经济损失。这时候我们采用了主从复制的方式,我们搭建了三台服务器的集
群,将一台服务器作为主,两台服务器作为从。(我们在主服务器上配置打开写二进制文件,
这样才能实现主从复制,在从服务器上执行绑定的 sql 语句,指定主服务器的 IP,端口,
用户名,密码,日志文件名称,位置,启动我们得从服务,如果配置成功,在主服务器的
mysql 上增加数据库,增加表,修改表结构,增加数据,修改数据,这时候从数据库的数据
都会与主数据库同步)
这样当主服务器宕机时候,这时候可以将数据源切换到某一个从服务器将他的配置
改为主(先停止当前从的复制服务再开启二进制文件写操作,然后将另外一个从绑定到这个
新的主服务器上,改变数据源连接指向这个新的主服务器,重启服务)