当前位置: 首页 > news >正文

一个网站做多少关键词万网网站电话

一个网站做多少关键词,万网网站电话,辽宁建设工程信息网招标公告桓仁金山热电厂防水工程,标准网站建设费用转载自 秒杀架构实践 前言 本次采用循序渐进的方式逐步提高性能达到并发秒杀的效果#xff0c;文章较长请准备好瓜子板凳(liushuizhang)。 本文所有涉及的代码#xff1a; https://github.com/crossoverJie/SSM https://github.com/crossoverJie/distributed-redis-tool 最…转载自 秒杀架构实践 前言 本次采用循序渐进的方式逐步提高性能达到并发秒杀的效果文章较长请准备好瓜子板凳(liushuizhang)。 本文所有涉及的代码 https://github.com/crossoverJie/SSM https://github.com/crossoverJie/distributed-redis-tool 最终架构图 先简单根据这个图谈下请求的流转因为后面不管怎么改进这个都是没有变的。 前端请求进入 web 层对应的代码就是 controller。 之后将真正的库存校验、下单等请求发往 Service 层其中 RPC 调用依然采用的 dubbo只是更新为最新版本本次不会过多讨论 dubbo 相关的细节有兴趣的可以查看 基于dubbo 的分布式架构。 Service 层再对数据进行落地下单完成。 无限制 其实抛开秒杀这个场景来说正常的一个下单流程可以简单分为以下几步 校验库存 扣库存 创建订单 支付 基于上文的架构所以我们有了以下实现 先看看实际项目的结构 还是和以前一样 提供出一个 API 用于 Service 层实现以及 web 层消费。 web 层简单来说就是一个 SpringMVC。 Service 层则是真正的数据落地。 SSM-SECONDS-KILL-ORDER-CONSUMER 则是后文会提到的 Kafka 消费。 数据库也是只有简单的两张表模拟下单 CREATE TABLE stock (  id int(11) unsigned NOT NULL AUTO_INCREMENT,  name varchar(50) NOT NULL DEFAULT COMMENT 名称,  count int(11) NOT NULL COMMENT 库存,  sale int(11) NOT NULL COMMENT 已售,  version int(11) NOT NULL COMMENT 乐观锁版本号,  PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT2 DEFAULT CHARSETutf8;     CREATE TABLE stock_order (  id int(11) unsigned NOT NULL AUTO_INCREMENT,  sid int(11) NOT NULL COMMENT 库存ID,  name varchar(30) NOT NULL DEFAULT COMMENT 商品名称,  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 创建时间,  PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT55 DEFAULT CHARSETutf8; web 层 controller 实现: Autowired    private StockService stockService;      Autowired    private OrderService orderService;      RequestMapping(/createWrongOrder/{sid})    ResponseBody    public String createWrongOrder(PathVariable int sid) {        logger.info(sid[{}], sid);        int id 0;        try {            id orderService.createWrongOrder(sid);        } catch (Exception e) {            logger.error(Exception,e);        }        return String.valueOf(id);    } 其中 web 作为一个消费者调用看 OrderService 提供出来的 dubbo 服务。 Service 层 OrderService 实现 首先是对 API 的实现(会在 API 提供出接口) Service public class OrderServiceImpl implements OrderService {      Resource(name DBOrderService)    private com.crossoverJie.seconds.kill.service.OrderService orderService ;      Override    public int createWrongOrder(int sid) throws Exception {        return orderService.createWrongOrder(sid);    } } 这里只是简单调用了 DBOrderService 中的实现DBOrderService 才是真正的数据落地也就是写数据库了。 DBOrderService 实现 Transactional(rollbackFor Exception.class) Service(value DBOrderService) public class OrderServiceImpl implements OrderService {    Resource(name DBStockService)    private com.crossoverJie.seconds.kill.service.StockService stockService;      Autowired    private StockOrderMapper orderMapper;      Override    public int createWrongOrder(int sid) throws Exception{          //校验库存        Stock stock checkStock(sid);          //扣库存        saleStock(stock);          //创建订单        int id createOrder(stock);          return id;    }      private Stock checkStock(int sid) {        Stock stock stockService.getStockById(sid);        if (stock.getSale().equals(stock.getCount())) {            throw new RuntimeException(库存不足);        }        return stock;    }      private int saleStock(Stock stock) {        stock.setSale(stock.getSale() 1);        return stockService.updateStockById(stock);    }      private int createOrder(Stock stock) {        StockOrder order new StockOrder();        order.setSid(stock.getId());        order.setName(stock.getName());        int id orderMapper.insertSelective(order);        return id;    }           } 预先初始化了 10 条库存。 手动调用下 createWrongOrder/1 接口发现 库存表 订单表 一切看起来都没有问题数据也正常。 但是当用 JMeter 并发测试时 请求都响应成功库存确实也扣完了但是订单却生成了 124 条记录。 这显然是典型的超卖现象。 其实现在再去手动调用接口会返回库存不足但为时晚矣。 乐观锁更新 怎么来避免上述的现象呢 最简单的做法自然是乐观锁了这里不过多讨论这个不熟悉的朋友可以看下这篇。 来看看具体实现 其实其他的都没怎么改主要是 Service 层。 Override    public int createOptimisticOrder(int sid) throws Exception {          //校验库存        Stock stock checkStock(sid);          //乐观锁更新库存        saleStockOptimistic(stock);          //创建订单        int id createOrder(stock);          return id;    }      private void saleStockOptimistic(Stock stock) {        int count stockService.updateStockByOptimistic(stock);        if (count 0){            throw new RuntimeException(并发更新库存失败) ;        }    } 对应的 XML update idupdateByOptimistic parameterTypecom.crossoverJie.seconds.kill.pojo.Stock        update stock        set            sale sale 1,            version version 1,        /set          WHERE id #{id,jdbcTypeINTEGER}        AND version #{version,jdbcTypeINTEGER}      /update 同样的测试条件我们再进行上面的测试 /createOptimisticOrder/1 这次发现无论是库存订单都是 OK 的。 查看日志发现 很多并发请求会响应错误这就达到了效果。 提高吞吐量 为了进一步提高秒杀时的吞吐量以及响应效率这里的 web 和 Service 都进行了横向扩展。 web 利用 Nginx 进行负载。 Service 也是多台应用。 再用 JMeter 测试时可以直观的看到效果。 由于我是在阿里云的一台小水管服务器进行测试的加上配置不高、应用都在同一台所以并没有完全体现出性能上的优势 Nginx 做负载转发时候也会增加额外的网络消耗。 shell 脚本实现简单的 CI 由于应用多台部署之后手动发版测试的痛苦相信经历过的都有体会。 这次并没有精力去搭建完整的 CI CD只是写了一个简单的脚本实现了自动化部署希望对这方面没有经验的同学带来一点启发 构建 web #!/bin/bash   # 构建 web 消费者   #read appname   appnameconsumer echo input$appname   PID$(ps -ef | grep $appname | grep -v grep | awk {print $2})   # 遍历杀掉 pid for var in ${PID[]}; do    echo loop pid $var    kill -9 $var done   echo kill $appname success   cd ..   git pull   cd SSM-SECONDS-KILL   mvn -Dmaven.test.skiptrue clean package   echo build war success   cp /home/crossoverJie/SSM/SSM-SECONDS-KILL/SSM-SECONDS-KILL-WEB/target/SSM-SECONDS-KILL-WEB-2.2.0-SNAPSHOT.war /home/crossoverJie/tomcat/tomcat-dubbo-consumer-8083/webapps echo cp tomcat-dubbo-consumer-8083/webapps ok!   cp /home/crossoverJie/SSM/SSM-SECONDS-KILL/SSM-SECONDS-KILL-WEB/target/SSM-SECONDS-KILL-WEB-2.2.0-SNAPSHOT.war /home/crossoverJie/tomcat/tomcat-dubbo-consumer-7083-slave/webapps echo cp tomcat-dubbo-consumer-7083-slave/webapps ok!   sh /home/crossoverJie/tomcat/tomcat-dubbo-consumer-8083/bin/startup.sh echo tomcat-dubbo-consumer-8083/bin/startup.sh success   sh /home/crossoverJie/tomcat/tomcat-dubbo-consumer-7083-slave/bin/startup.sh echo tomcat-dubbo-consumer-7083-slave/bin/startup.sh success   echo start $appname success 构建 Service # 构建服务提供者   #read appname   appnameprovider   echo input$appname     PID$(ps -ef | grep $appname | grep -v grep | awk {print $2})   #if [ $? -eq 0 ]; then #    echo process id:$PID #else #    echo process $appname not exit #    exit #fi   # 遍历杀掉 pid for var in ${PID[]}; do    echo loop pid $var    kill -9 $var done   echo kill $appname success     cd ..   git pull   cd SSM-SECONDS-KILL   mvn -Dmaven.test.skiptrue clean package   echo build war success   cp /home/crossoverJie/SSM/SSM-SECONDS-KILL/SSM-SECONDS-KILL-SERVICE/target/SSM-SECONDS-KILL-SERVICE-2.2.0-SNAPSHOT.war /home/crossoverJie/tomcat/tomcat-dubbo-provider-8080/webapps   echo cp tomcat-dubbo-provider-8080/webapps ok!   cp /home/crossoverJie/SSM/SSM-SECONDS-KILL/SSM-SECONDS-KILL-SERVICE/target/SSM-SECONDS-KILL-SERVICE-2.2.0-SNAPSHOT.war /home/crossoverJie/tomcat/tomcat-dubbo-provider-7080-slave/webapps   echo cp tomcat-dubbo-provider-7080-slave/webapps ok!   sh /home/crossoverJie/tomcat/tomcat-dubbo-provider-8080/bin/startup.sh echo tomcat-dubbo-provider-8080/bin/startup.sh success   sh /home/crossoverJie/tomcat/tomcat-dubbo-provider-7080-slave/bin/startup.sh echo tomcat-dubbo-provider-8080/bin/startup.sh success   echo start $appname success 之后每当我有更新只需要执行这两个脚本就可以帮我自动构建。 都是最基础的 Linux 命令相信大家都看得明白。 乐观锁更新 分布式限流 上文的结果看似没有问题其实还差得远呢。 这里只是模拟了 300 个并发没有问题但是当请求达到了 3000 3W300W 呢 虽说可以横向扩展可以支撑更多的请求。 但是能不能利用最少的资源解决问题呢 其实仔细分析下会发现 假设我的商品一共只有 10 个库存那么无论你多少人来买其实最终也最多只有 10 人可以下单成功。 所以其中会有 99% 的请求都是无效的。 大家都知道大多数应用数据库都是压倒骆驼的最后一根稻草。 通过 Druid 的监控来看看之前请求数据库的情况 因为 Service 是两个应用 数据库也有 20 多个连接。 怎么样来优化呢 其实很容易想到的就是分布式限流。 我们将并发控制在一个可控的范围之内然后快速失败这样就能最大程度的保护系统。 distributed-redis-tool ⬆️v1.0.3 为此还对 https://github.com/crossoverJie/distributed-redis-tool 进行了小小的升级。 因为加上该组件之后所有的请求都会经过 Redis所以对 Redis 资源的使用也是要非常小心。 API 更新 修改之后的 API 如下 Configuration public class RedisLimitConfig {      private Logger logger LoggerFactory.getLogger(RedisLimitConfig.class);      Value(${redis.limit})    private int limit;        Autowired    private JedisConnectionFactory jedisConnectionFactory;      Bean    public RedisLimit build() {        RedisLimit redisLimit new RedisLimit.Builder(jedisConnectionFactory, RedisToolsConstant.SINGLE)                .limit(limit)                .build();          return redisLimit;    } } 这里构建器改用了 JedisConnectionFactory所以得配合 Spring 来一起使用。 并在初始化时显示传入 Redis 是以集群方式部署还是单机强烈建议集群限流之后对 Redis 还是有一定的压力。 限流实现 既然 API 更新了实现自然也要修改 /**     * limit traffic     * return if true     */    public boolean limit() {          //get connection        Object connection getConnection();          Object result limitRequest(connection);          if (FAIL_CODE ! (Long) result) {            return true;        } else {            return false;        }    }      private Object limitRequest(Object connection) {        Object result null;        String key String.valueOf(System.currentTimeMillis() / 1000);        if (connection instanceof Jedis){            result ((Jedis)connection).eval(script, Collections.singletonList(key), Collections.singletonList(String.valueOf(limit)));            ((Jedis) connection).close();        }else {            result ((JedisCluster) connection).eval(script, Collections.singletonList(key), Collections.singletonList(String.valueOf(limit)));            try {                ((JedisCluster) connection).close();            } catch (IOException e) {                logger.error(IOException,e);            }        }        return result;    }      private Object getConnection() {        Object connection ;        if (type RedisToolsConstant.SINGLE){            RedisConnection redisConnection jedisConnectionFactory.getConnection();            connection redisConnection.getNativeConnection();        }else {            RedisClusterConnection clusterConnection jedisConnectionFactory.getClusterConnection();            connection clusterConnection.getNativeConnection() ;        }        return connection;    } 如果是原生的 Spring 应用得采用 SpringControllerLimit(errorCode200)注解。 实际使用如下 web 端 /**     * 乐观锁更新库存 限流     * param sid     * return     */    SpringControllerLimit(errorCode 200)    RequestMapping(/createOptimisticLimitOrder/{sid})    ResponseBody    public String createOptimisticLimitOrder(PathVariable int sid) {        logger.info(sid[{}], sid);        int id 0;        try {            id orderService.createOptimisticOrder(sid);        } catch (Exception e) {            logger.error(Exception,e);        }        return String.valueOf(id);    } Service 端就没什么更新了依然是采用的乐观锁更新数据库。 再压测看下效果 /createOptimisticLimitOrderByRedis/1 首先是看结果没有问题再看数据库连接以及并发请求数都有明显的下降。 乐观锁更新 分布式限流 Redis 缓存 其实仔细观察 Druid 监控数据发现这个 SQL 被多次查询 其实这是实时查询库存的 SQL主要是为了在每次下单之前判断是否还有库存。 这也是个优化点。 这种数据我们完全可以放在内存中效率比在数据库要高很多。 由于我们的应用是分布式的所以堆内缓存显然不合适Redis 就非常适合。 这次主要改造的是 Service 层 每次查询库存时走 Redis。 扣库存时更新 Redis。 需要提前将库存信息写入 Redis手动或者程序自动都可以。 主要代码如下 Override    public int createOptimisticOrderUseRedis(int sid) throws Exception {        //检验库存从 Redis 获取        Stock stock checkStockByRedis(sid);          //乐观锁更新库存 以及更新 Redis        saleStockOptimisticByRedis(stock);          //创建订单        int id createOrder(stock);        return id ;    }        private Stock checkStockByRedis(int sid) throws Exception {        Integer count Integer.parseInt(redisTemplate.opsForValue().get(RedisKeysConstant.STOCK_COUNT sid));        Integer sale Integer.parseInt(redisTemplate.opsForValue().get(RedisKeysConstant.STOCK_SALE sid));        if (count.equals(sale)){            throw new RuntimeException(库存不足 Redis currentCount sale);        }        Integer version Integer.parseInt(redisTemplate.opsForValue().get(RedisKeysConstant.STOCK_VERSION sid));        Stock stock new Stock() ;        stock.setId(sid);        stock.setCount(count);        stock.setSale(sale);        stock.setVersion(version);          return stock;    }            /**     * 乐观锁更新数据库 还要更新 Redis     * param stock     */    private void saleStockOptimisticByRedis(Stock stock) {        int count stockService.updateStockByOptimistic(stock);        if (count 0){            throw new RuntimeException(并发更新库存失败) ;        }        //自增        redisTemplate.opsForValue().increment(RedisKeysConstant.STOCK_SALE stock.getId(),1) ;        redisTemplate.opsForValue().increment(RedisKeysConstant.STOCK_VERSION stock.getId(),1) ;    }     压测看看实际效果 /createOptimisticLimitOrderByRedis/1 最后发现数据没问题数据库的请求与并发也都下来了。 乐观锁更新 分布式限流 Redis 缓存 Kafka 异步 最后的优化还是想如何来再次提高吞吐量以及性能的。 我们上文所有例子其实都是同步请求完全可以利用同步转异步来提高性能啊。 这里我们将写订单以及更新库存的操作进行异步化利用 Kafka 来进行解耦和队列的作用。 每当一个请求通过了限流到达了 Service 层通过了库存校验之后就将订单信息发给 Kafka 这样一个请求就可以直接返回了。 消费程序再对数据进行入库落地。 因为异步了所以最终需要采取回调或者是其他提醒的方式提醒用户购买完成。 这里代码较多就不贴了消费程序其实就是把之前的 Service 层的逻辑重写了一遍不过采用的是 SpringBoot。 感兴趣的朋友可以看下。 https://github.com/crossoverJie/SSM/tree/master/SSM-SECONDS-KILL/SSM-SECONDS-KILL-ORDER-CONSUMER 总结 其实经过上面的一顿优化总结起来无非就是以下几点 尽量将请求拦截在上游。 还可以根据 UID 进行限流。 最大程度的减少请求落到 DB。 多利用缓存。 同步操作异步化。 fail fast尽早失败保护应用。 码字不易这应该是我写过字数最多的了想想当年高中 800 字的作文都憋不出来
http://www.zqtcl.cn/news/893373/

相关文章:

  • 重点实验室网站建设宁波seo优化服务
  • 怎么用手机做刷会员网站网页设计指什么
  • 小企业网站建设多少钱网页设计图片剧中
  • 新乐做网站优化如何做二级域名子目录网站
  • 如何在网站上做推广中国在数码网站注册域名好 gt
  • 电子商务电商网站饿建设管理网站建设
  • php网站出现乱码网站建设项目总结
  • 网站建设公司墨子网络用我在线网站建设
  • 长寿网站建设公司服装设计有哪些网站
  • 苍溪规划和建设局网站网页设计制作报告
  • html5网站 源码360浏览器个别网页打不开怎么解决
  • 找个小网站建设网站优点
  • 台州网站建设优化网站建设加微信
  • 公司网站建设费会计分录义乌商城集团的网站建设
  • 彩票网站建设基本流程网站文章页做百度小程序
  • 在淘宝上做代销哪个网站好推广普通话喜迎二十大的手抄报怎么画
  • 知名网站建设开发受欢迎的唐山网站建设
  • 普洱网站搭建创建论坛网站需要多少钱
  • 自己做的网站如何在网络上展示wordpress 手动采集
  • 上海做网站要多少钱wordpress教程app
  • 房地产设计网站沈阳人流哪个医院好安全
  • 贵阳专业做网站微信小程序商城源代码
  • seo建站收费地震郑州做网站开发销售
  • 东莞整站优化推广公司找火速建设企业网站要多少钱
  • 网站备案 两个域名东莞保安公司联系电话
  • 网站专业制作公司律师如何在网上推广
  • 免费培训seo网站一直免费的服务器下载安装
  • 广州h5网站制作公司做竞价网站 要注意什么
  • 太原网站搭建推广id怎么编辑wordpress
  • 网站开发网站设计制作广告设计与制作基础知识