失效链接处理 |
java开发面试必问 PDF 下载
本站整理下载:
相关截图:
主要内容:
(1)项目名称:优配良品网上商城
开发时间:2016/12-至今
项目描述:
项目框架:
后台: shiro + Spring + Mybatis + SpringMVC(基于注解) + Bootstrap + OScache + Solr + ActiviMQ(消息队列) + dubbo + zookeeper + Echarts + WebService
前台: Spring + Mybatis + SpringMVC(基于注解) + FreeMarker + Redis + Solr集群 + spring定时器 + jQquery
该项目主要有会员注册及登录、商品分类展示、商品信息检索、购物车、生成订单、订单查询、商品排行、反馈留言、商品类别管理、商品品牌展示、商品管理、会员管理、订单管理、新闻管理等。包括严格的权限管理部分,客服登录系统后可以对客户所下订单进行查询和取消备注等操作,在取消的同时会对客户所下订单进行积分,款项返还等操作。
开发环境:idea + tomcat7.0 + jdk1.7 + Maven + git
使用技术:SpringMVC4.0 + Spring3 + Mybatis + dubbo + zookeeper + ActiviMQ + Solr + Nginx + FreeMarker + ECharts
运行环境:linux + Tomcat + Mysql + Redis + MongoDB + JDK1.7
本人角色:高级程序员
责任描述:在项目中,我参与架构选型与搭建框架,技术攻关,并且负责后台权限管理、商品管理、报表统计(ECharts)、商品信息检索引擎搭建(Solr)及部分核心代码的编写(大数据量poi导出、shiro安全认证整合)。负责前台的产品详情页(FreeMarker,Redis)、购物车及订单支付(ActiviMQ + WebService)、产品评论(MongoDB)、支付成功的短信提醒、服务器搭建(Nginx负载均衡Tomcat7.0集群)等,后期还对项目进行优化和安全测试,在项目中担任高级工程师,并带了一个新人。
项目总体采用的是Dubbo+zookeeper的分布式框架,可以理解问拆分成了三个项目,前端项目、服务端项目和缓存项目,前端项目包含页面、静态资源和控制层;服务端项目包含业务层和数据库操作层;缓存项目缓存前端项目和服务端项目公用的数据。用户请求进入前先通过负载均衡设备进行请求分发,采用nginx的负载均衡实现动静分离,首先呢我们使用nginx+keepalived双机热备的方式来实现负载均衡调度器的高可用性,其中keepalived主要是解决了nginx的单点故障,用来检测服务器状态,如果一台web服务器挂了,keepalived将检测到,并将有故障的机器剔除掉,然后人工进行修复。通过nginx中server标记下添加location规则信息,将css,js这些动态资源交给nginx来处理,将动态请求通过proxy_pass
介绍下dubbo和zookeeper:
Duboo是一个分布式框架,zookeeper是duboo生产者暴露服务的注册中心。起一个调度和协调功能,当然注册中心也可以用redis 或者duboo自带的Multicast 来注册。
Duboo 通信方式采用长链接方式,所以当spring启动后链接就接通,duboo的消费者和生产者就可以直接调用。性能上高于其他http协议的请求。(httpclient数据属于短请求,一次请求,一次响应,dubbo通道一旦建立(一旦连接),一直处于联通状态)dubbo基于tcp/ip协议的,交互性能非常高,支持匿名传参,隐式传参,泛化调用(隐式传参:不是以括号的形势传参,将参数存储到当前请求里面,到了服务端再从请求里面拿出来,request不能传参。泛化调用:是指采用一个service接口和一个service实现类来实现多个方法的调用)当时是为了解决单个服务器站点的压力,将项目拆分成页面加controller属于消费者,service+dao属于生产者,所有生产者暴露的端口都注册在zookeeper里面。这时候,消费者要调用生产者去zookeeper中取就可以了。所以我们部署了多套生产者,所有的消费者的请求可以由多个生产者去提供,具体由哪个生产者提供可以由zookeeper的配置去决定。如果某个生产者挂掉,zookeeper会加压力导向其他生产者,当这个生产者恢复状态的时候。Zookeeper会重新启用它。因为我们用n的执行,如果想让单个tomcat执行的action---service—dao 请求又多个tomcat来执行就可以使用 zookeeper+duboo 这时候一般是一个tomcat里面部署的是jsp+action所有的service接口都注册到了zookeeper里面,action去zookeeper里面通过duboo去调用哪个 service+dao 的组合。而且service+dao的组合可以配置多套。一套挂了其他的service+dao组合可以继续使用
dubbo框架的体系结构有5个核心组成部分,分别是提供者provider,它的作用是为消费者提供数据。注册中心registry,它的作用是用来注册和发现服务。消费者consumer,它的作用是调用远程提供者提供的服务。监控中心Monitor用来统计服务的调用次数以及调用时间,还有container用来充当容器来加载,运行服务提供者。新建dubbo-provider.xml配置文件,通过dubbo:application配置提供者应用名,通过dubbo:registry配置注册中心的地址,通过dubbo:protocol配置协议,以及通过dubbo:service来暴露要发布的接口。
最后我们在需要使用dubbo接口的项目中配置消费者信息,新建dubbo-consumer.xml文件,通过dubbo:application配置消费者应用名,通过dubbo:registry指明要订阅的注册中心地址,通过dubbo:reference指定要订阅的服务接口。除此之外考虑到dubbo的健壮性和性能我们对它的参数项进行的调优。通过在dubbo:protocol中threadpool="fixed" threads="200"来启用线程池,通过在dubbo:service中connections=5来指定建立长连接的数量。
配置dubbo集群来提高健壮性以及可用性。dubbo默认的集群容错机制是Failover即失败自动切换,默认的重试次数为2,可以通过retries调整。dubbo默认的负载均衡策略是Random随机,可以按权重设置随机概率。我们在写完dubbo提供者之后,为了测试接口的正确性,我们会进行直连测试。首先会在提供者端,通过将dubbo:registry的register设置为false,使其只订阅服务而不注册现在正在开发的服务;
在消费者端,通过设置dubbo:reference的url,直连提供者进行测试。被动说:
所谓dubbo集群就是将dubbo的提供者部署多份,在不同的机器上或者说在同一台机器上用不同的端口号。从而在启动时可以向注册中心进行注册,这样结合dubbo的集群容错策略以及负载均衡策略可以提高可用性。
dubbo负载均衡策略:随机,轮询,最少活跃调用数。
dubbo的集群容错:失败自动切换,快速失败,失败安全。
dubbo+zookeerper怎样实现session共享(在消费端):
我们的消费者只有一个模块,所有的请求首先都是进入这个模块里面,生产者有多个模块,session都在消费者这个action里面,所有的请求都是首先进入这个项目里面,我们的生产者有多个模块。如果有多个消费者的情况下,会存在session共享问题,我们可以将session的id作为key值,用户对象作为value值存储到redis里面。当每次发送的请求的时候,拿着浏览器的session的id去redis里面取,如果能取到,证明用户已经存在,如果不能取到,就重新登录。
为什么用dubbo+zeekeeper?介绍一下?
实现了分布式部署,如果不用的话,传统的项目就是action>servcie>dao,有三部分的请求,单个儿tomcat,用了dubbo+zookeerper后,分成了消费者和生产者,划分成不同的模块,有几个模块就有几个tomcat,大大降低了tomcat的压力,而且后期随着访问量的增加,我们可以不断的增加生产者,每个单个web服务节点者所受到的压力明显降低。
生产者和消费者是怎么交互的?
dubbo首先向zookeerper暴露端口,消费者向它们这边订阅服务通过zookeerper,消费者发送请求,zookeerper用来调度,调度的方式有三种,轮循,随机,把这些请求都拿给生产者,如果其中一个生产者挂掉,如果有新的生产者,会把请求分发给新的生产者。
你看过dubbo底层吗?
生产者跟消费者之间相当于长连接长请求,传统的request请求,response响应,请求一次,响应一次,开关一次,dubbo是基于 tcp/ip协议的,其他的源码闲的时候,基本上通过maven来看过没有刻意的去了解,因为平时这个业务开发状况很繁琐。
zookeeper的作用:
dubbo是把项目实现分离,分为消费端跟服务端,在消费者这边的配置文件中添上zookeeper地址,让它连上zookeeper,每个服务端都暴露一个端口给zookeeper,这样就实现服务端在zookeeper中注册,消费者发送请求给zookeeper,zookeeper接收请求,zookeeper再分发给这些服务端。
项目安全性怎么解决?
是指攻击安全还是访问量过大安全还是数据安全?
如果是攻击性的话,input框都采用了js验证,用java代替重复效验,避免输入不必要的空格或者sql注入。
如果是访问的安全的话,页面做了大量的页面静态化,缓存,通常访问量不会增加服务器造成负担。
如果是数据安全这一块的话,系统用的是linux,数据库的账号密码都做了加密处理。
如果是数据遗失或是自然灾害,数据库做了读写分离,而且一个主库,两个从库,其中一个从库做了备份,而且是定时备份。
在登录过程中,为了防止程序直接登录,也做了一些验证码,包括支付的资金账户安全,我们的系统没有钱,主要是通过超链接将钱存储到网络支付宝qq账号,最主要还是支付宝的安全和网民的安全,而且这个账号只能出不能进。
购物车在redis中是怎么存的?
我们的购物车,用的是redis 来实现的。 当加入购物车的时候 用户id作为redis 的key,产品集合作为redis的value。商品存的是 ,商品id 商品名称,和商品购买数量。
当加入商品到购物车的时候,首先判断当前用户id对应的的产品集合里面是否含有当前产品,有则数量加一。没有则新添加该商品。
问静态化的商品详情页面,如果数据库价格变动,页面会重新生成吗?
答:不会重新生成。我们的价格和数量是来自redis缓存的。一般我们将商品更新到,redis中,key是表名加id例如 sm_product_proid , value是产品对象。存储价格数量。
问购物车的商品会存到redis里面,假如有一件衬衣卖68,有一千万人将其加入购物车,而第二天老板将数据库价格改成88了,那购物车里面的价格会变化吗?
答:会变化。我的购物车用户id做为key,value是产品集合,而且默认过期时间是7天,产品集合只存储产品id ,数量,名称。 当显示该商品的时候我需要用产品id 去其他redis 库里面获取价格。其他的redis库里面已经存储了所有产品id作为key,value是商品 价格数量。当老板变动数据库价格的时候我只需要更新,单独存储的这边redis产品。
solr搜索引擎:
在整个项目中实现商品搜索功能电商项目中,因为用户有时候不是多么清楚他所需要的东西的名称或者商店的名称,有时候仅仅是只知道他所需要的商品是干嘛用的,又或者是有多个要求,为了满足用户的诸多需求更准确的查询到用户所需要的商品,所以就用到了Solr搜索引擎
solr是一个基于Lucene的全文搜索服务器,相比Lucene更高效、使用更便捷,在进行模糊匹配的时候,他可以 用来替代数据库中的like ,从而在匹配准确性以及性能进行大幅度的提高。因为官方的分词器对中文支持不好所以使用了第三方的IK分词器来进行分词。在建立索引的时候我们通过在schema.xml配置IK分词器来完成中文分词。从而实现了高亮显示关键词,分页,排序,多字段,多条件的高性能搜索。
其实目前有很多优秀的中文分词组件 :像mmseg4j,IK Analyzer ,Paoding
通过查询资料发现用 IKAnalyzer比较好IK 比其他中文分词维护的勤快,和 Solr 集成也相对容易。
其中我们在配IK分词器的时候,当时遇到了三个问题:
分词器版本与solr服务版本的匹配:
当时用的solr是4.10.4版本的因为这个版本比较好用,稳定,5.以上的版本bug比较多,配IK分词器的2012FF_u1版本,开始时用的是u6,然后配完效果出不来,上网查了好多资料,又问了之前加的一个搜索引擎群,才知道是版本问题,u3,u5,u6的都不好用,只有u1的和solr4.10版本的合适,改完之后问题确实解决了
加入分词器的schema文件要把version改为1.5基本的CRUD操作都可以。
但是搜索却只能全字匹配或者只能单个字符匹配出结果。这是绝对不能容忍的。定位问题接近了一天,找有经验的同事给 排查也没排查出来问题。最后我自己一点一点比对multicore文件夹下的配置文件跟F:\solr\solr-4.6.0\example\solr \collection1这个文件夹下的配置文件的配置区别。当我把schema.xml的version属性从1.1升到跟collection1下的相同文件的1.5一致之后。重启服务器,问题解决了!、
分词器的词典编码格式为UTF-8无BOM格式
在从数据中取数据生成索引的时候,因为表中的数据量比较大,防止一次取出所导致内存溢出问题,我采用了分段批量提取的方式进行,
此外我们为了提高solr搜索的性能对其进行了主从配置。
1. 我们solr使用的是solr4.7版本
2. 通过修改schema.xml来添加要进行索引的字段以及增加ik分词器
3. 通过solrj将数据库中的数据生成solr中的索引文件,注:solrj是java程序调用solr服务所用的jar包。
4. 通过在solrconfig.xml中配置requestHandler name=“/Replication”来进行主从同步的配置,在从solr中通过masterUrl指明要从哪些主solr服务器中同步数据
我们为了提高用户体验度还使用了solr的spellCheck可以用于查询输入的自动完成功能auto-complete。他是基于动态代码方式建立内容,suggestion可通过读文件方式建立内容,并有点击率排序。使用facet在查询结果上根据分类添加了count信息, 然后用户根据count信息做进一步的查询, facet的主要好处就是可以任意对搜索条件进行组合, 避免无效搜索, 改善搜索体验.
购物车实现cookie+redis:
一、未登录状态下添加商品到购物车
在不登陆的情况下也可以添加购物车。把购物车信息写入cookie。
优点:
1、不占用服务端存储空间
2、用户体验好。
3、代码实现简单。
缺点:
1、cookie中保存的容量有限。最大4k
2、把购物车信息保存在cookie中,更换设备购物车信息不能同步
实现思路:
(1) 从cookie中获取商品列表信息(单独提出来写成个通用的方法)
(2) 遍历购物车列表,判断需要添加的商品在购物车列表是否存在
(3) 商品存在的话,那么取出该商品原来的数量+添加的数量作为该商品现在的数量
(4) 如果商品不存在,那么调用服务,根据传来的商品id查询商品数量,设置商品的数量为页面传来的数量,取商品的第一张图片(购物车列表只展示一张图片)。
(5) 把修改后的购物车列表重新存入到cookie中
(6) 返回逻辑视图”cartSuccess”
2、展示购物车列表
单击“去购物车结算按钮”向服务端发送请求,服务端应该返回逻辑视图”cart”
请求地址:8090/cart/cart.html
返回逻辑视图:”cart”也就是购物车列表页面
3、登录状态下购物车列表页面修改商品数量
购物车列表页面单击”+”,”-”会向服务端发送ajax请求。
页面需要根据调整的数量重新显示商品总计(已经实现了也就是输入框的值*价格)和小计(用js,待实现)
服务端要求修改cookie中对应商品的数量
4、未登录状态下删除购物车商品
(1)从cookie中获取购物车列表
(2)遍历,查找到要删除的商品
(3)将该商品从购物车列表移除
(4)更新后的购物车列表重新写入cookie
(5)重定向到购物车列表页面
|