作业-2020年06月09日-Web开发实战 09

项目属于课上作业迭代,请不要用于真实开发和调试。

本文非解决方案文章,仅作为记录作业使用,类似于写点心路历程。

作业日期:2020年6月09日,作业09

作业安排:如何进行优化的秒杀

代码详见github:github.com/hitofuncy

零、优化作业的特别说明

        在本篇中将详细讲述秒杀系统的具体优化的方法,不涉及到秒杀系统的实现。


一、秒杀系统优化之 页面缓存技术

基本思路:

        首先页面优化技术包括1. 页面缓存+URL缓存+对象缓存 2.页面静态化,前后端分离 3.静态资源优化 4.CDN优化

在项目中,我们在 list的controller中添加字段produces参数 ,接着修改方法为String返回的方法、建立一个GoodKey缓存存取的类作为页面的缓存,然后在controller中检查redis是否为空,如果没有空就取缓存,接着我们手动渲染,,使用Thymeleafview Resolver 进行渲染,引入这个自动装配,使用thumeleafviewResolver 的方法get末班endine进行渲染。为了能够实现手动渲染,就要使用一个SpringWebContext 创建一个对象,增加request respones 这两个参数,做好前期的工作了之后基本的渲染就完成了,接着就可以使用已经渲染的内容进行字符串的构建然后返回一个html文本,浏览器就可以进行正常显示。

        别忘了,在方法上添加@ResponseBody 要不然会无法显示。

易错的坑:

    我在做这个实验的时候,出现了浏览器乱码的情况,期初我以为是thy的问题,因为视频中的thy版本比较老,这样子的话我把版本换回老版本,接着就发现了老版本的情况更为糟糕,老版本的语法限定十分复杂,这样让我改了好一会儿,改好之后还是出现这个乱码情况,我上网查询了一下,期初以为这个问题是tomcat问题,于是我手动把spring的tomcat给更改了,发现不行,接着又调节的IDEA这个字节编码还是不行,在这样的情况下,我甚至检查了html本身的字节编码格式和保存的格式,依然没有解决问题,然后我就看到了一个在google上的解决方案的:

@RequestMapping(value="/to_list", produces="text/html;charset=UTF-8")

这样问题就莫名其妙的解决了,你说好不好玩,改了我一整天。


二、URL缓存以及对象缓存 -- 更细粒度缓存

     这个技术就是对象进行缓存,我们不可可能全部页面都缓存,这次的对象缓存很不一样,我们做分布式的时候做了一个token 我们先改造一下这个方法。先写一个取缓存,我们使用redisService进行取缓存,如果取出的数据是空的,我们就去数据库查询,如我们数据库中存在,那么我们更新放入redis中,最后返回一个user.下次访问的时候就从redis中取就快了。

    例如我们还存在一个更新密码的方法 参数是id passwordNew ,那我们就需要先取一下id对应的user对象,我们如果没有获取不到,说明了用户不存在,如果取到之后,我们创建一个新的对象,复制源对象进去,然后进行修改密码,修改密码时使用MD5加密,然后使用dao层的update进行更新。一般为了速度,一般不直接修改提交。更新完数据库之后就要更新缓存,我们可以更新掉redisService。建立delete方法,接着修改逻辑完成删除redis的方法。删除MiaoshAUserKey  更新MiaoshaUserKey,然后设置一下user的密码。总体的说就是更新数据库-》更新缓存。其中Dao层的方法可以直接创建以后自行编写@Update。

主要的方法如下:

        public MiaoshaUser getById(long id) {

		//取缓存
		MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, ""+id, MiaoshaUser.class);
		if(user != null) {
			return user;
		}
		//取数据库
		user = miaoshaUserDao.getById(id);
		if(user != null) {
			redisService.set(MiaoshaUserKey.getById, ""+id, user);
		}
		return user;
	}

	public boolean updatePassword(String token, long id, String formPass) {
		//取user
		MiaoshaUser user = getById(id);
		if(user == null) {
			throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
		}
		//更新数据库
		MiaoshaUser toBeUpdate = new MiaoshaUser();
		toBeUpdate.setId(id);
		toBeUpdate.setPassword(MD5Util.formPassToDBPass(formPass, user.getSalt()));
		miaoshaUserDao.update(toBeUpdate);
		//处理缓存
		redisService.delete(MiaoshaUserKey.getById, ""+id);
		user.setPassword(toBeUpdate.getPassword());
		redisService.set(MiaoshaUserKey.token, token, user);
		return true;
	}

注意:千万不要调用别人的DAO,所有的操作在service中进行操作。别人可能是有缓存的,不能去覆盖别人的数据。


三、页面静态化:

        页面静态化常用技术是AngularJS、Vue.js 这两项技术。

        优点:利用浏览器的缓存  

项目解析部署:

        在项目中,我们看上一次更新密码的方法,必须先更新数据库再更新缓存,不能颠倒,因为修改缓存一旦数据库没有更新成功,那么就会造成数据库的密码不改变,每次是先验证缓存再数据库的,如果缓存中有错误的数据,那么这个数据可能用于攻击,或是在另外的分布式的服务器上用更新的密码无法访问账号,因为在那个redis上没有缓存更新之后的密码。数据不一致。

       页面静态化在项目中部署:我们挑选详情页controller ,删除取缓存,手动渲染也需要,获取商品的详情,我们得编写一个网页面传值的VO  ,这个VO里存着状态、remainSeconds、商品信息也放进去、user。接着我们new一个VO对象,我们set填写相关的内容,修改方法为Result<>返回,最后返回Result.success(data);

        接着改造我们的页面,我们修改商品详情页面的html第33行的 的href中的值修改为静态页面,然后传入goodsid就行了。我们先为了用户建立一个usertip ,商品名称、id号都建立。建立秒杀开始时间id,秒杀Tip,在表单中存在一个按钮,创建一个goodsid 隐藏的输入框,在秒杀价下创建一个td id=goodsPrice 秒杀价格下建立miaoshaPrice、库存数量也建立id。我们编写function 获取detail方法,编写阿贾克斯的方法获取如下:

function getDetail(){
	var goodsId = g_getQueryString("goodsId");
	$.ajax({
		url:"/goods/detail/"+goodsId,
		type:"GET",
		success:function(data){
			if(data.code == 0){
				render(data.data);
			}else{
				layer.msg(data.msg);
			}
		},
		error:function(){
			layer.msg("客户端请求有误");
		}
	});
}

接着编写一些获取参数的:

//设定时间格式化函数,使用new Date().format("yyyyMMddhhmmss");
Date.prototype.format = function (format) {
	var args = {
		"M+": this.getMonth() + 1,
		"d+": this.getDate(),
		"h+": this.getHours(),
		"m+": this.getMinutes(),
		"s+": this.getSeconds(),
	};
	if (/(y+)/.test(format))
		format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
	for (var i in args) {
		var n = args[i];
		if (new RegExp("(" + i + ")").test(format))
			format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? n : ("00" + n).substr(("" + n).length));
	}
	return format;
};

继续在获取Datail方法中编写一个变量获取一个userid,接着填写get参数,接着编写成功和失败的处理,错误则打出消息--客户端请求有误,如果正确就回调,如果code == 0 就是成功的,失败则返回消息,接着我们编写出另一个function方法render 的会调方法,详情见以下:

function render(detail){
	var miaoshaStatus = detail.miaoshaStatus;
	var  remainSeconds = detail.remainSeconds;
	var goods = detail.goods;
	var user = detail.user;
	if(user){
		$("#userTip").hide();
	}
	$("#goodsName").text(goods.goodsName);
	$("#goodsImg").attr("src", goods.goodsImg);
	$("#startTime").text(new Date(goods.startDate).format("yyyy-MM-dd hh:mm:ss"));
	$("#remainSeconds").val(remainSeconds);
	$("#goodsId").val(goods.id);
	$("#goodsPrice").text(goods.goodsPrice);
	$("#miaoshaPrice").text(goods.miaoshaPrice);
	$("#stockCount").text(goods.stockCount);
	countDown();
}

引入数据就可以手动填充我们的页面,就如以上填充,接着调用countDown 方法就行了。

坑点:秒杀时间的判断遗漏

        这个解决方案就是在countDown方法下新增一个字段$( #miaoshaTip ).html("秒杀倒计时" +   秒);


四、秒杀静态化

        同样的,需要对调用的网页进行静态化处理,这里就不多说了,内容大致一样。

        修改好页面之后,我们修改@RequestMapping("/do_miaosha")这个controller ,修改的方法同样的是把这个方法变成橙一个返回result.sucess的一个方法,添加@ResponeBody这个标记,修改方法使用post。为什么用post?post是get是幂等 Post是向后端提交的数据。

 @RequestMapping(value="/do_miaosha", method=RequestMethod.POST)
    @ResponseBody
    public Result miaosha(Model model,MiaoshaUser user,
    		@RequestParam("goodsId")long goodsId) {
    	model.addAttribute("user", user);
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	//判断库存
    	GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);//10个商品,req1 req2
    	int stock = goods.getStockCount();
    	if(stock <= 0) {
    		return Result.error(CodeMsg.MIAO_SHA_OVER);
    	}
    	//判断是否已经秒杀到了
    	MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
    	if(order != null) {
    		return Result.error(CodeMsg.REPEATE_MIAOSHA);
    	}
    	//减库存 下订单 写入秒杀订单
    	OrderInfo orderInfo = miaoshaService.miaosha(user, goods);
        return Result.success(orderInfo);
    }

接着编写html页面添加阿贾克斯的请求获取,如上一个方法一样,这样就可以获得相关的静态页面的要求,这里就不做过多的赘述。


五、订单详情静态化:跟之前的套路也是一样的套路。

建立OrderDetailVO 编写OrderController  添加一个返回Result < MiaoshaUser>的info方法。

具体见下方:

 @RequestMapping("/detail")
    @ResponseBody
    public Result info(Model model, MiaoshaUser user,
									  @RequestParam("orderId") long orderId) {
    	if(user == null) {
    		return Result.error(CodeMsg.SESSION_ERROR);
    	}
    	OrderInfo order = orderService.getOrderById(orderId);
    	if(order == null) {
    		return Result.error(CodeMsg.ORDER_NOT_EXIST);
    	}
    	long goodsId = order.getGoodsId();
    	GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
    	OrderDetailVo vo = new OrderDetailVo();
    	vo.setOrder(order);
    	vo.setGoods(goods);
    	return Result.success(vo);
    }

接着在CodeMsg.java添加订单模块相关的错误码。编写Dao层返回商品数量、页面相关的信息请求完善,创建OrderService层中通过orderid返回OrderInfo信息的方法和getMiaoshaOrderByUserIdGoodsId方法。

经过一系列的调试之后就可以实现了。


六、静态资源优化

        静态资源优化包括1. JS/CSS压缩,减少流量    2.多个JS/CSS组合,减少连接数    3. CDN就近访问

 

 

以上。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(课上区域)