Dropwizard,MongoDB和Gradle实验

介绍

我使用Dropwizard,MongoDB和Gradle创建了一个小项目。 它实际上是从一个实验性的Guava缓存开始的,作为将计数器发送到MongoDB(或任何其他DB)的缓冲区。 我也想尝试MondleDB插件的Gradle。 接下来,我想创建某种接口来检查此框架,因此我决定尝试使用DropWizard。 这就是该项目的创建方式。

本文不是使用任何选定技术的教程。 这是一个小展示柜,我做过实验。 我猜有一些缺陷,也许我没有使用所有“最佳实践”。 但是,我确实相信,在本文的帮助下,该项目可以成为我使用的各种技术的良好起点。 我还尝试显示一些设计选择,这些选择有助于实现SRP,去耦,内聚等。

我决定从用例描述及其实现开始。 之后,我将解释我对Gradle,MongoDB(和嵌入式)和Dropwizard所做的工作。

在开始之前,这里是源代码:

  • https://github.com/eyalgo/CountersBuffering

用例:带缓冲区的计数器

我们对服务器有一些输入请求。 在请求过程中,我们选择用一些数据(由某些逻辑决定)“绘制”它。 某些请求将由Value-1绘制,某些请求将由Value-2绘制,等等。有些将根本不会绘制。 我们要限制绘画请求的数量(每个绘画值)。 为了有限制,对于每个绘制值,我们知道最大值,但是还需要计算(每个绘制值)绘制请求的数量。 由于系统具有多个服务器,因此计数器应由所有服务器共享。

延迟至关重要。 通常,每个请求处理会得到4-5毫秒(对于所有流程。不仅仅是绘画)。 因此,我们不希望增加计数器会增加延迟。 相反,我们将保留一个缓冲区,客户端将向缓冲区发送“增加”。 缓冲区将定期以“批量增量”增加存储库。

我知道可以直接使用Hazelcast或Couchbase或其他类似的快速内存数据库。 但是对于我们的用例,那是最好的解决方案。

原理很简单:

  • 从属模块将调用服务以增加某个密钥的计数器
  • 该实现为每个键保留计数器的缓冲区
  • 这是线程安全的
  • 编写在单独的线程中进行
  • 每次写入都会大量增加
柜台高级设计

柜台高级设计

缓冲

对于缓冲区,我使用了Google Guava cache 。

缓冲结构

创建缓冲区:

private final LoadingCache<Counterable, BufferValue> cache;
...this.cache = CacheBuilder.newBuilder().maximumSize(bufferConfiguration.getMaximumSize()).expireAfterWrite(bufferConfiguration.getExpireAfterWriteInSec(), TimeUnit.SECONDS).expireAfterAccess(bufferConfiguration.getExpireAfterAccessInSec(), TimeUnit.SECONDS).removalListener((notification) -> increaseCounter(notification)).build(new BufferValueCacheLoader());
...

可逆的描述如下)

BufferValueCacheLoader实现了CacheLoader接口。 当我们调用增加(见下文)时,我们首先通过键从缓存中获取。 如果键不存在,则加载器返回值。

BufferValueCacheLoader:

public class BufferValueCacheLoader extends CacheLoader<Counterable, BufferValue> {@Overridepublic BufferValue load(Counterable key) {return new BufferValue();}
}

BufferValue包装一个AtomicInteger (在某些时候我需要将其更改为Long)

增加柜台

增加计数器,如果超过阈值则发送:

public void increase(Counterable key) {BufferValue meter = cache.getUnchecked(key);int currentValue = meter.increment();if (currentValue > threashold) {if (meter.compareAndSet(currentValue, currentValue - threashold)) {increaseCounter(key, threashold);}}
}

当增加一个计数器时,我们首先从缓存中获取当前值(在加载程序的帮助下。如上所述)。 compareAndSet将自动检查是否具有相同的值(未被另一个线程修改)。 如果是这样,它将更新该值并返回true。 如果成功(返回true),则缓冲区调用更新程序。

查看缓冲区

开发服务之后,我想要一种查看缓冲区的方法。 因此,我实现了以下方法,该方法由前端层(Dropwizard的资源)使用。 Java 8 Stream和Lambda表达式的小示例。

获取所有计数器在缓存中:

return ImmutableMap.copyOf(cache.asMap()).entrySet().stream().collect(Collectors.toMap((entry) -> entry.getKey().toString(),(entry) -> entry.getValue().getValue()));

MongoDB

我之所以选择MongoDB是因为两个原因:

  1. 我们的系统中有类似的实现,我们决定在那里也使用MongoDB。
  2. 易于与嵌入式服务器一起使用。

我试图设计系统,以便可以选择其他任何持久性实现并进行更改。

我将吗啡用作MongoDB客户端层,而不是直接使用Java客户端。 使用Morphia,您可以创建dao ,它是与MongoDB集合的连接。 您还声明了一个简单的Java Bean(POJO),它表示集合中的文档。 一旦有了dao,就可以使用相当简单的API以“ Java方式”对集合进行操作。 您可以查询和其他任何CRUD操作,以及更多。

我有两个操作:增加计数器和获取所有计数器。 服务实现不扩展Morphia的BasicDAO,而是具有一个继承它的类。 我使用了组合 (过度继承),因为我希望两种服务都具有更多的行为。

为了与键表示保持一致,并从依赖代码中隐藏其实现方式,我使用了一个接口:可通过单个方法counterCount()来 抵消

public interface Counterable {String counterKey();
}

DAO,是服务内部的组成部分:

final class MongoCountersDao extends BasicDAO<Counter, ObjectId> {MongoCountersDao(Datastore ds) {super(Counter.class, ds);}
}

增加柜台

MongoCountersUpdater扩展了实现CountersUpdater的AbstractCountersUpdater:

@Override
protected void increaseCounter(String key, int value) {Query<Counter> query = dao.createQuery();query.criteria("id").equal(key);UpdateOperations<Counter> ops = dao.getDs().createUpdateOperations(Counter.class).inc("count", value);dao.getDs().update(query, ops, true);
}

嵌入式MongoDB

为了在持久层上运行测试,我想使用内存数据库。 有一个MongoDB插件。 使用此插件,您可以通过仅在运行时创建服务器来运行服务器,或者在Gradle中的maven / task中作为目标运行。

  • https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo
  • https://github.com/sourcemuse/GradleMongoPlugin

Gradle上的嵌入式MongoDB

稍后我将详细介绍Gradle,但这是设置嵌入式mongo所需的操作。

dependencies {// More dependencies heretestCompile 'com.sourcemuse.gradle.plugin:gradle-mongo-plugin:0.4.0'
}

设置属性

mongo {//	logFilePath: The desired log file path (defaults to 'embedded-mongo.log')logging 'console'mongoVersion 'PRODUCTION'port 12345//	storageLocation: The directory location from where embedded Mongo will run, such as /tmp/storage (defaults to a java temp directory)
}

嵌入式MongoDB Gradle任务

  • startMongoDb只会启动服务器。 它将运行直到停止它。
  • stopMongoDb将停止它。
  • startManagedMongoDb test ,这两个任务将在测试运行之前启动嵌入式服务器。 jvm完成(测试完成)后,服务器将关闭

尽管我只触碰到冰山一角,但我开始看到Gradle的力量。 设置项目甚至都不是那么困难。

摇篮设置

首先,我在eclipse中创建了Gradle项目(安装插件后)。 我需要设置依赖项。 很简单。 就像行家一样。

一个大的JAR输出

当我想从Maven中的所有库中创建一个大jar时,我会使用shade插件。 我在寻找类似的东西,并发现gradle-one-jar插入。 https://github.com/rholder/gradle-one-jar我添加了该插件apply plugin: 'gradle-one-jar' 。 在类路径中添加了一个jar:

buildscript {repositories { mavenCentral() }dependencies {classpath 'com.sourcemuse.gradle.plugin:gradle-mongo-plugin:0.4.0'classpath 'com.github.rholder:gradle-one-jar:1.0.4'}
}

并添加了一个任务:

mainClassName = 'org.eyalgo.server.dropwizard.CountersBufferApplication'
task oneJar(type: OneJar) {mainClass = mainClassNamearchiveName = 'counters.jar'mergeManifestFromJar = true
}

这些是我需要执行的必要操作,才能使应用程序运行。

Dropwizard

Dropwizard是一堆库,可以轻松快速地创建Web服务器。 它对HTTP使用Jetty,对REST使用Jersey。 它具有其他成熟的库来创建复杂的服务。 它可以用作易于开发的微服务。

正如我在简介中所解释的,我不会介绍Dropwizard的所有功能和/或设置。 有很多的网站。 我将简要介绍为使应用程序运行而执行的操作。

Gradle运行任务

run { args 'server', './src/main/resources/config/counters.yml' }

第一个参数是服务器。 第二个参数是配置文件的位置。 如果不将Dropwizard作为第一个参数,则会收到有关可能选项的错误消息。

positional arguments:{server,check}         available commands

我已经在Gradle部分中展示了如何创建一个jar。

组态

在Dropwizard中,您可以使用扩展Configuration的类来设置应用程序。 类中的字段应与yml配置文件中的属性对齐。

优良作法是根据属性的用途/职责将其分组。 例如,我为mongo参数创建了一个组。

为了使配置类正确读取子组,您需要创建一个与该组中的属性对齐的类。

然后,在主配置中,将该类添加为成员,并使用批注进行标记: @JsonProperty

例:

@JsonProperty("mongo")
private MongoServicesFactory servicesFactory = new MongoServicesFactory();
@JsonProperty("buffer")
private BufferConfiguration bufferConfiguration = new BufferConfiguration();

示例:更改端口

这是配置文件的一部分,用于设置应用程序的端口。

server:adminMinThreads: 1adminMaxThreads: 64applicationConnectors:- type: httpport: 9090adminConnectors:- type: httpport: 9091

健康检查

Dropwizard提供了开箱即用的基本管理API。 我将端口更改为9091。我为MongoDB连接创建了运行状况检查。 您需要扩展HealthCheck并实施检查方法。

private final MongoClient mongo;
...
protected Result check() throws Exception {try {mongo.getDatabaseNames();return Result.healthy();} catch (Exception e) {return Result.unhealthy("Cannot connect to " + mongo.getAllAddress());}
}

其他功能几乎是不言自明的,或者像任何入门教程一样简单。

增强想法

这些是我可能会尝试添加的内容。

  • 将测试添加到Dropwizard部分。
    该项目以PoC开头,因此与往常不同,我跳过了服务器部分中的测试。
    Dropwizard拥有“ 测试Dropwizard” ,我想尝试一下。
  • 不同的持久性实现。 (couchbase?Hazelcast?)。
  • 使用Google Guice进行注射。 并在此帮助下,注入不同的持久性实现。

就这样。 希望能有所帮助。

  • 源代码: https : //github.com/eyalgo/CountersBuffering

翻译自: https://www.javacodegeeks.com/2015/02/dropwizard-mongodb-and-gradle-experimenting.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360451.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在wp中,使用NavigationService.Navigate导航页面出现错误

我们在WP项目中采用页面导航时候&#xff0c;经常会使用以下代码 NavigationService.Navigate(new Uri("/Page1.xaml",UriKind.Relative));但是&#xff0c;有的时候会出现错误&#xff1a; "Error 1 An object reference is required for the non-static field…

SSDT – Error SQL70001 This statement is not recognized in this context-摘自网络

March 28, 2013 — arcanecode One of the most common errors I get asked about when using SQL Server Data Tools (SSDT) Database Projects is the error “This statement is not recognized in this context”. This is actually a pretty simple error to fix. Envisi…

带有JAX-RS和PrimeFaces的RESTful图表

通常&#xff0c;利用图表提供数据的可视表示很有用。 PrimeFaces提供制图解决方案&#xff0c;可轻松将数据的可视表示形式添加到Web和移动应用程序中。 如果将PrimeFaces图表组件与RESTful Web服务数据结合使用&#xff0c;我们可以创建自定义图表&#xff0c;以适合桌面和移…

UVAlive 6131 dp+斜率优化

这道题和06年论文《从一类单调性问题看算法的优化》第一道例题很相似。 题意&#xff1a;给出n个矿的重量和位置&#xff0c;这些矿石只能从上往下运送&#xff0c;现在要在这些地方建造m个heap&#xff0c;要使得&#xff0c;sigma距离*重量最小。 思路&#xff1a;O(n ^ 3)的…

c语言程序整数四则运算,c语言中三个整数随机的四则运算

满意答案hors10722014.01.06采纳率&#xff1a;58% 等级&#xff1a;12已帮助&#xff1a;18274人#include #include #include #define N 10 //随机出10道题目int main(){int num1, num2, num3, count0, result,resultTrue,flag;//result:用户输入结果 resultTrue:正确结果 …

带有调试器的Apache Camel Eclipse工具

大约2个月前&#xff0c; Lars Heineman在 JBoss工具堆栈中的博客中介绍了改进的Apache Camel Eclipse工具。 在即将发布的版本中&#xff0c;他们将Camel调试器与本机Eclipse调试器集成在一起&#xff0c;因此当您使用断点时&#xff0c;您将获得Eclipse调试体验&#xff0c;…

HarmonyOS的功能及场景应用

一、基本介绍 鸿蒙HarmonyOS主要应用的设备包括智慧屏、平板、手表、智能音箱、IoT设备等。具体来说&#xff0c;鸿蒙系统是一款面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统&#xff0c;能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备…

SX-BOX试用笔记

1.今天开始试用SX-BOX&#xff0c;我将用它来做自己平时的开发和文档的工作&#xff0c;因为我的职责是一个程序员。 1.屏幕如果支持更大的也清晰的话&#xff0c;我会感觉很舒服。 2.USB设备插入还无法识别。。。 3.现在开始安装开发工具&#xff0c;目前我打算在上面安装Adob…

c语言成绩转换绩点,如何将平时成绩转化为GPA成绩?

新东方留学生资料下载姓名手机号短信验证码图片验证码年级请选择5年级6年级7年级8年级9年级高一高二高三高三复习大一大二大三大四大五研一研二研三博一博二在职课程请选择雅思托福GREGMATSAT其他行政区请选择请选择请选择{"name":{"label":"姓名&quo…

Hibernate查询缓存如何工作

介绍 既然我已经介绍了实体和集合缓存&#xff0c;现在该研究查询缓存的工作原理了。 查询缓存与实体严格相关&#xff0c;它在搜索条件和满足该特定查询过滤器的实体之间绘制关联。 像其他Hibernate功能一样&#xff0c;查询缓存也不像人们想象的那么琐碎。 实体模型 对于我…

Javascript学习笔记1 数论

1.Javascript不用担心内存的回收与对象的销毁&#xff01; 2.Javascript有&#xff1a;infinity、NaN全局变量表示 被0整除的无穷 和 非数字。undefined和null表示 未定义 和 空&#xff0c;undefined和null可以互换&#xff0c;判别二者需用 全等 号&#xff08;不光判断值&…

JavaFX技巧20:有很多需要展示的地方吗? 使用画布!

JavaFX应用程序似乎有两种&#xff1a;第一种使用带有节点和CSS样式的场景图&#xff0c;第二种使用单个画布。 但是&#xff0c;将这两种方法混合使用是完全合法的。 尤其是当您的应用程序必须显示大量详细信息时&#xff0c;您很容易最终创建成千上万个节点。 即使JavaFX的整…

VirtualBox命令更改虚拟硬盘空间

主要是使用VBoxManage命令来操作第一步&#xff1a;打开CMD&#xff0c;进入到virtualbox存放虚拟机的目录中(win7 系统可以直接在文件夹空白处按住shift键右键鼠标选择[在此处打开命令窗口])&#xff0c;输入[vboxmanage list hdds]可以查看所有的虚拟机的信息&#xff0c;这里…

华为手机老是android自动升级,华为手机系统怎么升级 华为手机升级系统的两种方法...

华为手机使用的均为安卓系统&#xff0c;升级方法有多种&#xff0c;各位可以根据条件不同自行选择升级。华为手机升级系统的两种方法方法一&#xff1a;手机联网更新首先把手机联网。点击设置。向下拖动点击最后一项“关于手机”点击“系统软件更新”。点击在线升级即可。友情…

linux网络体系架构

原创kylin_zeng:http://blog.csdn.net/kylin_fire_zeng 本文参考国嵌视频教程&#xff0c;再此感谢国嵌教育。 一、协议栈层次对比&#xff1a; 1&#xff09;网络接口层把数据链路层和物理层合并在了一起&#xff0c;提供访问物理设备的驱动程序&#xff0c;对应的网络协议主…

android 自定义弹窗diss,Android中自定义PopupWindow,动态弹窗。

我的第一篇博客&#xff0c;咱们直奔主题。先上个效果图在android中自定义PopupWindow&#xff1a;1、首先定义好你想要显示的窗口的布局文件&#xff0c;再实例化一个View对象&#xff1a;窗口布局可灵活变化&#xff0c;dialog_layout.xml代码如下&#xff1a;android:id&quo…

拼图项目的动机和目标

几周前&#xff0c;我写了一篇关于Jigsaw项目如何破坏现有代码的文章 。 那么&#xff0c;我们能得到什么回报呢&#xff1f; 让我们看一下项目解决的痛点及其在Java 9中解决问题的目标。 系列 这篇文章是正在进行的有关拼图项目系列的一部分。 按照推荐的顺序&#xff08;不同…

android中按一个按钮弹出字,允许用户在Android中长按一次即可编辑按钮文字

我想允许App用户在Android中更改Button文本。 当用户单击按钮时&#xff0c;它应该执行某些操作&#xff0c;但是当他/她长按按钮时&#xff0c;将弹出一个编辑文本&#xff0c;并且无论用户键入什么内容都应另存为按钮文本。到目前为止&#xff0c;我已经完成了以下操作。btn1…

关于单元测试脚手架的几点思考

莱昂&#xff1a;Luc Besson的专业人士 当我开始通过创建相同的对象并准备数据来运行测试来重复使用单元测试方法时&#xff0c;我对设计感到失望。 带有大量代码重复的长时间测试方法看起来并不正确。 为了简化和缩短它们&#xff0c;基本上有两个选项&#xff0c;至少在Java…

android 使用c 代码实现,JNI开发实现helloworld,调用自己的C代码实现(1)

JNI开发&#xff0c;实现自己的C代码&#xff0c;helloworld在这里实现一个简单的demo,完成加载自己的C代码使用Android studio&#xff0c;一步一步教你实现在屏幕上显示出helloworld如下图显示&#xff0c;配置号NDK的路径&#xff0c;没有路径的需要自己下载&#xff0c;在A…