责任链设计模式

什么是责任链模式

项目有个请求,需要有对应的服务来处理,然后这个请求可能需要被很多个层级权限的服务来处理。我们将这些处理该请求的服务放在一条链上,链从前往后,是层级更高的服务,第一个服务处理不了,传递到链上的下一个服务,直到这个请求被处理成功。

责任链模式(Chain of Responsibility)是一种处理请求的模式,它让多个处理器都有机会处理该请求,直到其中某个处理器成功处理该请求,责任链模式把多个处理器串成链,然后让请求在链上传递。

  1. 如何把多个处理器串成链,然后让请求在链上传递
    客户端发送请求,处理类去处理它的请求,所以有个处理方法(handleRequest),处理类要连接在一起,所以**处理类要有一个方法(成员变量nextHandler)**指向下一个处理类。
    我们抽象出一个公共的父类,然后去定义不同的处理类,这些处理类通过nextHandler连接起来。
  2. 代码演示
package interview.pattern;

public class ChainRespPattern {
    Handler level1 = new Leader();
    Handler level2 = new Boss();
    level1.setNextHandler(level2);
    level1(10);
    level1(11);
    
}

abstract class Handler {
    protected Handler nextHandler;

    public void setNextHandler(Handler nexthandler) {
        this.nextHandler = nexthandler;
    }

    public abstract void handleRequest(Integer info);
}

class Leader extends Handler {
    @Override
    public void handleRequest(Integer info) {
        if (info > 0 && info < 11)
            System.out.println("Leader处理!");
        else
            nextHandler.handleRequest(info);
    }
}

class Boss extends Handler {
    @Override
    public void handleRequest(Integer info) {
        System.out.println("Boss处理!");
    }
}

责任链模式总结

  1. 优点
    • 责任链模式将请求和处理分开,请求者不需要谁去处理,处理者也不需要知道请求的全貌
    • 提高系统灵活性,新增一个处理器到系统中代价非常小
  2. 缺点
    • 降低系统性能,某一个请求如果要到最高级别,需要一级一级的传递
    • 不易于调试,不知道请求在哪个处理器

责任链模式实现短信发送必填校验、数据校验、黑名单、超发等具体业务逻辑

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 定义责任链节点接口
interface MessageHandler {
   MessageResult handleRequest(MessageRequest request);
}

// 必填校验处理器
class RequiredFieldValidationHandler implements MessageHandler {
   private MessageHandler nextHandler;

   public void setNextHandler(MessageHandler nextHandler) {
      this.nextHandler = nextHandler;
   }

   @Override
   public MessageResult handleRequest(MessageRequest request) {
      // 检查消息内容是否为空
      if (request.getMessageContent() == null || request.getMessageContent().isEmpty()) {
         return new MessageResult(false, "消息内容不能为空");
      } else if (nextHandler != null) {
         // 如果有下一个处理器,则传递请求
         return nextHandler.handleRequest(request);
      } else {
         return new MessageResult(true, "必填校验通过");
      }
   }
}

// 数据校验处理器
class DataValidationHandler implements MessageHandler {
   private MessageHandler nextHandler;

   public void setNextHandler(MessageHandler nextHandler) {
      this.nextHandler = nextHandler;
   }

   @Override
   public MessageResult handleRequest(MessageRequest request) {
      // 进行数据校验,这里只是示范,具体校验逻辑根据业务需求实现
      // 如果数据校验通过,则继续向下传递请求
      // 这里假设数据校验通过的条件为消息长度不超过50个字符
      if (request.getMessageContent().length() <= 50) {
         if (nextHandler != null) {
            return nextHandler.handleRequest(request);
         } else {
            return new MessageResult(true, "数据校验通过");
         }
      } else {
         return new MessageResult(false, "消息长度超过50个字符");
      }
   }
}

// 黑名单校验处理器
class BlacklistValidationHandler implements MessageHandler {
   private MessageHandler nextHandler;
   private Set<String> blacklist;

   public void setNextHandler(MessageHandler nextHandler) {
      this.nextHandler = nextHandler;
   }

   public void setBlacklist(Set<String> blacklist) {
      this.blacklist = blacklist;
   }

   @Override
   public MessageResult handleRequest(MessageRequest request) {
      // 检查消息内容是否在黑名单中
      if (blacklist.contains(request.getMessageContent())) {
         return new MessageResult(false, "消息内容在黑名单中");
      } else if (nextHandler != null) {
         // 如果不在黑名单中,并且有下一个处理器,则传递请求
         return nextHandler.handleRequest(request);
      } else {
         return new MessageResult(true, "黑名单校验通过");
      }
   }
}

// 超发校验处理器
class OverdraftValidationHandler implements MessageHandler {
   private MessageHandler nextHandler;
   private Map<String, Integer> messageCounts = new ConcurrentHashMap<>();
   private Lock lock = new ReentrantLock();

   public void setNextHandler(MessageHandler nextHandler) {
      this.nextHandler = nextHandler;
   }

   @Override
   public MessageResult handleRequest(MessageRequest request) {
      try {
         // 尝试获取分布式锁
         lock.lock();

         // 检查消息发送次数是否超过限制
         int count = messageCounts.getOrDefault(request.getMessageContent(), 0);
         if (count >= 10) { // 假设超过 10 次为超发
            return new MessageResult(false, "消息发送超过限制");
         }
         // 更新消息发送次数记录
         messageCounts.put(request.getMessageContent(), count + 1);

         // 如果不超发,并且有下一个处理器,则传递请求
         if (nextHandler != null) {
            return nextHandler.handleRequest(request);
         } else {
            return new MessageResult(true, "超发校验通过");
         }
      } finally {
         // 释放锁
         lock.unlock();
      }
   }
}

// 客户端代码
public class Client {
   public static void main(String[] args) {
      // 创建责任链节点
      RequiredFieldValidationHandler requiredFieldHandler = new RequiredFieldValidationHandler();
      DataValidationHandler dataHandler = new DataValidationHandler();
      BlacklistValidationHandler blacklistHandler = new BlacklistValidationHandler();
      OverdraftValidationHandler overdraftHandler = new OverdraftValidationHandler();

      // 设置责任链节点的顺序
      requiredFieldHandler.setNextHandler(dataHandler);
      dataHandler.setNextHandler(blacklistHandler);
      blacklistHandler.setNextHandler(overdraftHandler);

      // 初始化黑名单
      Set<String> blacklist = new HashSet<>();
      blacklist.add("spam");
      blacklist.add("virus");
      blacklistHandler.setBlacklist(blacklist);

      // 创建消息请求
      MessageRequest request = new MessageRequest("Important message");

      // 处理消息请求
      MessageResult result = requiredFieldHandler.handleRequest(request);
      System.out.println("处理结果: " + result.isSuccess() + ", " + result.getMessage());
   }
}

Read more

如何设计秒杀系统

什么是秒杀设计 秒杀设计 是一种针对高并发、大流量场景的系统设计,通常应用于电商活动中(如限时抢购、促销等),用户在非常短的时间内大量涌入系统,抢购有限的商品或优惠。这种场景下,系统需要能够承受巨大的瞬时并发请求,同时保证数据的一致性和业务的正确性。 秒杀技术分析 秒杀系统贯穿活动、商品和下单三个领域,涵盖了页面静态化、接口限流、Redis预减库存、异步下单和接口动态化等技术。 需要迎接的挑战有: 1. 高并发和压力测试:秒杀活动会带来巨大的流量,服务器和数据库的并发处理能力是关键。 2. 保证数据一致性:抢购涉及到商品库存的实时减少,需要保证在高并发场景下库存数据的准确性和一致性 3. 防止超卖和重复购买:确保同一商品不会被重复购买,同时避免超卖,即使是在极端的高并发情况下 4. 分布式锁和限流:使用分布式锁来保护关键资源,限制用户访问频率以免系统崩溃 5. 性能优化:包括代码层面的优化、数据库的优化、缓存的使用等,以提高系统性能和响应速度。 页面静态化 秒杀页面静态化是将动态生成的秒杀页面转换为静态HTML页面,从而提高页面响应速度和系统性能

By Yucan Huang

分布式系统下雪花算法生成全局唯一ID

雪花算法 在分布式系统中,为了避免多个节点生成相同的唯一标识符id,我们通常使用一种全局唯一ID生成策略,而Snowflake Algorithm就是广泛使用的解决方案之一。 雪花算法简介 雪花算法生成的 ID 是一个 64 位的二进制整数,具有以下组成部分: 部分 字节长度 描述 符号位 1bit 固定为0,因为生成的ID是整数 时间戳 41bits 表示时间戳,单位是毫秒,可以存储大约68年的时间 机器ID 10bits 用于标识不同的机器(节点)。10位可以将其分数据中心ID(5位)和机器ID(5位),共支持1024台机器同时生成ID 序列号 12bits 同一毫秒内的序列号,用来区分在同一台机器、同一时间戳下生成的多个ID,最大支持同一毫秒生成4096个唯一ID 代码实现 package com.can.springbootmessage; public class SnowflakeIdGenerator { // 起始时间戳,Long是64位,

By Yucan Huang
外卖点餐系统07

外卖点餐系统07

缓存菜品 问题说明 用户通过微信小程序查询菜品,小程序会将请求发送给后端服务,后端就会查询MySQL数据库。 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大,就会造成系统响应慢,用户体验差。 解决思路 * 通过Redis来缓存菜品数据,减少数据库查询操作。 微信小程序查询数据后,会向后端服务发送请求,判断请求的数据在缓存中是否存在,如果存在直接读取缓存,不存在再查询MySQL,并将该数据载入缓存。 * 缓存逻辑分析 微信小程序展示菜品粒度是根据分类展示,所以缓存数据应该根据分类缓存菜品。 1. 每个分类下的菜品保存一份缓存数据 * 使用分类id作为key,分类下的菜品数据使用String字符串保存,分类下的菜品应该是List集合,在Java中可以通过序列化将这个集合转换成Redis的字符串类型 2. 数据库中菜品数据有变更时及时清理缓存数据 代码开发 1. 修改用户端接口 DishContr

By Yucan Huang