技术架构:简单工厂设计模式 单体设计模式 ConcurrentHashMap缓存 threadlocal参数共享

设计原因

前几个月,公司有一个新项目的对外api接口需求,我在里面扮演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;
    }

二、问题记录

缓存

参考地址:https://blog.csdn.net/lvoelife/article/details/108561287

线程池

参考我的博客:https://blog.csdn.net/lvoelife/article/details/108567110

Redis

参考我的博客:https://blog.csdn.net/lvoelife/article/details/108563113

threadlocal

参考我的博客:https://blog.csdn.net/lvoelife/article/details/108566362

rabbitMQ

docker

https://blog.csdn.net/lvoelife/article/details/108575897

三、总结

首先,非常感谢公司给我这个机会让我做架构设计。

在架构的过程中,叶遇到了各种问题。比如如何选择线程池的拒绝策略,如何选择核心线程数的大小,用户上传的图片过大造成响应时间过长等等。问题来了并不可怕,可怕的是向问题妥协,日日学,日日新。不逃避,迎面而上。人总得要尝试去做挑战自己的事情,这样才能有所成长。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网全栈开发实战

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值