mongodb实验报告_Dropwizard,MongoDB和Gradle实验
mongodb實(shí)驗(yàn)報(bào)告
介紹
我使用Dropwizard,MongoDB和Gradle創(chuàng)建了一個(gè)小項(xiàng)目。 它實(shí)際上是作為一個(gè)實(shí)驗(yàn)性的Guava緩存開始的,作為將計(jì)數(shù)器發(fā)送到MongoDB(或任何其他DB)的緩沖區(qū)。 我也想嘗試MondleDB插件的Gradle。 接下來,我想創(chuàng)建某種界面來檢查此框架,因此我決定嘗試使用DropWizard。 這就是這個(gè)項(xiàng)目的創(chuàng)建方式。
本文不是使用任何選定技術(shù)的教程。 這是一個(gè)小展示柜,我做過實(shí)驗(yàn)。 我猜有一些缺陷,也許我沒有使用所有“最佳實(shí)踐”。 但是,我確實(shí)相信,在本文的幫助下,該項(xiàng)目可以成為我使用的各種技術(shù)的良好起點(diǎn)。 我還嘗試顯示一些設(shè)計(jì)選擇,這些選擇有助于實(shí)現(xiàn)SRP,去耦,內(nèi)聚等。
我決定從用例描述及其實(shí)現(xiàn)方式開始。 之后,我將解釋我對(duì)Gradle,MongoDB(和嵌入式)和Dropwizard所做的工作。
在開始之前,這里是源代碼:
- https://github.com/eyalgo/CountersBuffering
用例:帶緩沖區(qū)的計(jì)數(shù)器
我們對(duì)服務(wù)器有一些輸入請(qǐng)求。 在請(qǐng)求過程中,我們選擇用一些數(shù)據(jù)(由某些邏輯決定)“繪制”它。 有些請(qǐng)求將由Value-1繪制,某些請(qǐng)求將由Value-2繪制,等等。有些將根本不會(huì)繪制。 我們要限制繪畫請(qǐng)求的數(shù)量(每個(gè)繪畫值)。 為了有限制,對(duì)于每個(gè)繪制值,我們知道最大值,但是還需要計(jì)算(每個(gè)繪制值)繪制請(qǐng)求的數(shù)量。 由于系統(tǒng)具有多個(gè)服務(wù)器,因此計(jì)數(shù)器應(yīng)由所有服務(wù)器共享。
延遲至關(guān)重要。 通常,每個(gè)請(qǐng)求處理會(huì)得到4-5毫秒(對(duì)于所有流程。不僅僅是繪畫)。 因此,我們不希望增加計(jì)數(shù)器會(huì)增加延遲。 相反,我們將保留一個(gè)緩沖區(qū),客戶端將向緩沖區(qū)發(fā)送“增加”。 緩沖區(qū)將定期以“批量增量”增加存儲(chǔ)庫。
我知道可以直接使用Hazelcast或Couchbase或其他類似的快速內(nèi)存數(shù)據(jù)庫。 但是對(duì)于我們的用例,那是最好的解決方案。
原理很簡單:
- 從屬模塊將調(diào)用服務(wù)以增加某個(gè)密鑰的計(jì)數(shù)器
- 該實(shí)現(xiàn)為每個(gè)鍵保留一個(gè)計(jì)數(shù)器緩沖區(qū)
- 這是線程安全的
- 編寫在單獨(dú)的線程中進(jìn)行
- 每次寫入都會(huì)大量增加
柜臺(tái)高級(jí)設(shè)計(jì)
緩沖
對(duì)于緩沖區(qū),我使用了Google Guava 緩存 。
緩沖結(jié)構(gòu)
創(chuàng)建緩沖區(qū):
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實(shí)現(xiàn)了CacheLoader接口。 當(dāng)我們調(diào)用增加(見下文)時(shí),我們首先通過鍵從緩存中獲取。 如果鍵不存在,則加載器返回值。
BufferValueCacheLoader:
public class BufferValueCacheLoader extends CacheLoader<Counterable, BufferValue> {@Overridepublic BufferValue load(Counterable key) {return new BufferValue();} }BufferValue包裝一個(gè)AtomicInteger (在某些時(shí)候我需要將其更改為Long)
增加柜臺(tái)
增加計(jì)數(shù)器,如果超過閾值則發(fā)送:
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);}} }當(dāng)增加一個(gè)計(jì)數(shù)器時(shí),我們首先從緩存中獲取當(dāng)前值(在加載程序的幫助下。如上所述)。 compareAndSet將自動(dòng)檢查是否具有相同的值(未被另一個(gè)線程修改)。 如果是這樣,它將更新該值并返回true。 如果成功(返回true),則緩沖區(qū)調(diào)用更新程序。
查看緩沖區(qū)
開發(fā)服務(wù)之后,我想要一種查看緩沖區(qū)的方法。 因此,我實(shí)現(xiàn)了以下方法,該方法由前端層(Dropwizard的資源)使用。 Java 8 Stream和Lambda表達(dá)式的小示例。
獲取所有計(jì)數(shù)器在緩存中:
return ImmutableMap.copyOf(cache.asMap()).entrySet().stream().collect(Collectors.toMap((entry) -> entry.getKey().toString(),(entry) -> entry.getValue().getValue()));MongoDB
我之所以選擇MongoDB是因?yàn)閮蓚€(gè)原因:
我試圖設(shè)計(jì)系統(tǒng),以便可以選擇其他任何持久性實(shí)現(xiàn)并進(jìn)行更改。
我使用嗎啡作為MongoDB客戶端層,而不是直接使用Java客戶端。 使用Morphia,您可以創(chuàng)建dao ,它是與MongoDB集合的連接。 您還聲明了一個(gè)簡單的Java Bean(POJO),它表示集合中的文檔。 一旦有了dao,就可以使用相當(dāng)簡單的API以“ Java方式”對(duì)集合進(jìn)行操作。 您可以查詢和其他任何CRUD操作,以及更多。
我有兩個(gè)操作:增加計(jì)數(shù)器和獲取所有計(jì)數(shù)器。 服務(wù)實(shí)現(xiàn)不擴(kuò)展Morphia的BasicDAO,而是具有一個(gè)繼承它的類。 我使用了組合 (過度繼承),因?yàn)槲蚁M麅煞N服務(wù)都具有更多的行為。
為了與鍵表示保持一致,并從依賴代碼中隱藏其實(shí)現(xiàn)方式,我使用了一個(gè)接口:可通過單個(gè)方法counterCount()來 抵消 。
public interface Counterable {String counterKey(); }DAO,是服務(wù)內(nèi)部的組成部分:
final class MongoCountersDao extends BasicDAO<Counter, ObjectId> {MongoCountersDao(Datastore ds) {super(Counter.class, ds);} }增加柜臺(tái)
MongoCountersUpdater擴(kuò)展了實(shí)現(xiàn)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
為了在持久層上運(yùn)行測試,我想使用內(nèi)存數(shù)據(jù)庫。 有一個(gè)MongoDB插件。 使用此插件,您可以通過僅在運(yùn)行時(shí)創(chuàng)建服務(wù)器來運(yùn)行服務(wù)器,或者在Gradle中的maven / task中作為目標(biāo)運(yùn)行。
- https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo
- https://github.com/sourcemuse/GradleMongoPlugin
Gradle上的嵌入式MongoDB
稍后我將詳細(xì)介紹Gradle,但這是設(shè)置嵌入式mongo所需的操作。
dependencies {// More dependencies heretestCompile 'com.sourcemuse.gradle.plugin:gradle-mongo-plugin:0.4.0' }設(shè)置屬性
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任務(wù)
- startMongoDb只會(huì)啟動(dòng)服務(wù)器。 它將運(yùn)行直到停止它。
- stopMongoDb將停止它。
- startManagedMongoDb test ,這兩個(gè)任務(wù)將在測試運(yùn)行之前啟動(dòng)嵌入式服務(wù)器。 jvm完成(測試完成)后,服務(wù)器將關(guān)閉
盡管我只觸碰到冰山一角,但我開始看到Gradle的力量。 設(shè)置項(xiàng)目甚至都不是那么困難。
Gradle設(shè)置
首先,我在eclipse中創(chuàng)建了Gradle項(xiàng)目(安裝插件后)。 我需要設(shè)置依賴項(xiàng)。 很簡單。 就像行家一樣。
一個(gè)大的JAR輸出
當(dāng)我想從Maven中的所有庫中創(chuàng)建一個(gè)大jar時(shí),我會(huì)使用shade插件。 我在尋找類似的東西,并發(fā)現(xiàn)gradle-one-jar插入。 https://github.com/rholder/gradle-one-jar我添加了該插件apply plugin: 'gradle-one-jar' 。 在類路徑中添加了一個(gè)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'} }并添加了一個(gè)任務(wù):
mainClassName = 'org.eyalgo.server.dropwizard.CountersBufferApplication' task oneJar(type: OneJar) {mainClass = mainClassNamearchiveName = 'counters.jar'mergeManifestFromJar = true }這些是我需要執(zhí)行的必要操作,才能使應(yīng)用程序運(yùn)行。
Dropwizard
Dropwizard是一堆庫,可以輕松快速地創(chuàng)建Web服務(wù)器。 它將Jetty用于HTTP,將Jersey用于REST。 它具有其他成熟的庫來創(chuàng)建復(fù)雜的服務(wù)。 它可以用作易于開發(fā)的微服務(wù)。
正如我在簡介中所解釋的,我不會(huì)介紹Dropwizard的所有功能和/或設(shè)置。 有很多的網(wǎng)站。 我將簡要介紹為使應(yīng)用程序運(yùn)行而執(zhí)行的操作。
Gradle運(yùn)行任務(wù)
run { args 'server', './src/main/resources/config/counters.yml' }第一個(gè)參數(shù)是服務(wù)器。 第二個(gè)參數(shù)是配置文件的位置。 如果不將Dropwizard作為第一個(gè)參數(shù),則會(huì)收到有關(guān)可能選項(xiàng)的錯(cuò)誤消息。
positional arguments:{server,check} available commands我已經(jīng)在Gradle部分中展示了如何創(chuàng)建一個(gè)jar。
組態(tài)
在Dropwizard中,您可以使用擴(kuò)展Configuration的類來設(shè)置應(yīng)用程序。 類中的字段應(yīng)與yml配置文件中的屬性對(duì)齊。
優(yōu)良作法是根據(jù)屬性的用途/職責(zé)將其分組。 例如,我為mongo參數(shù)創(chuàng)建了一個(gè)組。
為了使配置類正確讀取子組,您需要?jiǎng)?chuàng)建一個(gè)與組中的屬性對(duì)齊的類。
然后,在主配置中,將該類添加為成員,并使用批注進(jìn)行標(biāo)記: @JsonProperty 。
例:
@JsonProperty("mongo") private MongoServicesFactory servicesFactory = new MongoServicesFactory(); @JsonProperty("buffer") private BufferConfiguration bufferConfiguration = new BufferConfiguration();示例:更改端口
這是配置文件的一部分,用于設(shè)置應(yīng)用程序的端口。
server:adminMinThreads: 1adminMaxThreads: 64applicationConnectors:- type: httpport: 9090adminConnectors:- type: httpport: 9091健康檢查
Dropwizard提供了開箱即用的基本管理API。 我將端口更改為9091。我為MongoDB連接創(chuàng)建了運(yùn)行狀況檢查。 您需要擴(kuò)展HealthCheck并實(shí)施檢查方法。
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());} }其他功能幾乎是不言自明的,或者像任何入門教程一樣簡單。
增強(qiáng)想法
這些是我可能會(huì)嘗試添加的內(nèi)容。
- 將測試添加到Dropwizard部分。
該項(xiàng)目以PoC開頭,因此與往常不同,我跳過了服務(wù)器部分中的測試。
Dropwizard擁有“ 測試Dropwizard” ,我想嘗試一下。 - 不同的持久性實(shí)現(xiàn)。 (couchbase?Hazelcast?)。
- 使用Google Guice進(jìn)行注射。 并借助它注入不同的持久性實(shí)現(xiàn)。
就這樣。 希望有幫助。
- 源代碼: https : //github.com/eyalgo/CountersBuffering
翻譯自: https://www.javacodegeeks.com/2015/02/dropwizard-mongodb-and-gradle-experimenting.html
mongodb實(shí)驗(yàn)報(bào)告
總結(jié)
以上是生活随笔為你收集整理的mongodb实验报告_Dropwizard,MongoDB和Gradle实验的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拟彻底“抛弃”半导体业务 百年东芝退市后
- 下一篇: guava集合操作类的使用_使用Guav