个人是首先通过在“多备份”注册一个账号,把Discuz备份好后,在后台绑定到七牛云存储上面,这样不担心数据丢失了
高并发问题
就是指在同一个时间点,有大量用户同时访问URL地址,比如淘宝双11都会产生高并发。
高并发带来的后果
服务端 导致站点服务器、DB服务器资源被占满崩溃。 数据的存储和更新结果和理想的设计不一致。用户角度 尼玛,网站这么卡,刷新了还这样,垃圾网站,不玩了二:分析阻碍服务速度的原因1:事物行级锁的等待
java的事务管理机制会限制在一次commit之前,下一个用户线程是无法获得锁的,只能等待
2:网络延迟
3:JAVA的自动回收机制(GC)
三:处理高并发的常见方法
1:首先可以将静态资源放入CDN中,减少后端服务器的访问
2:访问数据使用Redis进行缓存
3:使用Negix实现负载均衡
4:数据库集群与库表散列
四:实战优化秒杀系统
1:分析原因
当用户在想秒杀时,秒杀时间未到,用户可能会一直刷新页面,获取系统时间和资源(A:此时会一直访问服务器),当时间到了,大量用户同时获取秒杀接口API(B),获取API之后执行秒杀(C),指令传输到各地服务器,服务器执行再将传递到中央数据库执行(D),服务器启用事务执行减库存操作,在服务器端JAVA执行过程中,可能因为JAVA的自动回收机制,还需要一部分时间回收内存(E)。
2:优化思路:
面对上面分析可能会影响的过程,我们可以进行如下优化
A:我们可以将一些静态的资源放到CDN上,这样可以减少对系统服务器的请求
B:对于暴露秒杀接口,这种动态的无法放到CDN上,我们可以采用Redis进行缓存
request——>Redis——>MySQL
C:数据库操作,对于MYSQL的执行速度大约可以达到1秒钟40000次,影响速度的还是因为行级锁,我们应尽可能减少行级锁持有时间。
DE:对于数据库来说操作可以说是相当快了,我们可以将指令放到MYSQL数据库上去执行,减少网络延迟以及服务器GC的时间。
3:具体实现
3.1:使用Redis进行缓存
引入redis访问客户端Jedis
1 <!-- redis客户端:Jedis -->2 <dependency>3 <groupId>redis.clients</groupId>4 <artifactId>jedis</artifactId>5 <version>2.7.3</version>6 </dependency>优化暴露秒杀接口:对于SecviceImpl 中 exportSeckillUrl 方法的优化,伪代码如下
get from cache //首先我们要从Redis中获取需要暴露的URL
if null //如果从Redis中获取的为空
get db //那么我们就访问MYSQL数据库进行获取
put cache //获取到后放入Redis中
else locgoin //否则,则直接执行
我们一般不能直接访问Redis数据库,首先先建立数据访问层RedisDao,RedisDao中需要提供两个方法,一个是 getSeckill 和 putSeckill
在编写这两个方法时还需要注意一个问题,那就是序列化的问题,Redis并没有提供序列化和反序列化,我们需要自定义序列化,我们使用 protostuff 进行序列化与反序列化操作
引入 protostuff 依赖包
1 <!-- protostuff序列化依赖 --> 2 <dependency> 3 <groupId>com.dyuproject.protostuff</groupId> 4 <artifactId>protostuff-core</artifactId> 5 <version>1.0.8</version> 6 </dependency> 7 <dependency> 8 <groupId>com.dyuproject.protostuff</groupId> 9 <artifactId>protostuff-runtime</artifactId>10 <version>1.0.8</version>11 </dependency>编写数据访问层RedisDao
1 package com.xqc.seckill.dao.cache;2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 6 import com.dyuproject.protostuff.linkedBuffer; 7 import com.dyuproject.protostuff.ProtostuffIOUtil; 8 import com.dyuproject.protostuff.runtime.RuntimeSchema; 9 import com.xqc.seckill.entity.Seckill;10 11 import redis.clients.jedis.Jedis;12 import redis.clients.jedis.JedisPool;13 14 20 public class RedisDao {21 private final Logger logger = LoggerFactory.getLogger(this.getClass());22 23 private final JedisPool jedisPool;24 25 public RedisDao(String ip, int port) {26 jedisPool = new JedisPool(ip, port);27 }28 29 private RuntimeSchema<Seckill> schema = RuntimeSchema.createFrom(Seckill.class);30 31 public Seckill getSeckill(long seckillId) {32 //redis操作逻辑33 try {34 Jedis jedis = jedisPool.getResource();35 try {36 String key = "seckill:" + seckillId;37 //并没有实现内部序列化操作38 // get-> byte[] -> 反序列化 ->Object(Seckill)39 // 采用自定义序列化40 //protostuff : pojo.41 byte[] bytes = jedis.get(key.getBytes());42 //缓存中获取到bytes43 if (bytes != null) {44 //空对象45 Seckill seckill = schema.newMessage();46 ProtostuffIOUtil.mergeFrom(bytes, seckill, schema);47 //seckill 被反序列化48 return seckill;49 }50 } finally {51 jedis.close();52 }53 } catch (Exception e) {54 logger.error(e.getMessage(), e);55 }56 return null;57 }58 59 public String putSeckill(Seckill seckill) {60 // set Object(Seckill) -> 序列化 -> byte[]61 try {62 Jedis jedis = jedisPool.getResource();63 try {64 String key = "seckill:" + seckill.getSeckillId();65 byte[] bytes = ProtostuffIOUtil.toByteArray(seckill, schema,66 linkedBuffer.allocate(linkedBuffer.DEFAULT_BUFFER_SIZE));67 //超时缓存68 int timeout = 60 * 60;//1小时69 String result = jedis.setex(key.getBytes(), timeout, bytes);70 return result;71 } finally {72 jedis.close();73 }74 } catch (Exception e) {75 logger.error(e.getMessage(), e);76 }77 78 return null;79 }80 81 82 }优化ServiceImpl的 exportSeckillUrl 的方法
1 public Exposer exportSeckillUrl(long seckillId) { 2 // 优化点:缓存优化:超时的基础上维护一致性 3 //1:访问redis 4 Seckill seckill = redisDao.getSeckill(seckillId); 5 if (seckill == null) { 6 //2:访问数据库 7 seckill = seckillDao.queryById(seckillId); 8 if (seckill == null) { 9 return new Exposer(false, seckillId);10 } else {11 //3:放入redis12 redisDao.putSeckill(seckill);13 }14 }15 16 Date startTime = seckill.getStartTime();17 Date endTime = seckill.getEndTime();18 //系统当前时间19 Date nowTime = new Date();20 if (nowTime.getTime() < startTime.getTime()21 || nowTime.getTime() > endTime.getTime()) {22 return new Exposer(false, seckillId, nowTime.getTime(), startTime.getTime(),23 endTime.getTime());24 }25 //转化特定字符串的过程,不可逆26 String md5 = getMD5(seckillId);27 return new Exposer(true, md5, seckillId);28 }29 30 private String getMD5(long seckillId) {31 String base = seckillId + "/" + salt;32 String md5 = DigestUtils.md5DigestAsHex(base.getBytes());33 return md5;34 }3.2 并发优化:
在执行秒杀操作死,正常的执行应该如下:先减库存,并且得到行级锁,再执行插入购买明细,然后再提交释放行级锁,这个时候行级锁锁住了其他一些操作,我们可以进行如下优化,这时只需要延迟一倍。
修改executeSeckill方法如下:
1 @Transactional 2 8 public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) 9 throws SeckillException, RepeatKillException, SeckillCloseException {10 if (md5 == null || !md5.equals(getMD5(seckillId))) {11 throw new SeckillException("seckill data rewrite");12 }13 //执行秒杀逻辑:减库存 + 记录购买行为14 Date nowTime = new Date();15 16 try {17 //记录购买行为18 int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);19 //唯一:seckillId,userPhone20 if (insertCount <= 0) {21 //重复秒杀22 throw new RepeatKillException("seckill repeated");23 } else {24 //减库存,热点商品竞争25 int updateCount = seckillDao.reduceNumber(seckillId, nowTime);26 if (updateCount <= 0) {27 //没有更新到记录,秒杀结束,rollback28 throw new SeckillCloseException("seckill is closed");29 } else {30 //秒杀成功 commit31 SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);32 return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, successKilled);33 }34 }35 } catch (SeckillCloseException e1) {36 throw e1;37 } catch (RepeatKillException e2) {38 throw e2;39 } catch (Exception e) {40 logger.error(e.getMessage(), e);41 //所有编译期异常 转化为运行期异常42 throw new SeckillException("seckill inner error:" + e.getMessage());43 }44 }3.3深度优化:(存储过程)
定义一个新的接口,使用存储过程执行秒杀操作
1 7 SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5);实现executeSeckillProcedure方法
1 public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5) { 2 if (md5 == null || !md5.equals(getMD5(seckillId))) { 3 return new SeckillExecution(seckillId, SeckillStatEnum.DATA_REWRITE); 4 } 5 Date killTime = new Date(); 6 Map<String, Object> map = new HashMap<String, Object>(); 7 map.put("seckillId", seckillId); 8 map.put("phone", userPhone); 9 map.put("killTime", killTime);10 map.put("result", null);11 //执行存储过程,result被复制12 try {13 seckillDao.killByProcedure(map);14 //获取result15 int result = MapUtils.getInteger(map, "result", -2);16 if (result == 1) {17 SuccessKilled sk = successKilledDao.18 queryByIdWithSeckill(seckillId, userPhone);19 return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, sk);20 } else {21 return new SeckillExecution(seckillId, SeckillStatEnum.stateOf(result));22 }23 } catch (Exception e) {24 logger.error(e.getMessage(), e);25 return new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);26 27 }28 29 }编写SeckillDao实现有存储过程执行秒杀的逻辑
1 5 void killByProcedure(Map<String,Object> paramMap);在Mybatis中使用
1 <!-- mybatis调用存储过程 -->2 <select id="killByProcedure" statementType="CALLABLE">3 call execute_seckill(4 #{seckillId,jdbcType=BIGINT,mode=IN},5 #{phone,jdbcType=BIGINT,mode=IN},6 #{killTime,jdbcType=TIMESTAMP,mode=IN},7 #{result,jdbcType=INTEGER,mode=OUT}8 )9 </select>在Controller层使用
1 @ResponseBody 2 public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") Long seckillId, 3 @PathVariable("md5") String md5, 4 @cookievalue(value = "killPhone", required = false) Long phone) { 5 //springmvc valid 6 if (phone == null) { 7 return new SeckillResult<SeckillExecution>(false, "未注册"); 8 } 9 SeckillResult<SeckillExecution> result;10 try {11 //存储过程调用.12 SeckillExecution execution = seckillService.executeSeckillProcedure(seckillId, phone, md5);13 return new SeckillResult<SeckillExecution>(true,execution);14 } catch (RepeatKillException e) {15 SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.REPEAT_KILL);16 return new SeckillResult<SeckillExecution>(true,execution);17 } catch (SeckillCloseException e) {18 SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.END);19 return new SeckillResult<SeckillExecution>(true,execution);20 } catch (Exception e) {21 logger.error(e.getMessage(), e);22 SeckillExecution execution = new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);23 return new SeckillResult<SeckillExecution>(true,execution);24 }25 }至此,此系统的代码优化工作基本完成。但是在部署时可以将其更加优化,我们一般会使用如下架构
注册了域名就相当于在因特网上给自己的公司报了户口,但只有户口而没有家,你无法向别人介绍自己,别人也无法找到你。建立一个网站就是在网上给你的企业建一个家。这样你就可以在这里向客户介绍你的企业,展示你的实力,推销你的产品。客户可以给你的网站发电子邮件咨询问题,你可以把最新的产品放在网上供大家选购。世界上任何一个上网的人都可以拜访你的企业,而你的企业同时也展现在世界的面前。这是一个多好的宣传企业、树立企业形象的方式。1、有利于提升企业形象一般说来,企业建立自己的网站,不大可能马上为企业带来新客户、新生意,也不大可能大幅度提高企业业绩。毕竟,企业用于网站的费用很低,期望极低的费用能马上为企业带巨大的收益是不现实的,企业网站的作用更类似于企业在报纸和电视上所做的宣传企业本身及品牌的广告。不同之处在于企业网站容量更大,企业可以把任何想让客户及公众知道的内容放入网站。此外,相对来说,建立企业网站的投入比其它广告方式要低得多。当然,网站和广告是两种不同的宣传方式,各有不同作用,它们之间更多的是互相补充,而不是排斥,企业如拥有自己的网址,应在网络(INTERNET)中推介该网址,并把具体的内容放入网站。2、使企业具有网络沟通能力 在中国,人们对互联网络往往有所误解,以为电子信箱就是互联网络,有的企业将电子邮件地址当成网址,并印在名片上。实际上,电子邮件只是互联网络最常用、最简单的功能之一。互联网络真正的内涵在于其内容的丰富性,几乎无所不有。对企业来说,具有网络沟通能力标志是企业拥有自己的独立网站,而非电子信箱。3、可以全面详细地介绍企业及企业产品 企业网址的一个最基本的功能,就是能够全面、详细地介绍企业及企业产品。事实上,企业就可以把任何想让人们知道的信息放入网址。如企业简介、企业的人员、厂房、生产设施、研究机构、产品外观、功能及使用方法等,都可以展示于网上。4、可以与客户保持密切联系 在美国,每当人们想知道某企业有什么产品、服务或新产品、服务,甚至只是想知道该企业有什么新闻时,他们就会习惯性的进入该企业的网址。因为大多数企业已经把所有的产品、服务信息发布于网上,并且定期在网上发布有关企业的新闻信息。由于互联网络这种高科技的媒介进入中国时间不长,中国企业与客户之间现在暂时还不习惯于这种联系方式。但随着越来越多的企业对互联网络认识的加深,在网上发布产品和信息这种现况将发生巨大改变 ,目前已经有越来越多的中国企业具有自己的网络能力,并逐渐习惯于利用网络与客户进行沟通。5、可以与潜在客户建立商业联系 这是企业网址最重要的功能之一,也是为什么那么多国外企业非常重视网站建设的根本原因。现在,世界各国的经销商主要都是利用互联网络来寻找新的产品和新的供求,因为这样做费用最低、效率最高。原则上,全世界任何人,只要知道了企业的网址,就可以看到企业的产品和服务。因此,关键在于如何将企业网址推介出去。一种非常实用、有效且常用的方法是将企业的网址登记在全球著名的搜索引擎上(如YAHOO、EXCITE、ALT**ISTA等),则可以使潜在客户能够容易地找到企业和企业的产品。这正是商业上通行的做法,而且已被证明是十分有效的。6、可以降低通迅费用 对于企业来说,每年的通迅费用,尤其是涉及到进出口贸易的通迅费用,是一笔非常庞大的开支。利用企业网站所提供的集团电子信箱(可达到达30个左右,usename@qicaispace.com),可以有效地降低通迅费用,这是企业建立网站的另一个益处(利用E-mail通迅的费用仅为市话费用)。7、可以利用网站及时得到客户反馈的信息 客户一般是不会积极主动地向企业反馈信息的。如企业在设计网站时,加入客户与企业联系的电子邮件和电子表格,因使用极其方便,一般来说,客户习惯于使用这种方式与企业进行联系。因此,企业可以得到大量的客户意见和建议,将有利于企业的蓬勃发展。
互联网的本质是让数据真正的流动起来,实现互联、互通、开放和融合,而这无疑也是对作为互联网基础设施的CDN平台的基本要求。
根据VNI(Visual Networking Index)报告显示,到2020年,全球互联网用户将增至41亿,全球IP流量未来三年复合年增长率(CAGR)将达到22%,视频流量将占79%,玛丽·米克尔(Mary Meeker)的2018年《互联网趋势》(Internet Trends)报告更是指出中国市场的增长更是领跑全球。而CDN之于互联网如同水电煤之于我们日常生活,将会在其中扮演着不可或缺的角色,CDN 流量占比也将逐年提高。
当下伴随着视频形式和类型的不断丰富和延伸:4K、8K、3D、AR、VR、360度全景视频等等不断涌现,观众越来越苛刻的观看体验要求无疑是对作为互联网基础配套设施、凝聚每一刻极致视听体验的CDN服务提出了更高的期待和要求!
国外知名调研机构MarketsandMarkets统计数据预测,2019年全球CDN市场规模将达到121亿美元,超过50%的互联网流量通过CDN进行加速,而在国内却不足20%。另据方证证券预测,“未来2-3年CDN行业增速40%以上,行业前景还是很不错的”。
根据公开资料显示:国内CDN 市场规模持续保持较快增长态势
2016 年我国 CDN 市场规模达到 110 亿元左右,目前我国的 CDN 覆盖率仅为 17.2%,与北美成熟市场的 50%覆盖率相比具有较大差距。未来伴随一系列互联网相关政策的出台,以及VR/AR、 4K 视频、游戏、电商等业务带动下的高流量需求爆发,未来 5 年中国 CDN 市场年均增速将达 35%以上, CDN 产业将迎来黄金发展时期。回顾过去两年,国内CDN市场很不平静,价格战也是每次必被提及的一个话题。网宿科技无疑是整个行业的一个缩影,市场份额从2015年的超过80%下降到2017年的不到40%,利润同比下降超过30%。
实际上,价格战是每个行业发展过程中所必须经历的一个过程,但对于致力于打造极致CDN服务体验的平台而言,他们也深刻知道优质的头部客户更看重产品品质和服务的可扩展性及定制化,而极致的服务体验配得上它应得的价值回报!
成立于2016年2月的视界云天,创立伊始就致力于云传输技术的深入拓展和研发,同年9月推出CDN服务平台“视界云”,视界云核心团队来自CDN行业内顶尖企业,拥有超过十年的分布式计算技术研发经验和运营服务经验,具有业内顶尖的定制需求研发能力和本地化团队服务能力。成立近三年以来,视界云坚持以极佳的客户体验赢得了良好口碑和众多殊荣,在此衷心感谢每一位合作伙伴对视界云产品、技术和服务的认可和支持!
对于CDN云服务领域而言,不需要太多的套路,需要的是踏踏实实做事,不断向客户提供优质的产品和服务,让商业自然回归本质,仅此而已!
直播加速可以更好的提高用户体验感,可以减少视频播放过程中的卡顿黑屏等情况,
蔚可云即时通讯IM 专业定制软件APP开发,蔚可云IM即时通讯软件成品(集成,定制,源码,数据私有,安全加密)可用于社交沟通,企业通讯,游戏交流,直播互动。蔚可云SSL证书只需159元,cdn加速和云服务器0元免费试用,等保合规2.0申请,ddos防御业务 等都可接。
-
多家中小银行跟进下调存款利率,整体仍
10月下旬以来,多家农商行、村镇银行发布存款利率调整通知,对一年期、三年期、五年期等存款利率进行下调,同时,下调幅度从10个…
-
叮咚买菜正式入驻淘宝买菜,联手给消费
11月9日消息,天猫双11期间,叮咚买菜入驻淘宝买菜,将联手提供1小时到家服务,为消费者们带来优质、更低价的商品。据介绍,这是…
-
华为再次开启了先锋计划推出了Mate 60
今天上午,华为再次开启了先锋计划,推出了Mate 60 Pro+和Mate X5两款手机。尽管没有任何宣发,但Mate X5作为理财神器的继任者,仍然展现了…
-
华为推出新一代折叠屏手机Mate X5
华为推出新一代折叠屏手机Mate X5,该款手机于昨日开始预订,订金为1000元,最终价格尚未公布。Mate X5采用横向大折叠设计,内屏为7.85英…
-
鹅蛋的做法大全家常做法(鹅蛋的做法大
你们好,最近小元发现有诸多的小伙伴们对于鹅蛋的做法大全家常做法,鹅蛋的做法大全家常这个问题都颇为感兴趣的,今天小活为大家梳理了下,…