[Abp vNext 入坑分享] – 2.简化项目结构

一、简要说明

本篇文章根据我自己的需要对项目结果进行简化,让项目结构更符合我自己的要求,同时让项目跑起来。仅供参考

二、具体步骤

2.1卸载掉对我来说目前使用不上的项目,identityserver,mongodb,httpapi.client,以及对应的test项目

 

 

 

 

2.2删除掉下图红框中的包与类文件,由于我后期会创建一个独立的项目来做migrations,所以不需要在HttpApi.Host里面直接使用EF相关的操作,而且个人认为这样会模糊掉abpvnext的层级,混乱了层级的职责。

 

 

 

上图的报错都是由于我删除了相关的包引起的,因此跳转到相关文件中,把所有的报错行,全部删除。同时由于我没有启用redis的服务,所以要把下图的redis服务也先行注释掉。

2.3将启动模式修改成:如下图,同时把原来输出日志到文件的模式修改成:console(),以便在控制台时能很直观的看到Log。调试项目,则可以启动成功了。

 

 

 

 

 

2.4在src下面增加DbMigrations类库,注意:此处只能选择.netcore类型的类库,不能是只属于netstandard这样会导致无法使用。创建后的样子如下,这样就可以使用此项目进行migration操作了,且不会影响主线代码。

 

 

 

1.关于DbM_LearnDbContext这个类,如果你能保证整个项目的所有开发人员都只能使用codefirst进行开发的话,可以直接继承主的LearnDbContext,这样LearnDbContext的所有DbSet都会得到继承,则不需要再重复写dbset。

2.若是codefirstdbfirst混用的情况,则不要继承

3.单纯dbfirst的话,此项目可以去掉

Unity ML-agents 一、初次尝试

前言

曾在高二寒假的时候,跟表哥在外面玩,当时他问我有没有想过以后要做什么,我愣了一下,回答不上来。是的,从没想过以后要做什么,只是一直在完成学校、老师安排的任务,于是那之后半年,我一直在思考,大学要学什么。在大二下期中之后,我觉得自己还是对游戏更感兴趣,便想到以后想做游戏。于是,高考后填志愿,填的都是计算机专业。在大一的时候,自学了一段时间的 Unity,到大二在实验室接触强化学习之后,就想着用 RL 来做游戏 AI,但后来一直在做数据挖掘相关的内容,基本上以参加比赛为主。直到去年参加上海的谷歌开发者节,了解到 ML-agents 之后,就十分的想尝试。

然后由于疫情,直到现在还在家里咸鱼,前段时间一边咸鱼一边投简历,奈何自己水平太低又偏偏想投算法岗,直到现在也没有几次面试机会 orz。最近就想继续当年未完成的 Unity 的学习,顺便学习 ML-agents,回到原点,重新出发。

初试 ML-agents

环境配置

既然要尝试,肯定免不了环境配置
目前我的环境为:

  1. Win10
  2. Tensorflow 2.0
  3. ML-agents 0.15.0
  4. Unity 2019.3.1f1
    关于 Tensorflow 2.0 的安装,参考我之前的博客,Unity 的安装,推荐先下载 Unity Hub,通过 Unity Hub 可以管理不同版本的 Unity,下载戳这里

ML-agents 的安装有两种方式
一是直接 pip

pip install mlagents

二是从官方 github 中 clone 整个项目,然后 cd 到目录中

pip install -e ./ml-agents-envs
pip install -e ./ml-agents

到这里,环境配置就算完成了

跑个 Demo 先吧

在 Unity Hub 中导入刚刚从 github 上 clone 的项目

选择 Unity 版本之后打开

然后打开 3DBall 这个场景

点击运行的话可以直接看到效果

接着,开始尝试自己训练,打开命令行,进入到之前 clone 下来的项目目录中,并在目录中创建一个名为 summaries 的文件夹

然后输入

mlagents-learn config/trainer_config.yaml --run-id=test --train

在出现图标之后,切换到 Unity 运行项目,就可以看见开始训练了

可以看出来一开的效果是很差的,完全控制不好,这次训练大概训练 40w+ 步,到后面就很稳了

注:项目目录中尽量不要出现中文,下午在尝试的时候一直报错,后来更改目录之后发现成功了,不清楚是不是路径中有中文的原因
报错内容

 "The Unity environment took too long to respond. Make sure that :\n"
mlagents_envs.exception.UnityTimeOutException: The Unity environment took too long to respond. Make sure that :
         The environment does not need user interaction to launch
         The Agents are linked to the appropriate Brains
         The environment and the Python interface have compatible versions.

训练完成后,我们可以在 models 目录下看到刚刚训练好的模型,重命名一下,然后把模型拖到 TFModel 目录中

接着打开 prefabs 中的 3DBall,点击其中的 agent,然后将 Behavior 改成我们刚刚拖到 TFModel 目录中的模型

点击运行,发现控制的十分稳定,跟一开始差不多。至此,初试完毕。

小节

这里我们安装配置了 ML-agnets 的相关环境,并运行了个 Demo 来熟悉了遍流程,后面将开始尝试自己搭建环境,训练 AI,也不知道能不能捣鼓出来,23333。

kafka消息分区机制原理

背景

kafka如何支撑海量消息的集中写入?

答案就是消息分区。

核心思想是:负载均衡,采用合适的分区策略把消息写到不同的broker上的分区中;

其它的产品中有类似的思想。

比如monogodb, es 里面叫做 shard;   hbase叫region,  cassdra叫vnode;

消息的三层结构

如下图:

即  topic -> partition -> message ;

topic是逻辑上的消息容器;

partition实际承载消息,分布在不同的kafka的broke上;

message即具体的消息。

分区策略

round-robin轮询

消息按照分区挨个的写。

randomness随机分区

随机的找一个分区写入,代码如下:

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return ThreadLocalRandom.current().nextInt(partitions.size());

key

相同的key的消息写到固定的分区中

自定义分区

必须完成两步:

1,自定义分区实现类,需要实现org.apache.kafka.clients.producer.Partitioner接口。

主要是实现下面的方法:

int partition(String topic, Object key, byte[] keyBytes, 
              Object value, byte[] valueBytes, Cluster cluster);

比如按照区域分区。

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return partitions.stream().filter(p -> isSouth(p.leader().host()))
    .map(PartitionInfo::partition).findAny().get();

2,显示配置生产者端的参数partitioner.class为具体的类

系统默认:如果消息有key,按照key分区策略,否则按照轮询策略。

小结

kafka的分区实现消息的高吞吐量的主要依托,主要是实现了写的负载均衡。可以指定各种负载均衡算法。
负载均衡算法非常重要,需要极力避免消息分区不均的情况,可能给消费者带来性能瓶颈。

小结如下:

原创不易,点赞关注支持一下吧!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。
我会持续分享Java软件编程知识和程序员发展职业之路,欢迎关注,我整理了这些年编程学习的各种资源,关注公众号‘李福春持续输出’,发送’学习资料’分享给你!

Volatile的应用DCL单例模式(四)

Volatile的应用

单例模式DCL代码

首先回顾一下,单线程下的单例模式代码

/**
 * 单例模式
 *
 * @author xiaocheng
 * @date 2020/4/22 9:19
 */
public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    public static void main(String[] args) {
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        System.out.println(Singleton.getInstance() == Singleton.getInstance());
    }
}

最后输出的结果

但是在多线程的环境下,我们的单例模式是否还是同一个对象了

/**
 * 单例模式
 *
 * @author xiaocheng
 * @date 2020/4/22 9:19
 */
public class Singleton {

    private static Singleton singleton = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

从下面的结果我们可以看出,我们通过SingletonDemo.getInstance() 获取到的对象,并不是同一个,而是被下面几个线程都进行了创建,那么在多线程环境下,单例模式如何保证呢?

解决方法1

引入synchronized关键字

    public synchronized static SingletonDemo getInstance() {
        if(instance == null) {
            instance = new SingletonDemo();
        }
        return instance;
    }

输出结果

我们能够发现,通过引入Synchronized关键字,能够解决高并发环境下的单例模式问题

但是synchronized属于重量级的同步机制,它只允许一个线程同时访问获取实例的方法,但是为了保证数据一致性,而减低了并发性,因此采用的比较少

解决方法2

通过引入DCL Double Check Lock 双端检锁机制

就是在进来和出去的时候,进行检测

    public static SingletonDemo getInstance() {
        if(instance == null) {
            // 同步代码段的时候,进行检测
            synchronized (SingletonDemo.class) {
                if(instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

最后输出的结果为:

从输出结果来看,确实能够保证单例模式的正确性,但是上面的方法还是存在问题的

DCL(双端检锁)机制不一定是线程安全的,原因是有指令重排的存在,加入volatile可以禁止指令重排

原因是在某一个线程执行到第一次检测的时候,读取到 instance 不为null,instance的引用对象可能没有完成实例化。因为 instance = new SingletonDemo();可以分为以下三步进行完成:

  • memory = allocate(); // 1、分配对象内存空间
  • instance(memory); // 2、初始化对象
  • instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null

但是我们通过上面的三个步骤,能够发现,步骤2 和 步骤3之间不存在 数据依赖关系,而且无论重排前 还是重排后,程序的执行结果在单线程中并没有改变,因此这种重排优化是允许的。

  • memory = allocate(); // 1、分配对象内存空间
  • instance = memory; // 3、设置instance指向刚刚分配的内存地址,此时instance != null,但是对象还没有初始化完成
  • instance(memory); // 2、初始化对象

这样就会造成什么问题呢?

也就是当我们执行到重排后的步骤2,试图获取instance的时候,会得到null,因为对象的初始化还没有完成,而是在重排后的步骤3才完成,因此执行单例模式的代码时候,就会重新在创建一个instance实例

指令重排只会保证串行语义的执行一致性(单线程),但并不会关系多线程间的语义一致性

所以当一条线程访问instance不为null时,由于instance实例未必已初始化完成,这就造成了线程安全的问题

所以需要引入volatile,来保证出现指令重排的问题,从而保证单例模式的线程安全性

private static volatile SingletonDemo instance = null;

最终代码

/**
 * 单例模式
 *
 * @author xiaocheng
 * @date 2020/4/22 9:19
 */
public class Singleton {

    private static volatile Singleton singleton = null;

    private Singleton() {
        System.out.println(Thread.currentThread().getName() + "\t单例构造方法");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    public static void main(String[] args) {
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
//        System.out.println(Singleton.getInstance() == Singleton.getInstance());
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Singleton.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

如何将你的 Vue.js 项目部署在云开发静态托管之上

云开发静态托管是云开发提供的静态网站托管的能力,静态资源(HTML、CSS、JavaScript、字体等)的分发由腾讯云对象存储 COS 和拥有多个边缘网点的腾讯云 CDN 提供支持.

在云开发静态托管中,你同样可以托管一个 Vue.js 项目,接下来,我就介绍一下应该如何将一个 Vue.js 项目部署到云开发静态网站托管服务中。

初始化一个 Vue 项目

首先,使用 Vue cli 创建一个项目,来作为演示。同时,为了符合日常使用场景,引入 Vue Router 组件,并配置 Router 为 Histroy 模式。

vue create cloudbase
cd cloudbase
vue add router

执行完成后,可以执行 npm run serve 启动预览,查看一下效果

创建云开发环境

完成了Vue 项目的创建后,接下来创建云开发的环境,访问云开发控制台,点击上方的新建环境,创建一个新的环境。在弹出的界面中输入你的环境名称,并选择按量计费,点击下方的立即开通,就可以开通一个云开发环境了。

等待环境初始化完成后,点击刚刚创建好的环境,进入到详情页,点击左侧的环境设置,可以看到环境的 ID, 记住这里的环境 ID,后续上传文件的时候会用到。

再次选择左侧列表的「静态网站托管」

在静态网站托管页面选择立即开通。

等待静态网站托管服务开通后,你就可以看到这样的界面。点击上方的「设置」,可以看到你的测试域名,后续上传后,你就可以在这个测试域名中查看你的站点。

初始化云开发 Cli

完成了环境的创建后,接下来配置云开发 Cli。

安装云开发 Cli 并登陆

首先,我们执行命令安装云开发 Cli

npm i -g @cloudbase/cli

安装完成后, 执行命令登陆 Cli

tcb login

系统会自动打开浏览器,你只需要在弹出的页面中登陆你的腾讯云账号,并授予 Cli 权限就可以操作了。

上传文件

完成了 Cli 的登陆后,接下来就可以上传文件了。首先,进入到 vue 项目的 dist 目录:

cd dist

,然后,执行命令来上传文件

tcb hosting:deploy -e envId

这里你需要将 envId 替换为你自己的环境 ID,比如我的替换为 website-126ca8,结果如下

可以看到,我成功的上传了文件,这个时候,我可以直接访问我的测试域名来查看我刚刚上传的 Vue.js 项目。

当你看到这样的界面时,就说明你配置成功了。

一些配置

在 Vue 中,我们常常会用到 Vue Router 的 History Mode 来做更好的 URL,但如果你不做任何配置,在云开发的 Hosting 上就会导致访问时出现 404 错误

这个问题可以通过在云开发静态网站托管的设置页面将索引文档和错误文档均设置为 index.html 即可解决。

总结

云开发的静态托管中想要上传 Vue 项目也十分简单,你只需要初始化一个 Vue 项目,并使用云开发的 CLi 工具就可以完成文件的上传。此外,还可以通过修改索引文档和错误文档来实现 Vue Router 的 History Mode 的支持。

公众号:腾讯云云开发

腾讯云云开发:https://cloudbase.net

云开发控制台:https://console.cloud.tencent.com/tcb?from=12304

更多精彩
扫描二维码了解更多