javascript
Spring,Reactor和ElasticSearch:使用伪造的测试数据进行标记
在上一篇文章中,我們創(chuàng)建了一個(gè)從ElasticSearch的API到Reactor的Mono的簡單適配器,如下所示:
import reactor.core.publisher.Mono;private Mono indexDoc(Doc doc) {//... }現(xiàn)在,我們希望以受控的并發(fā)級別運(yùn)行此方法數(shù)百萬次。 基本上,我們想看看索引代碼在負(fù)載下的行為,對其進(jìn)行基準(zhǔn)測試。
用jFairy偽造數(shù)據(jù)
首先,我們需要一些美觀的測試數(shù)據(jù)。 為此,我們將使用方便的jFairy庫。 我們將索引的文檔是一個(gè)簡單的POJO:
@Value class Doc {private final String username;private final String json; }生成邏輯包裝在Java類中:
import io.codearte.jfairy.Fairy; import io.codearte.jfairy.producer.person.Address; import io.codearte.jfairy.producer.person.Person; import org.apache.commons.lang3.RandomUtils;@Component class PersonGenerator {private final ObjectMapper objectMapper;private final Fairy fairy;private Doc generate() {Person person = fairy.person();final String username = person.getUsername() + RandomUtils.nextInt(1_000_000, 9_000_000);final ImmutableMap<String, Object> map = ImmutableMap.<String, Object>builder().put("address", toMap(person.getAddress())).put("firstName", person.getFirstName()).put("middleName", person.getMiddleName()).put("lastName", person.getLastName()).put("email", person.getEmail()).put("companyEmail", person.getCompanyEmail()).put("username", username).put("password", person.getPassword()).put("sex", person.getSex()).put("telephoneNumber", person.getTelephoneNumber()).put("dateOfBirth", person.getDateOfBirth()).put("company", person.getCompany()).put("nationalIdentityCardNumber", person.getNationalIdentityCardNumber()).put("nationalIdentificationNumber", person.getNationalIdentificationNumber()).put("passportNumber", person.getPassportNumber()).build();final String json = objectMapper.writeValueAsString(map);return new Doc(username, json);}private ImmutableMap<String, Object> toMap(Address address) {return ImmutableMap.<String, Object>builder().put("street", address.getStreet()).put("streetNumber", address.getStreetNumber()).put("apartmentNumber", address.getApartmentNumber()).put("postalCode", address.getPostalCode()).put("city", address.getCity()).put("lines", Arrays.asList(address.getAddressLine1(), address.getAddressLine2())).build();}}相當(dāng)無聊的代碼實(shí)際上確實(shí)很酷。 每次運(yùn)行它時(shí),它都會(huì)生成隨機(jī)但合理的JSON,如下所示:
{"address": {"street": "Ford Street","streetNumber": "32","apartmentNumber": "","postalCode": "63913","city": "San Francisco","lines": ["32 Ford Street","San Francisco 63913"]},"firstName": "Evelyn","middleName": "","lastName": "Pittman","email": "pittman@mail.com","companyEmail": "evelyn.pittman@woodsllc.eu","username": "epittman5795354","password": "VpEfFmzG","sex": "FEMALE","telephoneNumber": "368-005-109","dateOfBirth": "1917-05-14T16:47:06.273Z","company": {"name": "Woods LLC","domain": "woodsllc.eu","email": "contact@woodsllc.eu","vatIdentificationNumber": "30-0005081","url": "http://www.woodsllc.eu"},"nationalIdentityCardNumber": "713-79-5185","nationalIdentificationNumber": "","passportNumber": "jVeyZLSt3" }整齊! 不幸的是,沒有記錄jFairy是否是線程安全的,因此以防萬一在實(shí)際代碼中,我正在使用ThreadLocal 。 好的,所以我們只有一個(gè)文檔,但是我們需要數(shù)百萬個(gè)文檔! 使用for -loop太過時(shí)了。 您會(huì)告訴我們無限的隨機(jī)人流嗎?
import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers;private final Scheduler scheduler = Schedulers.newParallel(PersonGenerator.class.getSimpleName());Mono<Doc> generateOne() {return Mono.fromCallable(this::generate).subscribeOn(scheduler); }Flux<Doc> infinite() {return generateOne().repeat(); }generateOne()在Mono<Doc>包裝阻塞的generate()方法。 另外, generate()在parallel Scheduler上運(yùn)行。 為什么? 事實(shí)證明,jFairy在單個(gè)內(nèi)核上還不夠快(很多隨機(jī)數(shù)生成,表查找等),因此我不得不并行化數(shù)據(jù)生成。 通常不應(yīng)該是一個(gè)問題。 但是,當(dāng)生成偽造數(shù)據(jù)的速度比接觸外部服務(wù)器的響應(yīng)式應(yīng)用程序慢時(shí),它會(huì)告訴您有關(guān)基于Netty的Spring Web-flux(!)的性能。
同時(shí)調(diào)用ElasticSearch
好的,擁有無數(shù)好看的假測試數(shù)據(jù)流,我們現(xiàn)在要在ElasticSearch中對其進(jìn)行索引。
@PostConstruct void startIndexing() {index(1_000_000, 1_000); }private void index(int count, int maxConcurrency) {personGenerator.infinite().take(count).flatMap(this::indexDocSwallowErrors, maxConcurrency).window(Duration.ofSeconds(1)).flatMap(Flux::count).subscribe(winSize -> log.debug("Got {} responses in last second", winSize)); }private Mono<IndexResponse> indexDocSwallowErrors(Doc doc) {return indexDoc(doc).doOnError(e -> log.error("Unable to index {}", doc, e)).onErrorResume(e -> Mono.empty()); }當(dāng)應(yīng)用程序啟動(dòng)時(shí),它將啟動(dòng)對一百萬個(gè)文檔的索引編制。 注意,告訴Reactor(它與RxJava相同)多么容易,它應(yīng)該調(diào)用多達(dá)1000個(gè)對ElasticSearch的并發(fā)請求。 每秒我們計(jì)算收到的回復(fù)數(shù):
Got 2925 responses in last second Got 2415 responses in last second Got 3336 responses in last second Got 2199 responses in last second Got 1861 responses in last second不錯(cuò)! 特別是當(dāng)您考慮到有多達(dá)一千個(gè)并發(fā)HTTP請求并且我們的應(yīng)用程序啟動(dòng)時(shí)幾乎只有30個(gè)線程峰值(!),好吧,這是localhost <-> localhost ,有罪! 但是,我們實(shí)際上如何知道所有這些呢? 日志記錄很好,但是到了二十一世紀(jì),我們可以做得更好! 監(jiān)視將是下一批的主題。
源代碼可在react reactive-elastic-search分支中的github.com/nurkiewicz/elastic-flux中獲得。
翻譯自: https://www.javacodegeeks.com/2018/01/spring-reactor-elasticsearch-bechmarking-fake-test-data.html
總結(jié)
以上是生活随笔為你收集整理的Spring,Reactor和ElasticSearch:使用伪造的测试数据进行标记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓手机悬浮窗怎么开(安卓手机悬浮)
- 下一篇: ddos攻击形式主要有哪两种(ddos有