[XChat-Server开发笔记]2019.01.28 – 30

看到此文,是否觉得体内洪荒之力爆发,饥渴难耐想吐槽、情不自禁想捐赠
本文为原创文章,尊重辛勤劳动,可以免费摘要、推荐或聚合,亦可完整转载,但完整转载需要标明原出处,违者必究。

支付宝微  信
在👆上面,可以找到[XChat-Server开发笔记]主题相关的所有文章

1. 未登录的客户端线程池

采用单例模式(Singleton Parttern)设计未登录的客户端线程池,采用静态内部类的形式保证其线程安全。之所以静态内部类能够保证单例模式的线程安全,是因为Java在底层针对类的加载已经做了同步处理,已经是线程安全的了。

这种方式的优点:

  • 懒加载,只有在需要的时候进行加载,节省资源
  • 在多线程共享使用一个单例时,确保线程安全

关于线程安全下的单例模式的使用,参考文章:http://blog.ihuxu.com/java-singleton-pattern-usages-for-thread-safe/

1.1 客户端线程池的主要数据结构

1.1.1 基于链表的阻塞队列(Linked Blocking Queue)

需要声明一个数据结构来存放“未登录的客户端”(以下简称“数据”),对于其中的“数据”操作有如下特点:

  • 先加入的“数据”优先处理,后加入的“数据”次级处理
  • 加入的数据量要有一定的限制
  • 获取数据时,如果有那么返回,如果没有就阻塞住,直到有数据时再返回
  • 添加数据时,如果容量已经达到上限,那么久阻塞,并等待一定时间,如果在达到时间上限之前有可用容量,那么添加数据;否则,报错异常。

根据以上特点,采用BlockingQueue(阻塞队列)协议实现的LinkedBlockingQueue来实现。这种队列是线程安全的,以为底层针对他的操作时已经做了同步的机制。

1.1.1 线程安全的ConcurrentHashMap集合

上面的“阻塞队列”数据结构并不能满足我的需求。因为我需要迭代当前容器中的“未登录客户端”,但是队列却不能。

针对于线程安全的集合,比如ConcurrentHashMap是没问题的。但是我有个疑问,这个集合如何保证并发的情况下制定最大的容器大小?看来是个难题,容我了解一下。

最后选定ConcurrentHashMap数据结构,这个数据结构是线程安全的。但是,这里面还有另一个问题。如何保证这个容器有一个固定的容量上限?

这个问题和“秒杀”的思路特别像,可以用数据库或者队列的原子性来维护一个固定最大容量的容器。但是,项目中并不打算用数据库或者队列这样的外部资源,所以我们就用一个静态变量来实现。那么对于静态变量,在java中也是非线程安全的?如何保证其线程安全呢?

可以使用java.util.concurrent.adomic包下的AtomicInteger类实现具有原子性的操作。

将容器改成了ConcurrentSkipListSet,并使用AdomicInteger类实现了容器的指定上限。

参考文章:探索 ConcurrentHashMap 高并发性的实现机制

1.2 客户端线程的抽象化

打算按照客户端的状态将客户端的线程进行抽象,比如登录状态(LoggedClient),未登录状态(NotLoggedClient)等等。

由此一来,需要构建一下客户端的状态流转过程。这个状态流转用树型数据结构进行存储,好处在于:

  1. 能够回溯当前客户端整个证明周期的过程(如果有必要)
  2. 将状态抽象成树型的数据结构,容易理解整个生命周期
  3. 将状态抽象成树型的数据结构,可以针对不同分支下的同一个状态进行差异化处理(树型结构下,不同分支下的相同状态会用不同的状态码表示)

状态机树型数据

{
    "status":0,
    "desc":"未登录",
    "parent":-1,
    "next":[
        {
            "status":16777216,
            "desc":"已登录",
            "parent":0,
            "next":[
                {
                    "status":33554432,
                    "desc":"登录失败",
                    "parent":0,
                    "next":[

                    ]
                },
                {
                    "status":33554433,
                    "desc":"断开",
                    "parent":0,
                    "next":[

                    ]
                }
            ]
        },
        {
            "status":16777217,
            "desc":"登录失败",
            "parent":0,
            "next":[

            ]
        },
        {
            "status":16777218,
            "desc":"断开",
            "parent":0,
            "next":[

            ]
        }
    ]
}

同时,也参考下状态机的概念和实现方案:

有限状态机(英语:finite-state machine,縮寫:FSM)又稱有限状态自动,简称状态机,是表示有限状态以及在这些状态之间的转移和动作等行为的数学模型。

相关示例代码:https://github.com/elimisteve/fsm/blob/master/fsm.go

1.2.1 状态机设计

状态值

状态值用32位的有符号(int)整型表示,高8位代表所在的树的深度(根的深度为0),低24位代表当前层级的序号(序号自增,从0开始)

构建工具的引入

在网络上搜索了主要的构建工具,目前比较流行的是Gradle,索性就她了。

在现有的系统上,通过增加脚本的方式来构建当前项目,却总是提示“包不存在”错误。我想应该是哪里的初始化配置不正确,折腾了大几个小时,看了很多文章,都不行。

回想起在网吧做网管时的绝招,重启!最后,索性全部删除从新使用Eclipse的图形工具进行初始化Gradle项目,成功。

构建源

https://mvnrepository.com/repos/jcenter

Json依赖库的使用

网络上搜索了json的库,相中了Google的Gson。

官方文档:https://github.com/google/gson

状态机函数

  • 构建状态机
  • 遍历状态机

Junit测试状态机

搞定


这是一篇原创文章,如果您觉得有价值,可以通过捐赠来支持我的创作~
捐赠者会展示在博客的某个页面,钱将会用在有价值的地方,思考中...


分类: JAVA, 技术, 随记 | 标签: , , , , , | 评论 | Permalink

发表评论

电子邮件地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据