【AKKA干货】AKKA-HTTP(JAVA版)踩坑记
因为不会屎克拉,所以只能使用java版本。
国内AKKA的中文资料实在太少,想要找解决方案真心头大。 特别是对我这种英文差的小白来说实在痛苦。
===================================================================================
先公布一下我的AKKA-HTTP的性能测试数据吧。
测试环境:华为云 2核4G 云耀云服务器
单机简单GET返回数据(hello world) 并发可达 30000+
请求转到AKKA集群环境输出 并发可达 23000-28000 (因为我在转发之前使用了AES解密,可能有影响)
AKKA-HTTP的几个难点主要集中在以下几点:
1. 支持websocket
2. 支持https(TSL)
3. RESTful实现
4. 跨域支持
5. 支持静态资源(屏蔽各种配置文件)
6. 一个进程同时绑定HTTP和HTTPS
7. 将http请求异步接管到akka集群
===================================================================================
好了,直接上干活吧。
支持websocket
private Flow<Message, Message, NotUsed> websocketFlow() {
ActorRef actor =
system.actorOf(GatewayActor.props(Globals.getGameRouter())); //gameRouter是集群路由Group
return socketFlow(actor);
}
private Flow<Message, Message, NotUsed> socketFlow(ActorRef actor) {
// 背压支撑
Source<Message, NotUsed> source = Source.<String>actorRefWithBackpressure("ack", o -> {
if (o == "complete")
return Optional.of(CompletionStrategy.draining());
else
return Optional.empty();
}, o -> Optional.empty()).map(message -> (Message) TextMessage.create(message))
.mapMaterializedValue(textMessage -> {
actor.tell(textMessage, ActorRef.noSender());
return NotUsed.getInstance();
})
// .keepAlive(Duration.ofSeconds(10), () -> TextMessage.create("ping")) //这段代码可让服务端自动向客户端发送ping
;
Sink<Message, NotUsed> sink = Flow.<Message>create().to(Sink.actorRef(actor, PoisonPill.getInstance()));
return Flow.fromSinkAndSource(sink, source);
}
@Override
protected Route routes() {
// TODO Auto-generated method stub
return path("gateway", () -> get(() -> handleWebSocketMessages(websocketFlow))));
支持HTTPS
第一步:在HttpApp中实现useHttps
public HttpsConnectionContext useHttps(ActorSystem system) {
HttpsConnectionContext https = null;
try {
// initialise the keystore
// !!! never put passwords into code !!!
final char[] password = "123456789".toCharArray();
final KeyStore ks = KeyStore.getInstance("PKCS12");
final InputStream keystore = KutaHttpApp.class.getClassLoader().getResourceAsStream("xxx-xxx-com-akka-0709085814.pfx");
if (keystore == null) {
throw new RuntimeException("Keystore required!");
}
ks.load(keystore, password);
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
String next = aliases.nextElement();
logger.info(next);
java.security.Key key = ks.getKey(next, password);
logger.info("alg:{},format:{}",key.getAlgorithm(),key.getFormat());
}
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(ks, password);
final TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
https = ConnectionContext.https(sslContext);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
system.log().error("Exception while configuring HTTPS.", e);
} catch (CertificateException | KeyStoreException | UnrecoverableKeyException | IOException e) {
system.log().error("Exception while ", e);
}
return https;
}
第二步:在main函数中注册
final Http http = Http.get(system);
HttpsConnectionContext https = app.useHttps(system);
http.setDefaultServerHttpContext(https);
Integer sslPort = PropertyUtils.getInteger("app", "gateway.ssl.port");
http.bindAndHandle(flow, ConnectHttp.toHost(host, sslPort), materializer);
RESTful实现
public Route RESTfulAsyncRouter() {
return path(PathMatchers.segment("RESTful"),()->{
complete("hello");
});
}
跨域支持
public Route RESTfulRouter() {
return path(PathMatchers.segment("RESTful"),
()-> concat(
post(()->{
return entity(Jackson.unmarshaller(JSONObject.class), json -> {
final JSONObject data = json;
final MessageDispatcher dispatcher = system.dispatchers().lookup(KSFConstants.BLOCKING_DISPATCHER);
try {
return
CompletableFuture.<Route>supplyAsync(()->{
final ActorRef RESTfull = system.actorOf(RESTfulActor.props(Globals.getHallRouter())
.withDispatcher(KSFConstants.BLOCKING_DISPATCHER));
// logger.info("当前运行线程:{}",Thread.currentThread());
CompletionStage<Optional<KutaJsonResponse>> rsp = Patterns
.ask(RESTfull, data, timeout)
.thenApply(a -> {
return Optional.of((KutaJsonResponse) a);
});
return onSuccess(() -> rsp, performed -> {
RESTfull.tell(PoisonPill.getInstance(), ActorRef.noSender());
List<HttpHeader> list = new ArrayList<>();
list.add(HttpHeader.parse("Access-Control-Allow-Origin", "*"));
list.add(HttpHeader.parse("Access-Control-Allow-Credentials", "true"));
list.add(HttpHeader.parse("Access-Control-Allow-Methods", "POST,OPTIONS"));
list.add(HttpHeader.parse("Access-Control-Expose-Headers", "Content-Type, Access-Control-Allow-Origin, Access-Control-Allow-Credentials"));
list.add(HttpHeader.parse("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization"));
if (performed.isPresent()) {
if(performed.get().getSize() > (1024 * 50)) {
return encodeResponseWith(
Collections.singletonList(Coder.Gzip),
()->complete(StatusCodes.OK, list, performed.get(), Jackson.marshaller())
);
}
else {
return complete(StatusCodes.OK, list, performed.get(), Jackson.marshaller());
}
}
else {
return complete(StatusCodes.NOT_FOUND);
}
}).orElse(complete(StatusCodes.INTERNAL_SERVER_ERROR));
}, dispatcher).get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
});
})
, options(()->{
List<HttpHeader> list = new ArrayList<>();
list.add(HttpHeader.parse("Access-Control-Allow-Origin", "*"));
list.add(HttpHeader.parse("Access-Control-Allow-Credentials", "true"));
list.add(HttpHeader.parse("Access-Control-Allow-Methods", "POST,OPTIONS"));
list.add(HttpHeader.parse("Access-Control-Expose-Headers", "Content-Type, Access-Control-Allow-Origin, Access-Control-Allow-Credentials,Vary"));
list.add(HttpHeader.parse("Access-Control-Allow-Headers", "Content-Type,Access-Token,Authorization"));
return respondWithHeaders(list,()-> complete(""));
})
)
);
}
支持静态资源(屏蔽各种配置文件)
public Route staticResourceRouter() {
return path(PathMatchers.remaining(), remain -> get(()-> {
if(remain.endsWith(".properties") || remain.endsWith(".xml") || remain.endsWith(".conf")) {
return complete(StatusCodes.UNAUTHORIZED);
}
return getFromResource(remain);
}));
}
将资源文件放到你的classPath目录下即可
一个进程同时绑定HTTP和HTTPS
//我自己实现的HttpApp
KutaHttpApp app = new KutaHttpApp(system);
final Http http = Http.get(system);
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Flow<HttpRequest, HttpResponse, NotUsed> flow = app.routes().flow(system, materializer);
//先绑定http
http.bindAndHandle(flow, ConnectHttp.toHost(host, port), materializer);
boolean useHttps = Boolean.parseBoolean(PropertyUtils.getProperty("app", "gateway.usessl"));
if (useHttps) {
HttpsConnectionContext https = app.useHttps(system);
http.setDefaultServerHttpContext(https);
Integer sslPort = PropertyUtils.getInteger("app", "gateway.ssl.port");
http.bindAndHandle(flow, ConnectHttp.toHost(host, sslPort), materializer);
logger.info("启动ssl服务.host:{},port:{}",host,sslPort);
}
将http请求异步接管到akka集群
public Route RESTfulAsyncRouter() {
return path(PathMatchers.segment("RESTful"),()->{
return entity(Jackson.unmarshaller(JSONObject.class), json -> {
final Set<HttpHeader> headers = new HashSet<>();
return completeWith(Marshaller.entityToOKResponse(headers, Jackson.<KutaJsonResponse>marshaller()), f->{
system.actorOf(RESTfulAsyncActor.props(json, f));
});
});
});
}
研究了好久才研究出来。 希望能帮助到正在使用akka-http的同学们吧。
【AKKA干货】AKKA-HTTP(JAVA版)踩坑记的更多相关文章
- C#调用java方法踩坑记
首先,我的java代码写了一个遗传算法,这是我硕士毕业论文的核心算法,项目是基于C#的web项目.但是现在又不想用C#重写遗传算法代码,于是就想用C#去调用java的代码.在网上找了方法,一般有两种: ...
- Spark踩坑记——Spark Streaming+Kafka
[TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...
- Spark踩坑记——数据库(Hbase+Mysql)
[TOC] 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库,去统计或者改变一些值.最近一个实时消费者处理任务,在使用spark streami ...
- 【踩坑记】从HybridApp到ReactNative
前言 随着移动互联网的兴起,Webapp开始大行其道.大概在15年下半年的时候我接触到了HybridApp.因为当时还没毕业嘛,所以并不清楚自己未来的方向,所以就投入了HybridApp的怀抱. Hy ...
- Spark踩坑记——共享变量
[TOC] 前言 Spark踩坑记--初试 Spark踩坑记--数据库(Hbase+Mysql) Spark踩坑记--Spark Streaming+kafka应用及调优 在前面总结的几篇spark踩 ...
- Spark踩坑记——从RDD看集群调度
[TOC] 前言 在Spark的使用中,性能的调优配置过程中,查阅了很多资料,之前自己总结过两篇小博文Spark踩坑记--初试和Spark踩坑记--数据库(Hbase+Mysql),第一篇概况的归纳了 ...
- [转]Spark 踩坑记:数据库(Hbase+Mysql)
https://cloud.tencent.com/developer/article/1004820 Spark 踩坑记:数据库(Hbase+Mysql) 前言 在使用Spark Streaming ...
- Spark踩坑记——数据库(Hbase+Mysql)转
转自:http://www.cnblogs.com/xlturing/p/spark.html 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库 ...
- Spark踩坑记:Spark Streaming+kafka应用及调优
前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark streaming从k ...
- centos 7( linux )下搭建elasticsearch踩坑记
原文:https://blog.csdn.net/an88411980/article/details/83150380 概述 公司最近在做全文检索的项目,发现elasticsearch踩了不少 ...
随机推荐
- .NET Core CLI 的性能诊断工具介绍
前言 开发人员的.NET Core项目上线后,经常会出现各种问题,内存泄漏,CPU 100%,处理时间长等, 这个时候就需要快速并准确的发现问题,并解决问题, 除了项目本身的日志记录外,NET Cor ...
- javascript : 复杂数据结构拷贝实验
数组深拷贝看起来很简单. array.concat()就行了. 但是,如果数组里有对象呢? 实际上,你以为你拷贝了对象,但实际上你只拷贝了对象的引用(指针)! 我们可以做个试验. // test le ...
- 猴子吃桃问题之《C语言经典案例分析》
猴子吃桃问题之<C语言经典案例分析>一.[什么是猴子吃桃] 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个.第二天早上又将第一天剩下的桃子吃掉一半 ...
- 设计模式:observer模式
目标:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新 例子: class Observer //观察者 { public: virtual vo ...
- 教孩子学编程 python语言版PDF高清完整版免费下载|百度云盘|Python入门
百度云盘:教孩子学编程 python语言版PDF高清完整版免费下载 提取码:mnma 内容简介 本书属于no starch的经典系列之一,英文版在美国受到读者欢迎.本书全彩印刷,寓教于乐,易于学习:读 ...
- 使用java实现希表的基础功能
用java代码完成哈希表数据结构的简单实现, 以公司雇员的添加修改作为模拟实例 具体代码如下: package com.seizedays.hashtable; import java.util.Sc ...
- Raid0,1,5,10,50
raid0 就是把多个硬盘合并成1个逻辑盘使用,数据读写时对各硬盘同时操作,不同硬盘写入不同数据,速度快. **最少需要2块硬盘 raid1 同时对2个硬盘读写(同样的数据).强调数据的安全性.损坏一 ...
- C#怎么统计网站当前在线人数
1.问题背景 c#网站怎么合理的统计在线人数?我想通过全局变量来统计软件的使用情况,当启动软件时向服务器的用户表写开始使用时间,正常退出时写一个结束使用时间,来统计用户的在线使用情况. 但是有一个问题 ...
- 巩固复习(Django最基础的部分_具体查看官方文档)
Django学习路1 1.脚本不能随便运行,没准 linux 运行完就上不去了 2.pip 在 linux 上 写 pip3 同理 python 写为 python3 3.在 pycharm 上安装库 ...
- PHP array_slice() 函数
实例 从数组的第二个元素开始取出,并返回直到数组末端的所有元素: <?php$a=array("red","green","blue" ...