我制定了一些基准测试来确定非阻塞io是否真的比阻塞更好。
为此,我写信给服务:一个在玩,另一个在春天。 每个服务发出几个并行的远程请求: Play使用非阻塞WSClient,Sprint-阻塞RestTemplate。
但是在具有各种配置的所有基准测试中(我已经使用过Apache长凳和火炮),弹簧靴的性能要优于其他。
在游戏中,我经常会因apache bench发生Connection reset by peer (54)
错误(尽管spring句柄可以很好地保存并发用户的值)。
这确实让我感到困惑,我试图在播放配置中设置各种值(例如并行度等),但仍然无济于事。
这两个服务都部署到了运行ubuntu 18的t2.xlarage的AWS(每个服务都在其自己的实例上运行)。
播放服务代码:
HomeController
@Singleton
class HomeController @Inject()(requestService: RequestService, cc: ControllerComponents)(implicit ec: ExecutionContext) extends AbstractController(cc) {
def index() = Action { implicit request: Request[AnyContent] =>
Ok(views.html.index())
}
val rand = new Random
def parallelRequests() = Action.async {
val futures = (1 to 10).map( _ =>
requestService.makeRequest(s"http://3.17.161.135:9000?p=${rand.nextInt(99999) + 1}"),
)
Future.sequence(futures).map{ _ =>
Ok("Done")
}
}
}
请求服务
@Singleton
class RequestService @Inject()(wsClient: WSClient)(implicit ec: ExecutionContext){
def makeRequest(url: String): Future[String] = {
wsClient.url(url).get().map { r =>
r.body
}
}
}
application.conf
# https://www.playframework.com/documentation/latest/Configuration
play.filters.enabled += play.filters.hosts.AllowedHostsFilter
play.filters.hosts {
# Allow requests to example.com, its subdomains, and localhost:9000.
allowed = ["."]
}
play.application.secret = "supersecret"
akka {
http {
server {
max-connections = 1024
}
}
actor {
default-dispatcher {
# This will be used if you have set "executor = "fork-join-executor""
fork-join-executor {
# Min number of threads to cap factor-based parallelism number to
parallelism-min = 8
# The parallelism factor is used to determine thread pool size using the
# following formula: ceil(available processors * factor). Resulting size
# is then bounded by the parallelism-min and parallelism-max values.
parallelism-factor = 3.0
# Max number of threads to cap factor-based parallelism number to
parallelism-max = 64
# Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack
# like peeking mode which "pop".
task-peeking-mode = "FIFO"
}
}
}
}
(我在这里尝试过其他操作,包括播放的默认配置)
完整的播放服务源代码:http://cat1.com
春季服务
控制器
@RestController
public class Controller {
private RequestService requestService;
@Autowired
public Controller(RequestService requestService) {
this.requestService = requestService;
}
Random rand = new Random();
@GetMapping
public String home() {
return "Hi";
}
@GetMapping("/parallel-requests")
public String parallelRequests() throws InterruptedException {
CompletableFuture<String> res1 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture<String> res2 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture<String> res4 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture<String> res5 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture<String> res6 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture<String> res7 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
// CompletableFuture<String> res8 = requestService.makeRequest("https://amazon.com?p=" + rand.nextInt(99999) + 1);
CompletableFuture<String> res9 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture<String> res10 = requestService.makeRequest("http://18.188.1.66:8080" + rand.nextInt(99999) + 1);
CompletableFuture.allOf(res1, res2, res4, res5, res6, res7, res9, res10).join();
return "Done";
}
}
RequestService
@Service
public class RequestService {
private static final Logger logger = LoggerFactory.getLogger(RequestService.class);
private final RestTemplate restTemplate;
@Autowired
public RequestService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
@Async
public CompletableFuture<String> makeRequest(String url) throws InterruptedException {
// logger.info("Making request to url " + url);
String result = "";
result = restTemplate.getForObject(url, String.class);
return CompletableFuture.completedFuture(result);
}
}
应用
@SpringBootApplication
@EnableAsync
public class BenchmarkApplication {
public static void main(String[] args) {
SpringApplication.run(BenchmarkApplication.class, args);
}
@Bean
public Executor taskExecutor() {
return Executors.newFixedThreadPool(400);
}
}
application.properties
server.tomcat.max-threads=500
完整的源代码https://github.com/teimuraz/benchmark-play-async
用炮兵进行基准测试
artillery quick --count 500 -n 20 [url]
播放
Summary report @ 14:47:25(+0400) 2018-12-24
Scenarios launched: 500
Scenarios completed: 411
Requests completed: 8614
RPS sent: 104.45
Request latency:
min: 150.9
max: 72097.2
median: 195
p95: 1793.2
p99: 12623.9
Scenario counts:
0: 500 (100%)
Codes:
200: 8614
Errors:
ECONNRESET: 89
春天
Summary report @ 14:49:14(+0400) 2018-12-24
Scenarios launched: 500
Scenarios completed: 500
Requests completed: 10000
RPS sent: 600.24
Request latency:
min: 155.2
max: 3550.4
median: 258.9
p95: 500.8
p99: 1370.7
Scenario counts:
0: 500 (100%)
Codes:
200: 10000
游戏中的RPS为150,而春季为600 !!
以a作为基准结果
ab -n 5000 -c 200 -s 120 [url]
播放
apr_socket_recv: Connection reset by peer (54)
春天
Concurrency Level: 200
Time taken for tests: 23.523 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 680000 bytes
HTML transferred: 20000 bytes
Requests per second: 212.56 [#/sec] (mean)
Time per request: 940.924 [ms] (mean)
Time per request: 4.705 [ms] (mean, across all concurrent requests)
Transfer rate: 28.23 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 153 352 198.5 303 1485
Processing: 163 364 242.4 319 6589
Waiting: 163 363 240.3 318 6589
Total: 324 716 313.2 642 7003
我在玩游戏时怎么了?
我应该调整哪些配置值以使游戏性能更好?
还是非阻塞仅仅是炒作和流行语?