商城业务-商品秒杀服务

  • 一、秒杀介绍
    • 1、秒杀业务
    • 2、秒杀流程
    • 3、限流
  • 二、秒杀
    • 1.启动前端项目
      • 1).修改coupon的网关
      • 2).vscode代码
        • 2.1).查询、新增秒杀场次
        • 2.2).关联商品
  • 三、秒杀服务-定时任务&cron表达式
    • 1.秒杀微服务
      • 1.0 创建 秒杀微服务
      • 1.1 添加依赖
      • 1.2 配置文件application.properties
      • 1.3 启动类
    • 2.秒杀业务流程
      • 2.1 秒杀流程图
      • 2.2 定时任务笔记
        • 1、cron 表达式
        • 2、cron 示例
        • 3、SpringBoot 整合
      • 2.3、测试 定时任务-异步执行
    • 3.秒杀商品定时上架
      • 3.1远程coupon
        • 3.1.1 coupon的feign接口
        • 3.1.2 coupon秒杀的商品
        • 3.1.3 coupon秒杀的商品实现类
      • 3.2 秒杀的控制器-SeckillSkuScheduled
        • 3.2.1 秒杀商品定时上架
        • 3.2.2 秒杀商品定时上架的实现类
          • 1).上架商品
          • 2).缓存到redis
            • a.缓存秒杀活动的信息
            • b.缓存秒杀活动的关联商品信息
        • 3.2.3. 秒杀上架幂等性问题
          • 1).修改定时上架SeckillSkuScheduled-分布式锁
          • 2).修改SeckillServiceImpl实现类
        • 3.2.4. 同一商品多次被秒杀,怎办?
    • 4.秒杀商品页面渲染
      • 4.1 查询当前时秒杀的商品
        • 1).控制器controller
        • 2).实现类SeckillServiceImpl
      • 4.2 页面渲染-product
        • 1).修改网关
        • 2).配置域名
        • 3).页面渲染
        • 4).获得秒杀的商品
          • 4.1).秒杀的控制器
          • 4.2).秒杀的实现类
          • 4.3).商品product项目秒杀的feign接口
          • 4.4).修改展示当前sku商品详情--SkuInfoServiceImpl
          • 4.5).渲染商品详情页面-item.html
  • 四、秒杀系统设计

一、秒杀介绍

1、秒杀业务

秒杀具有瞬间高并发的特点,针对这一特点,必须要做限流 + 异步 + 缓存(页面静态化)

  • 独立部署

限流方式:

  • 前端限流,一些高并发的网站直接在前端页面开始限流,例如:小米的验证码设计
  • nginx 限流,直接负载部分请求到错误的静态页面:令牌算法 漏斗算法
  • 网关限流,限流的过滤器
  • 代码中使用分布式信号量
  • rabbitmq 限流(能者多劳:chanel.basicQos(1)),保证发挥所有服务器的性能。

2、秒杀流程

见秒杀流程图

3、限流

参照 Alibaba Sentinel

二、秒杀

京东秒杀

1.启动前端项目

IDEA启动renren-fast项目,打开vscode,npm run dev

1).修改coupon的网关

        - id: coupon_routeuri: lb://gulimall-couponpredicates:- Path=/api/coupon/**filters:- RewritePath=/api/(?<segment>.*),/$\{segment}

2).vscode代码

coupon-seckillsession

2.1).查询、新增秒杀场次

查询秒杀场次
http://localhost:88/api/coupon/seckillskurelation/list?t=1623417281403&page=1&limit=10&key=&promotionSessionId=1

新增秒杀场次

2.2).关联商品

http://localhost:88/api/coupon/seckillskurelation/list?t=1623417281403&page=1&limit=10&key=&promotionSessionId=1

查询已经关联的商品:sms_seckill_sku_relation
coupon的seckillSkuRelationServiceImpl

    @Overridepublic PageUtils queryPage(Map<String, Object> params) {//list?t=1623417281403&page=1&limit=10&key=&promotionSessionId=1QueryWrapper<SeckillSkuRelationEntity> wrapper = new QueryWrapper<SeckillSkuRelationEntity>();String promotionSessionId = (String) params.get("promotionSessionId");if (!StringUtils.isEmpty(promotionSessionId)) {wrapper.eq("promotion_session_id", promotionSessionId);}IPage<SeckillSkuRelationEntity> page = this.page(new Query<SeckillSkuRelationEntity>().getPage(params),wrapper);return new PageUtils(page);}

三、秒杀服务-定时任务&cron表达式

1.秒杀微服务

1.0 创建 秒杀微服务

1.1 添加依赖

<dependency><groupId>com.gulimall</groupId><artifactId>common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

1.2 配置文件application.properties

spring.application.name=gulimall-seckill
server.port=25000
spring.cloud.nacos.discovery.server-addr=192.168.56.10:8848
spring.redis.host=192.168.56.10

1.3 启动类

@EnableDiscoveryClient //开启nacos

2.秒杀业务流程

把秒杀场次放入session、库存页放入session

2.1 秒杀流程图

逻辑:秒杀商品定时上架、秒杀

2.2 定时任务笔记

定时任务与分布式调度笔记:百度云地址 提取码1111
定时任务使用quartz-schedule。 http://www.quartz-scheduler.org/

1、cron 表达式

语法:秒 分 时 日 月 周 年(Spring 不支持)
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

cron表达式
在线Cron表达式生成器

2、cron 示例

3、SpringBoot 整合

@EnableScheduling
@Scheduled

2.3、测试 定时任务-异步执行

/*** 定时任务*      1、@EnableScheduling 开启定时任务*      2、@Scheduled开启一个定时任务** 异步任务*      1、@EnableAsync:开启异步任务*      2、@Async:给希望异步执行的方法标注*/@Slf4j
@Component //放入容器中
@EnableScheduling //开启定时任务
@EnableAsync // 开启异步任务
public class HelloScheduled {@Scheduled(cron = "* * * * * ?")@Async //异步执行的方法public void hello() {log.info("hello...");}
}
 * 秒  分  时  日  月  周  年(Spring 不支持)* 1、在Spring中表达式是6位组成,不允许第七位的年份* 2、在周几的的位置,1-7代表周一到周日* 3、定时任务不该阻塞。默认是阻塞的*      1)、可以让业务以异步的方式,自己提交到线程池*              CompletableFuture.runAsync(() -> {*         },execute);**      2)、支持定时任务线程池;设置 TaskSchedulingProperties*        spring.task.scheduling.pool.size: 5**      3)、让定时任务异步执行*          异步任务**      解决:使用异步任务 + 定时任务来完成定时任务不阻塞的功能**/

3.秒杀商品定时上架

3.1远程coupon

3.1.1 coupon的feign接口

@FeignClient("gulimall-coupon")
public interface CouponFeignService {@GetMapping("/coupon/seckillsession/lates3DaySession")R getLates3DaySession();
}

3.1.2 coupon秒杀的商品

    @GetMapping("/lates3DaySession")public R getLates3DaySession() {List<SeckillSessionEntity> sessions= seckillSessionService.getLates3DaySession();return R.ok().setData(sessions);}

3.1.3 coupon秒杀的商品实现类

    @Overridepublic List<SeckillSessionEntity> getLates3DaySession() {//计算最近三天String startTime = startTime();String endTime = endTime();//获取最近三天的秒杀场次QueryWrapper<SeckillSessionEntity> wrapper = new QueryWrapper<>();wrapper.between("start_time", startTime, endTime);List<SeckillSessionEntity> list = this.list(wrapper);if (list != null && list.size() > 0) {List<SeckillSessionEntity> collect = list.stream().map(session -> {Long id = session.getId();QueryWrapper<SeckillSkuRelationEntity> wrapper1 = new QueryWrapper<SeckillSkuRelationEntity>().eq("promotion_session_id", id);List<SeckillSkuRelationEntity> relationSkus = seckillSkuRelationService.list(wrapper1);session.setRelationSkus(relationSkus);return session;}).collect(Collectors.toList());return collect;}return null;}//获取开始、结束时间private String startTime() {LocalDate now = LocalDate.now();LocalTime min = LocalTime.MIN;LocalDateTime start = LocalDateTime.of(now, min);String format = start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return format;}private String endTime() {LocalDate now = LocalDate.now();LocalDate plus2 = now.plusDays(2);LocalTime max = LocalTime.MAX;LocalDateTime end = LocalDateTime.of(plus2, max);String format = end.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));return format;}

3.2 秒杀的控制器-SeckillSkuScheduled

3.2.1 秒杀商品定时上架

    //上架最近三天需要三天秒杀的商品@Scheduled(cron = "0 0 3 * * ?")public void uploadSeckillSkuLatest3Days() {//1、重复上架无需处理log.info("上架秒杀的商品...");seckillService.uploadSeckillSkuLatest3Days();}

3.2.2 秒杀商品定时上架的实现类

扫描最近三天需要参与秒杀的活动 分 上架商品缓存到redis

1).上架商品
      @AutowiredCouponFeignService couponFeignService;@AutowiredStringRedisTemplate redisTemplate;@AutowiredProductFeignService productFeignService;@AutowiredRedissonClient redissonClient;private final String SESSION_CACHE_PREFIX = "seckill:sessions:";private final String SKUKILL_CACHE_PREFIX = "seckill:skus:";private final String SKU_STOCK_SEMAPHORE = "seckill:stock:";//+商品随机码@Overridepublic void uploadSeckillSkuLatest3Days() {//1.扫描最近三天需要参与秒杀的活动。远程coupon的seckill-sessionR r = couponFeignService.getLates3DaySession();if (r.getCode() == 0) {//1.1上架商品List<SeckillSessionWithSkus> sessionData = r.getData(new TypeReference<List<SeckillSessionWithSkus>>() {});//1.2.缓存到redis//1.2.1.缓存秒杀活动的信息saveSessionInfos(sessionData);//1.2.2.缓存秒杀活动的关联商品信息saveSessionSkuInfos(sessionData);}}
2).缓存到redis

保存数据到redis,包括缓存秒杀活动的信息缓存秒杀活动的关联商品信息

a.缓存秒杀活动的信息
    private void saveSessionInfos(List<SeckillSessionWithSkus> sessions) {sessions.stream().forEach(session -> {Long startTime = session.getStartTime().getTime();long endTime = session.getEndTime().getTime();String key = SESSION_CACHE_PREFIX + startTime + "_" + endTime;List<String> collect = session.getRelationSkus().stream().map(item -> session.getId().toString()).collect(Collectors.toList());//缓存活动信息redisTemplate.opsForList().leftPushAll(key, collect);});}
b.缓存秒杀活动的关联商品信息

分sku基本信息、sku秒杀信息、设置当前商品的秒杀时间信息、设置随机码、使用库存作为分布式的信号量。限流:

 private void saveSessionSkuInfos(List<SeckillSessionWithSkus> sessions) {BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);//准备hash操作sessions.stream().forEach(session -> {session.getRelationSkus().stream().forEach(seckillSkuVo -> {//缓存商品信息。缓存为Hash结构SeckillSkuRedisTo redisTo = new SeckillSkuRedisTo();//1.sku基本信息。// 远程product服务的SkuInfoR r = productFeignService.getSkuInfo(seckillSkuVo.getSkuId());if (r.getCode() == 0) {SkuInfoVo skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {});redisTo.setSkuInfo(skuInfo);}//2.sku秒杀信息BeanUtils.copyProperties(seckillSkuVo, redisTo);//3.设置当前商品的秒杀时间信息redisTo.setStartTime(session.getStartTime().getTime());redisTo.setEndTime(session.getEndTime().getTime());//4.设置随机码。seckill?skuId=1&key=1234r5498r5String token = UUID.randomUUID().toString().replace("_", "");redisTo.setRandomCode(token);//5.使用库存作为分布式的信号量。限流:RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + token);//商品可以秒杀的数量作为信号量semaphore.trySetPermits(seckillSkuVo.getSeckillCount());//为了好保存,把数据变成JSON数据String jsonString = JSON.toJSONString(redisTo);ops.put(seckillSkuVo.getSkuId().toString(), jsonString);});});}

导入Redisson依赖、Redisson配置

        <!--使用redisson作为所有分布式锁,分布式对象等功能性框架--><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></dependency>import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;/*** @Description:**/@Configuration
public class MyRedissonConfig {/*** 所有对Redisson的使用都是通过RedissonClient* @return* @throws IOException*/@Bean(destroyMethod="shutdown")public RedissonClient redissonClient() throws IOException {//1、创建配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.56.10:6379");//Redis url 应该以 redis:// 或 redis:// 开头//2、根据Config创建出RedissonClient实例RedissonClient redissonClient = Redisson.create(config);return redissonClient;}
}

远程商品服务,获取sku信息。feign接口

    //信息@RequestMapping("/info/{skuId}")public R info(@PathVariable("skuId") Long skuId) {SkuInfoEntity skuInfo = skuInfoService.getById(skuId);return R.ok().put("skuInfo", skuInfo);}@FeignClient("gulimall-product")
public interface ProductFeignService {//获取sku商品信息@RequestMapping("/product/skuinfo/info/{skuId}")R getSkuInfo(@PathVariable("skuId") Long skuId);
}

3.2.3. 秒杀上架幂等性问题

上架过的商品重复上架,咋办??。保证幂等性问题
不管多少服务,商品只能上架一次。使用分布式锁

1).修改定时上架SeckillSkuScheduled-分布式锁

使用分布式锁,实现一次商品上架
锁的业务执行完成,状态已经更新完成。其他人会获取到最新状态

@Slf4j
@Service
public class SeckillSkuScheduled {@Autowiredprivate SeckillService seckillService;@Autowiredprivate RedissonClient redissonClient;//秒杀商品上架功能的锁private final String upload_lock = "seckill:upload:lock";//TODO 保证幂等性问题。上架后就不用再上架了//@Scheduled(cron = "*/5 * * * * ? ")//上架最近三天需要三天秒杀的商品
//    @Scheduled(cron = "0 0 3 * * ?")@Scheduled(cron = "0 * * * * ?")public void uploadSeckillSkuLatest3Days() {//1、重复上架无需处理log.info("上架秒杀的商品...");//分布式锁//锁的业务执行完成,状态已经更新完成。其他人会获取到最新状态RLock lock = redissonClient.getLock(upload_lock);try {//加锁lock.lock(10, TimeUnit.SECONDS);seckillService.uploadSeckillSkuLatest3Days();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}
2).修改SeckillServiceImpl实现类

判断redis是否有数据key,没有就缓存数据到redis

3.2.4. 同一商品多次被秒杀,怎办?

.同一商品,在不同场次秒杀,怎么处理?修改保存的redis的key


redis秒杀信息为sessionId_skuId,商品信息为sessionId_skuIds

4.秒杀商品页面渲染

4.1 查询当前时秒杀的商品

1).控制器controller

@RestController
public class SeckillController {@AutowiredSeckillService seckillService;//返回当前时间可以参与的秒杀商品信息@GetMapping("/currentSeckillSkus")public R getCurrentSeckillSkus() {List<SeckillSkuRedisTo> vos = seckillService.getCurrentSeckillSkus();System.out.println("当前时间秒杀场次的商品数据vos: "+ vos);return R.ok().setData(vos);}
}

2).实现类SeckillServiceImpl

    //返回当前时间可以参与的秒杀商品信息@Overridepublic List<SeckillSkuRedisTo> getCurrentSeckillSkus() {//1.确定当前时间属于哪个秒杀场次long time = new Date().getTime();//从redis获取缓存数据。获取开始、结束时间Set<String> keys = redisTemplate.keys(SESSION_CACHE_PREFIX + "*");for (String key : keys) {//seckill:sessions:1624068000000_1624075200000String replace = key.replace(SESSION_CACHE_PREFIX, "");String[] s = replace.split("_");Long startTime = Long.parseLong(s[0]);Long endTime = Long.parseLong(s[1]);if (time >= startTime && time <= endTime) {//2.获取这个秒杀场次 所有的商品信息List<String> range = redisTemplate.opsForList().range(key, -100, 100);//获得操作redis的opsBoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);List<String> list = ops.multiGet(range);if (list != null) {List<SeckillSkuRedisTo> collect = list.stream().map(item -> {SeckillSkuRedisTo redis = JSON.parseObject((String) item, SeckillSkuRedisTo.class);//redis.setRandomCode(null);//当前秒杀开始需要随机码return redis;}).collect(Collectors.toList());return collect;}break;}}return null;}

4.2 页面渲染-product

1).修改网关

        - id: gulimall_seckill_routeuri: lb://gulimall-seckillpredicates:- Host=seckill.gulimall.com

2).配置域名

使用SwitchHosts.exe
测试http://seckill.gulimall.com/currentSeckillSkus

3).页面渲染

商品项目的index.html

                <div class="swiper-slide"><ul id="seckillSkuContent"></ul></div>
    $.get("http://seckill.gulimall.com/currentSeckillSkus", function (res) {if (res.data.length > 0) {res.data.forEach(function (item) {$("<li οnclick='toDetail(" + item.skuId + ")'></li>").append($("<img style='width: 130px; height: 130px' src='" + item.skuInfo.skuDefaultImg + "' />")).append($("<p>"+item.skuInfo.skuTitle+"</p>")).append($("<span>" + item.seckillPrice + "</span>")).append($("<s>" + item.skuInfo.price + "</s>")).appendTo("#seckillSkuContent");})}})function toDetail(skuId) {location.href = "http://item.gulimall.com/" + skuId + ".html";}

4).获得秒杀的商品

4.1).秒杀的控制器
    @GetMapping("/sku/seckill/{skuId}")public R getSkuSeckillInfo(@PathVariable("skuId") Long skuId) {SeckillSkuRedisTo to = seckillService.getSkuSeckillInfo(skuId);System.out.println("当前时间秒杀的商品to: " + to);return R.ok().setData(to);}
4.2).秒杀的实现类
    //获得秒杀的商品@Overridepublic SeckillSkuRedisTo getSkuSeckillInfo(Long skuId) {//1.找到所有需要参与秒杀的商品key---BoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);Set<String> keys = ops.keys();if (keys != null && keys.size() > 0) {String regx = "\\d_" + skuId;//正则表达式for (String key : keys) {//7_9,8_1。匹配成功,说明有此商品参与秒杀if (Pattern.matches(regx, key)) {String s = ops.get(key);SeckillSkuRedisTo redisTo = JSON.parseObject(s, SeckillSkuRedisTo.class);//随机码long current = new Date().getTime();if (current >= redisTo.getStartTime() && current <= redisTo.getEndTime()) {} else redisTo.setRandomCode(null);System.out.println("redisTo: "+redisTo);return redisTo;}}}return null;}
4.3).商品product项目秒杀的feign接口
@FeignClient("gulimall-seckill")
public interface SecKillFeignService {//查询秒杀的商品sku信息@GetMapping("/sku/seckill/{skuId}")R getSkuSeckillInfo(@PathVariable("skuId") Long skuId);
}
4.4).修改展示当前sku商品详情–SkuInfoServiceImpl
    @AutowiredSecKillFeignService secKillFeignService;public SkuItemVo item(Long skuId){//3.查询当前sku商品是否参与秒杀优惠CompletableFuture<Void> secKillFuture = CompletableFuture.runAsync(() -> {R r = secKillFeignService.getSkuSeckillInfo(skuId);if (r.getCode() == 0) {SeckillInfoVo data = r.getData(new TypeReference<SeckillInfoVo>() {});skuItemVo.setSeckillInfo(data);}}, executor);CompletableFuture<Void> allOf = CompletableFuture.allOf( saleAttrFuture, descFuture, baseAttrFuture, imageFuture,secKillFuture);}
4.5).渲染商品详情页面-item.html
    <li style="color: red" th:if="${item.seckillInfo!=null}"><span th:if="${#dates.createNow().getTime() < item.seckillInfo.startTime}">商品将会在[[${#dates.format(new java.util.Date(item.seckillInfo.startTime),"yyyy-MM-dd HH:mm:ss")}]]进行秒杀</span><span th:if="${#dates.createNow().getTime() >= item.seckillInfo.startTime && #dates.createNow().getTime() <= item.seckillInfo.endTime}">秒杀价:  [[${#numbers.formatDecimal(item.seckillInfo.seckillPrice,1,2)}]]</span></li>

四、秒杀系统设计

秒杀系统设计

商城业务-商品秒杀服务相关推荐

  1. 谷粒商城二十三秒杀服务

    秒杀是每一个电商系统中非常重要的模块,商家会不定期的发布一些低价商品,发布到秒杀系统中,秒杀系统的商品一般会放到首页展示,这样就可以引导用户购买商品. 秒杀的购买流程和普通的购买流程最大的特点就是瞬时 ...

  2. 203、商城业务-商品详情-环境搭建

    package com.atguigu.gulimall.product.web;import org.springframework.stereotype.Controller; import or ...

  3. 【谷粒商城高级篇】商城业务:商品检索

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

  4. JSD-2204-酷鲨商城(管理商品模块)-Day02

    1.补充 1.1嵌套路由(补充) 一旦使用了嵌套路由,必须有某个View是不完整的(其内部有某个区域使用了<router-view/>,是由其它View来负责显示的),这样的View不应该 ...

  5. 谷粒商城-分布式高级篇[商城业务-秒杀服务]

    谷粒商城-分布式基础篇[环境准备] 谷粒商城-分布式基础[业务编写] 谷粒商城-分布式高级篇[业务编写]持续更新 谷粒商城-分布式高级篇-ElasticSearch 谷粒商城-分布式高级篇-分布式锁与 ...

  6. 【秒杀购物商城业务服务】「分布式架构服务」盘点中间件服务的高可用模式及集群技术的方案分析

    秒杀购物商城业务服务-分布式架构介绍 基于MySQL数据库集群技术实现服务的高可用 基于Tomcat的集群负载机制实现Tomcat服务器的高可用 基于Nginx负载均衡机制实现负载均衡(介绍和配置) ...

  7. 「秒杀购物商城业务服务」「分布式架构服务」盘点中间件服务

    ​ 秒杀购物商城业务服务-分布式架构介绍 基于MySQL数据库集群技术实现服务的高可用 基于Tomcat的集群负载机制实现Tomcat服务器的高可用 基于Nginx负载均衡机制实现负载均衡(介绍和配置 ...

  8. 【谷粒商城 -秒杀服务】

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

  9. 谷粒商城--秒杀服务--高级篇笔记十二

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

最新文章

  1. JAVA-基础(Class对象及反射)
  2. 五大微信小程序开发IDE深度评测
  3. C++实现简单选择排序
  4. Unity Heathaze shader
  5. 装机防骗武器——鲁大师
  6. 算法十大排序(含动图)
  7. 批处理获取操作系统版本信息
  8. Oracle的共享关闭 独有关闭和共享更新关闭 (2)
  9. cannot open clipboard
  10. 破解各类加密文件密码
  11. @media scree 手机移动端屏幕自适应
  12. 南京商品房信息在哪里查询(查备案价)
  13. 必须记住这几种庄家洗盘的形态
  14. 华为mate8 解锁+root手记
  15. verilog代码中避免出现latch方法
  16. 为什么百度查到的ip和ipconfig查到的不一样?
  17. bada-开发入门之HelloWorld(上)
  18. SM2签名方案的安全性
  19. 人工神经网络的发展前景,人工神经网络及其应用
  20. 使用 rsync 服务(二)

热门文章

  1. 实现双token无感刷新
  2. web2.0的开源程序的下载和演示
  3. Cygwin+OSgeo4w安装
  4. python安装包的离线安装方法
  5. Apache Beam实战指南 | 玩转KafkaIO与Flink
  6. 分手后一定要过得开心
  7. mllib调参 spark_Spark入门:MLlib基本数据类型(1)
  8. Mysql 中ERROR 1406 (22001): Data too long for column 解决方法
  9. 基于Deep Sort的视频车流量计数(一)
  10. mac平台使用adb、tcpdump工具抓取android手机网络包