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

导读:本篇文章讲解 技术架构:简单工厂设计模式 单体设计模式 ConcurrentHashMap缓存 threadlocal参数共享,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

设计原因

前几个月,公司有一个新项目的对外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

三、总结

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/99252.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!