目录

  • 设计原因
  • 架构流程图
  • 架构分析
  • 设计模式
  • ApiFactory
  • AbstractApiTemplate
  • 二、问题记录
  • 缓存
  • 线程池
  • Redis
  • threadlocal
  • rabbitMQ
  • docker
  • 三、总结


设计原因

前几个月,公司有一个新项目的对外api接口需求,我在里面扮演api的架构设计。起初,拿到需求并不急于编写代码,而是从软件工程的设计角度出发,满足可扩展和开闭原则,设计了这个api的接口。

架构流程图

单体架构技术选型 单体结构设计_设计模式

架构分析

设计模式

使用设计模式开发软件,能够让我们写出的类更能见名知其意,比如SQLSessionFactory是创建SQL会话的工厂类,APiFactory类是创建api对象的工厂类。同时,设计模式能够实现高内聚和低耦合。

但是,我们不能为了设计模式而写将代码模式化,主要是灵活运用设计模式,往往在代码架构中,不止使用一个设计模式,多种设计模式相互关联。

ApiFactory

这是创建型简单工厂设计模式,这种设计模式简单易用,但是耦合度比较大,如果有心的接口api需求过来,那么就要修改APIFactory内部的结构。

该类有一个重要原子性的属性noInstance,默认是false。仿照 J.U.C报下的CAS模式,创建名为compareAndSetState方法签名, 该方法内的逻辑是,如果工厂类已创建,将该属性置位true。

@Component
public class ApiFactory {

    private volatile static ApiFactory instance = null;
    private static volatile boolean noInstance = false;

    @Bean
    public ApiFactory ApiFactory() {
        return ApiFactory.getInstance();
    }

    public static ApiFactory getInstance() {
        if (noInstance) {
            instance = new ApiFactory();
            compareAndSetState();
            if (noInstance) {
                return instance;
            }
            instance = new ApiFactory();
        }
        return instance;
    }

    private static void compareAndSetState() {
        if (null != instance) {
            noInstance = true;
        }
    }
    
    public AbstractApiTemplate createApi(HttpServletRequest request, PoolStrategy poolStrategy) {
        if (poolStrategy.equals(POOL_STRATEGY_QUERY_USER_STATUS)) {
            return new UserStatusApi(request);
        } else if (poolStrategy.equals(POOL_STRATEGY_QUERY_ORDER_STATUS)) {
            return new TaskOrderApi(request);
        } else if (poolStrategy.equals(POOL_STRATEGY_REMIT)) {
            return new RemitApi(request);
        } else if (poolStrategy.equals(POOL_STRATEGY_UPLOAD)) {
            return new UploadApi(request);
        } else if (poolStrategy.equals(POOL_STRATEGY_AUTH)) {
            return new AuthApi(request);
        } else if (poolStrategy.equals(POOL_STRATEGY_SIGN)) {
            return new SignApi(request);
        } 
        return null;
    }
}

使用无锁状态下实现单例创建工厂,并使用@bean返回api工厂对象。

AbstractApiTemplate

该模板是是实现各个api接口的父类,在父类中做统一的服务管理:

  1. 入参校验:采用MD5进行数据加签。使用ThereadLocal存储入参, protected ThreadLocal<JSONObject> paramsCache = new ThreadLocal<>();,因为改参数只在当前线程有效。使用完毕后及时调用 paramsCache.remove();方法, 以免内存泄漏。
  2. IP校验。当spring服务启动后,即从数据库加载IP并存储到 public static ConcurrentHashMap<String, CopyOnWriteArrayList<String>> ipMapping = new ConcurrentHashMap();如果用户服务器请求的IP不在ipMapping 当中,此时去数据库中查询是否配置了该公司商户的IP,,如果配置了【缓存击穿】,并写入到ipMapping 之中,否则【缓存穿透】,返回调用者IP不存在的异常。
  3. 限流:使用将对应的接口以key的形式存储到Redis中,如果value值大于指定的数值,则返回调用者通道繁忙,稍后重试,并设置失效时间。
  4. 日志记录
  5. 接口超时时间设置

比如配置IP的代码如下:

private JSONObject configIp(String... ignores) {
        JSONObject jsonObject = checkBlank(ignores);
        if (!SUCCESS.getCode().equals(jsonObject.getString(RET_CODE))) {
            return jsonObject;
        }
        DaoRegister register = DaoRegister.getInstance();
        if (ipMapping.size() < 1) {
            register.init(request);
        }
        String maskCode = paramsCache.get().getString(API_MASK_CODE);
        String ipAddress = getIpAddr(request);
        if (!ipMapping.containsKey(maskCode)) {
            jsonObject.replace(RET_CODE, IP_NOT_REPORT.getCode());
            jsonObject.replace(RET_MSG, IP_NOT_REPORT.getMsg());
            return jsonObject;
        }
        CopyOnWriteArrayList<String> coaList = ipMapping.get(maskCode);
        for (String ip : coaList) {
            if (ipAddress.equals(ip)) {
                return jsonObject;
            }
        }
//        如果不包含,再刷新数据库,是否缓存造成
        boolean refresh = register.getIpDao(request).refresh(ipAddress, maskCode);
        if (refresh) {
            jsonObject.replace(RET_CODE, IP_NOT_REPORT.getCode());
            jsonObject.replace(RET_MSG, IP_NOT_REPORT.getMsg());
            return jsonObject;
        }
        coaList.add(ipAddress);
        ipMapping.replace(maskCode, coaList);
        return jsonObject;
    }

二、问题记录

缓存


线程池


Redis


threadlocal


rabbitMQ

docker


三、总结