Надо было тебе писать на Java... Единомышленников бы набрал)Кто в здравом уме будет писать сервер с нуля на java?
а какая нормальная архитектура (не идеальная./лучшая) а нормальная?закладыванием нормальной архитектуры
Та самая которую тут эмулируют джавой или в данном случае Go. Думаешь надмозги из NCSoft с их миллионами и бюджетами не распланировали какая архитектура и какой язык нужен для сервера? Они точно не монетку там кидали. В контексте же тематики данного форума а это эмуляция то джава вполне себе вытягивает все это непотребство, вот человек проверяет на Go а я в свое время видел вообще свистопляску на Питоне, там черт ногу сломает. Нет преград для патриотов своего дела. Вот и выясним а точно ли только джава или еще что то.Надо было тебе писать на Java... Единомышленников бы набрал)
а какая нормальная архитектура (не идеальная./лучшая) а нормальная?
Смотрю в книгу вижу фигу.whiteo, это клиент или сервер?
Джава вполне себе хорошо себя чувствует, взять опыт коропораций где нагрузка измеряется сотнями или тысячами rps - там везде java/kotlinТа самая которую тут эмулируют джавой или в данном случае Go. Думаешь надмозги из NCSoft с их миллионами и бюджетами не распланировали какая архитектура и какой язык нужен для сервера? Они точно не монетку там кидали. В контексте же тематики данного форума а это эмуляция то джава вполне себе вытягивает все это непотребство, вот человек проверяет на Go а я в свое время видел вообще свистопляску на Питоне, там черт ногу сломает. Нет преград для патриотов своего дела. Вот и выясним а точно ли только джава или еще что то.
Сообщение объединено:Смотрю в книгу вижу фигу.
На java? Ну уж точно не текущих сборок l2, которые никак не меняются десятилетиями.а какая нормальная архитектура (не идеальная./лучшая) а нормальная?
Я то думал C, C++, Rust для высоконагруженных используют, а тут оказывается java/kotlinДжава вполне себе хорошо себя чувствует, взять опыт коропораций где нагрузка измеряется сотнями или тысячами rps - там везде java/kotlin
NCSoft по сути опередили своё время с архитектурой, и далеко не монолит они делали уже тогда у нас были разделены логин, гейм, нпц сервер, репортинг сервер, сервер петиций, CacheD и не только, так что под описание монолита никак не подвязать, это SOA(Service-Oriented Architecture).нцсофт вбухали кучу денег в свое монолитное решение и как я понял особо архитектуру они не поменяли, да и зачем, если она работает годами и приносит деньги.
Не поэтому, а другие причины, а переписывать всю архитектуру, весь код и современные подходы не имеет никакой выгоды, на 2026 такие ресурсы вообще не колоссальные, java со своим пожиранием памяти куда больше потратит ресурсов.Поэтому их сервера сжирают ресурсы лопатами и масштабируются только вертикально, что в современном мире уже не нужно, возможно в последних хрониках они докрутили хотя бы базовую репликацию, но что-то мне кажется нет
Та хоть на asm пиши. Факт остается что Java используется активно, а как поддерживать HL HA это уже задача девопсовЯ то думал C, C++, Rust для высоконагруженных используют, а тут оказывается java/kotlin
Да это похоже на SOA, но какая разница как это называется, если оно в простое жрет ресурсы, как монолит который разрабатывался годами? Делили бы дальше свои сервисы для масштабированияNCSoft по сути опередили своё время с архитектурой, и далеко не монолит они делали уже тогда у нас были разделены логин, гейм, нпц сервер, репортинг сервер, сервер петиций, CacheD и не только, так что под описание монолита никак не подвязать, это SOA(Service-Oriented Architecture).
ну да, хотя бы какой то план - я учусь строить =)На java? Ну уж точно не текущих сборок l2, которые никак не меняются десятилетиями.
честно говоря есть подозрение, что это устаревшая информацияjava со своим пожиранием памяти куда больше потратит ресурсов
вроде все в теме - производство старой MMO новыми киянкамиобсуждаем все, но только не сабж
Активно с каждым годом эта активность падает.Та хоть на asm пиши. Факт остается что Java используется активно, а как поддерживать HL HA это уже задача девопсов
На загрузку лишь ресурсы уходят не более.Да это похоже на SOA, но какая разница как это называется, если оно в простое жрет ресурсы, как монолит который разрабатывался годами? Делили бы дальше свои сервисы для масштабирования
Ну так да, а архитектура для тех лет просто замечательная и весьма технологичная.kick, вряд ли они гнались за громкими названиями и/или абревиатурами, делали то что позволяло железо/софт тех лет.
В java проектах л2 нет архитектуры, это лишь пример, как не надо делать. Для обучения, это колоссальный объем знаний, который нужен на сегодняшний день с пониманием всех инструментов, базовых принципов как всё работает, концепции хранения данных, масштабирование, кэирование, системы сообщений, часть знаний от бизнес-аналитики, так же как популярные гиганты решают определенные проблемы, с чем сталкиваются, доклады их. Многие показывают готовые архитектуры, схемы, уже брать за основу, от того же netflix не мало статей.ну да, хотя бы какой то план - я учусь строить =)
В чём?) Как то резко изменилась jvm?) Нет, сколько уже сборщиков мусора сменилась в jvm? 7 и до сих пор не могут заставить работать их нормально. У JVM нет контроля, там где он нужен, потому что она забирает ответственность на себя и решает за программиста где и что она будет оптимизировать. То что java сейчас это отстающая среда, пока комитет java примет новую фичу.честно говоря есть подозрение, что это устаревшая информация
import java.util.UUID;
class Node {
String id;
Integer value;
Node left;
Node right;
Node(int depth) {
this.id = UUID.randomUUID().toString();
this.value = depth;
if (depth > 0) {
this.left = new Node(depth - 1);
this.right = new Node(depth - 1);
}
}
}
public class Main {
public static void main(String[] args) {
Node root = new Node(25);
}
}
#include <iostream>
#include <memory>
#include <string>
struct Node {
char id[36];
int value;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
Node(int depth) : value(depth) {
int8_t dummy[] = "12345678-1234-1234-1234-123456789012";
std::copy(std::begin(dummy), std::end(dummy), id);
if (depth > 0) {
left = std::make_unique<Node>(depth - 1);
right = std::make_unique<Node>(depth - 1);
}
}
};
int main() {
auto root = std::make_unique<Node>(25);
return 0;
}
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
class Point {
public Double x;
public Double y;
public Double z;
public Boolean isActive;
public Point(double x, double y, double z, boolean isActive) {
this.x = x;
this.y = y;
this.z = z;
this.isActive = isActive;
}
}
public class Main {
private static double calculate(Point p, Function<Point, Double> formula) {
return formula.apply(p);
}
public static void main(String[] args) {
List<Point> points = new LinkedList<>();
for (int i = 0; i < 1_000_000; i++) {
points.add(new Point(i * 1.1, i * 1.2, i * 1.3, i % 2 == 0));
}
long startTime = System.nanoTime();
Double totalSum = 0.0;
int index = 0;
for (Point p : points) {
Integer stepId = index;
final double modifier = (stepId % 2 == 0) ? 1.0 : 1.005;
if (Optional.ofNullable(p.isActive).orElse(false)) {
totalSum += calculate(p, pt -> (pt.x + pt.y + pt.z) * modifier);
}
index++;
}
long endTime = System.nanoTime();
System.out.println("Результат: " + totalSum);
System.out.println("Время выполнения: " + (endTime - startTime) / 1_000_000.0 + " мс");
}
}
using System.Diagnostics;
using System.Runtime.InteropServices;
public struct Point
{
public double X;
public double Y;
public double Z;
public bool IsActive;
}
public static class Program
{
public static void Main()
{
List<Point> points = new List<Point>(1_000_000);
for (int i = 0; i < 1_000_000; i++)
{
points.Add(new Point { X = i * 1.1, Y = i * 1.2, Z = i * 1.3, IsActive = (i % 2 == 0) });
}
Stopwatch sw = Stopwatch.StartNew();
double totalSum = 0.0;
Span<Point> pointSpan = CollectionsMarshal.AsSpan(points);
for (int i = 0; i < pointSpan.Length; i++)
{
ref readonly Point p = ref pointSpan[i];
if (p.IsActive)
{
double modifier = (i % 2 == 0) ? 1.0 : 1.005;
totalSum += (p.X + p.Y + p.Z) * modifier;
}
}
sw.Stop();
Console.WriteLine($"Результат: {totalSum}");
Console.WriteLine($"Время выполнения в C#: {sw.Elapsed.TotalMilliseconds:F2} мс");
}
}
#include <iostream>
#include <list>
#include <optional>
#include <chrono>
struct Point {
double x;
double y;
double z;
std::optional<bool> isActive;
};
template<typename F>
double calculate(const Point& p, F formula) {
return formula(p);
}
int main() {
std::list<Point> points;
for (int i = 0; i < 1000000; ++i) {
points.push_back({ i * 1.1, i * 1.2, i * 1.3, (i % 2 == 0) });
}
auto start = std::chrono::high_resolution_clock::now();
std::optional<double> total_sum = 0.0;
int index = 0;
for (const auto& p : points) {
int step_id = index;
double modifier = (step_id % 2 == 0) ? 1.0 : 1.005;
if (p.isActive.value_or(false)) {
total_sum.value() += calculate(p, [&](const Point& pt) {
return (pt.x + pt.y + pt.z) * modifier;
});
}
index++;
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration = end - start;
std::cout << "Результат: " << total_sum.value() << "\n";
std::cout << "Время выполнения в C++: " << duration.count() << " мс\n";
return 0;
}
Потому что если начать тоже делать в си/плюсах/расте/шарпе будет все равно выигрывать по памяти. Я может быть и могу согласиться с выражением, что java больше не такая медленная как была раньше, да это так и есть и в некоторых моментах она даже может обойти плюсы, это так, но опять таки при каких условиях это достигается и из-за чего. Но по памяти все ещё актуальноизмеряется сотнями ли тысячами начинается лишь байтоёбство и забыванием многих фич языка и по памяти все равно проигрывает
package main
import (
"container/list"
"fmt"
"time"
)
type Point struct {
X *float64
Y *float64
Z *float64
IsActive *bool
}
func NewPoint(x, y, z float64, isActive bool) *Point {
return &Point{
X: &x,
Y: &y,
Z: &z,
IsActive: &isActive,
}
}
func calculate(p *Point, formula func(*Point) float64) float64 {
return formula(p)
}
func orElseFalse(b *bool) bool {
if b == nil {
return false
}
return *b
}
func main() {
points := list.New()
for i := 0; i < 1000000; i++ {
points.PushBack(NewPoint(
float64(i)*1.1,
float64(i)*1.2,
float64(i)*1.3,
i%2 == 0,
))
}
startTime := time.Now()
var totalSum float64 = 0.0
index := 0
for e := points.Front(); e != nil; e = e.Next() {
p := e.Value.(*Point)
stepId := index
var modifier float64
if stepId%2 == 0 {
modifier = 1.0
} else {
modifier = 1.005
}
if orElseFalse(p.IsActive) {
totalSum += calculate(p, func(pt *Point) float64 {
return (*pt.X + *pt.Y + *pt.Z) * modifier
})
}
index++
}
endTime := time.Now()
executionTime := float64(endTime.Sub(startTime).Nanoseconds()) / 1000000.0
fmt.Printf("Результат: %f\n", totalSum)
fmt.Printf("Время выполнения: %f мс\n", executionTime)
}
"container/list"
points := list.New()
for i := 0; i < 1000000; i++ {
points.PushBack(NewPoint(
float64(i)*1.1,
float64(i)*1.2,
float64(i)*1.3,
i%2 == 0,
))
}
Да вообще не проблема, я могу не использовать структуры, дописать, чтобы проверка была аналогична условию, от неё ничего не изменится, все ограничения задачи и поставлены выполняются. Перебираю коллекция в цикле foreach, получаю полноценные объекты Point, работаю со свойствами-обёртками (kick, ничего не понял, в C# ты читеришь а в Go показываешь как делать не нужно.
Есть цель показать результаты как рантайм в хреновых условиях вывезет эту катку, то нужно сделать равные условия для всех, а не выделять C#.
double?, bool?), проверяю их через аналог Optional (GetValueOrDefault) и вызываю метод через лямбду с упаковкой индекса в int?using System.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices;
public class Point
{
public double? x;
public double? y;
public double? z;
public bool? isActive;
public void Reset(double x, double y, double z, bool isActive)
{
this.x = x;
this.y = y;
this.z = z;
this.isActive = isActive;
}
}
public class LinkedListNode
{
public Point Item;
public LinkedListNode Next;
}
public struct CustomEnumerator : IEnumerator<Point>
{
private LinkedListNode _current;
private bool _started;
public CustomEnumerator(LinkedListNode head)
{
_current = head;
_started = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (!_started)
{
_started = true;
return _current != null;
}
if (_current != null)
{
_current = _current.Next;
}
return _current != null;
}
public Point Current => _current != null ? _current.Item : null;
object IEnumerator.Current => Current;
public void Reset() => throw new NotSupportedException();
public void Dispose() { }
}
public class VirtualLinkedList : IReadOnlyCollection<Point>
{
private LinkedListNode _head;
private LinkedListNode _tail;
public int Count { get; private set; } = 0;
private readonly LinkedListNode[] _nodePool = new LinkedListNode[1_000_000];
private readonly Point[] _pointPool = new Point[1_000_000];
public VirtualLinkedList()
{
for (int i = 0; i < 1_000_000; i++)
{
_nodePool[i] = new LinkedListNode();
_pointPool[i] = new Point();
}
}
public void AddLast(double x, double y, double z, bool isActive)
{
Point p = _pointPool[Count];
p.Reset(x, y, z, isActive);
LinkedListNode newNode = _nodePool[Count];
newNode.Item = p;
newNode.Next = null;
if (_head == null)
{
_head = newNode;
}
else
{
_tail.Next = newNode;
}
_tail = newNode;
Count++;
}
public CustomEnumerator GetEnumerator() => new CustomEnumerator(_head);
IEnumerator<Point> IEnumerable<Point>.GetEnumerator() => new CustomEnumerator(_head);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public readonly struct CalculationContext
{
public readonly double Modifier;
public CalculationContext(double modifier) => Modifier = modifier;
}
public static class Program
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double Calculate(Point p, in CalculationContext ctx, Func<Point, CalculationContext, double> formula)
{
return formula(p, ctx);
}
public static void Main()
{
VirtualLinkedList points = new VirtualLinkedList();
for (int i = 0; i < 1_000_000; i++)
{
points.AddLast(i * 1.1, i * 1.2, i * 1.3, i % 2 == 0);
}
Stopwatch sw = Stopwatch.StartNew();
double? totalSum = 0.0;
double localSum = 0.0;
int index = 0;
Func<Point, CalculationContext, double> formula = (pt, ctx) => (pt.x.Value + pt.y.Value + pt.z.Value) * ctx.Modifier;
foreach (Point p in points)
{
int? stepId = index;
double modifier = (stepId % 2 == 0) ? 1.0 : 1.005;
if (p.isActive.GetValueOrDefault(false))
{
var context = new CalculationContext(modifier);
localSum += Calculate(p, in context, formula);
}
index++;
}
totalSum = localSum;
sw.Stop();
Console.WriteLine($"Результат: {totalSum}");
Console.WriteLine($"Время выполнения в C#: {sw.Elapsed.TotalMilliseconds:F2} мс");
}
}
В эту игру можно играть вдвоем))Да вообще не проблема, я могу не использовать структуры, дописать, чтобы проверка была аналогична условию, от неё ничего не изменится, все ограничения задачи и поставлены выполняются. Перебираю коллекция в цикле foreach, получаю полноценные объекты Point, работаю со свойствами-обёртками (double?, bool?), проверяю их через аналогOptional (GetValueOrDefault)и вызываю метод через лямбду с упаковкой индекса вint?
Посмотреть вложение 96608
Вся форма кода осталась оригинальной и оригинальной джавовской задачей.
C#:using System.Collections; using System.Diagnostics; using System.Runtime.CompilerServices; public class Point { private readonly VirtualStorage _storage; internal int _index; public double? x => _storage.XArray[_index]; public double? y => _storage.YArray[_index]; public double? z => _storage.ZArray[_index]; public bool? isActive => _storage.ActiveArray[_index]; public Point(VirtualStorage storage, int index) { _storage = storage; _index = index; } } public class VirtualStorage { public readonly double[] XArray = new double[1_000_000]; public readonly double[] YArray = new double[1_000_000]; public readonly double[] ZArray = new double[1_000_000]; public readonly bool[] ActiveArray = new bool[1_000_000]; } public class VirtualLinkedList : IReadOnlyCollection<Point> { private readonly VirtualStorage _storage; public int Count => 1_000_000; public VirtualLinkedList(VirtualStorage storage) => _storage = storage; public Enumerator GetEnumerator() => new Enumerator(_storage); IEnumerator<Point> IEnumerable<Point>.GetEnumerator() => new Enumerator(_storage); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator : IEnumerator<Point> { private readonly VirtualStorage _storage; private int _currentIdx; public Point Current { get; } public Enumerator(VirtualStorage storage) { _storage = storage; _currentIdx = -1; Current = new Point(storage, 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _currentIdx++; if (_currentIdx < 1_000_000) { Current._index = _currentIdx; return true; } return false; } object IEnumerator.Current => Current; public void Reset() => _currentIdx = -1; public void Dispose() { } } } public readonly struct CalculationContext { public readonly double Modifier; public CalculationContext(double modifier) => Modifier = modifier; } public static class Program { [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double Calculate(Point p, in CalculationContext ctx, Func<Point, CalculationContext, double> formula) { return formula(p, ctx); } public static void Main() { VirtualStorage storage = new VirtualStorage(); for (int i = 0; i < 1_000_000; i++) { storage.XArray[i] = i * 1.1; storage.YArray[i] = i * 1.2; storage.ZArray[i] = i * 1.3; storage.ActiveArray[i] = (i % 2 == 0); } VirtualLinkedList points = new VirtualLinkedList(storage); Stopwatch sw = Stopwatch.StartNew(); double? totalSum = 0.0; double localSum = 0.0; int index = 0; Func<Point, CalculationContext, double> formula = (pt, ctx) => (pt.x.Value + pt.y.Value + pt.z.Value) * ctx.Modifier; foreach (Point p in points) { int? stepId = index; double modifier = (stepId % 2 == 0) ? 1.0 : 1.005; if (p.isActive.GetValueOrDefault(false)) { var context = new CalculationContext(modifier); localSum += Calculate(p, in context, formula); } index++; } totalSum = localSum; sw.Stop(); Console.WriteLine($"Результат: {totalSum}"); Console.WriteLine($"Время выполнения в C#: {sw.Elapsed.TotalMilliseconds:F2} мс"); } }
Или мне убрать Enumerator?)
Никто не запрещал использовать в Go колоночное хранение и паттерн легковес, но ты же код не показал
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.LongAdder;
public class Main {
static final int N = 10_000_000;
static final ScopedValue<RequestContext> CONTEXT = ScopedValue.newInstance();
record RequestContext(UUID requestId, UUID token) {}
static class Client {
int query(UUID id) {
var context = CONTEXT.get();
if (!context.requestId().equals(id) || context.token() == null) {
throw new SecurityException();
}
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 5001));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return Math.floorMod(id.hashCode(), 11);
}
}
public static void main(String[] args) {
var ids = new UUID[N];
var contexts = new RequestContext[N];
for (int i = 0; i < N; i++) {
ids[i] = UUID.randomUUID();
contexts[i] = new RequestContext(ids[i], UUID.randomUUID());
}
var client = new Client();
var sum = new LongAdder();
long start = System.nanoTime();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < N; i++) {
var id = ids[i];
var context = contexts[i];
executor.submit(() -> ScopedValue.where(CONTEXT, context)
.run(() -> sum.add(client.query(id))));
}
}
System.out.println(sum.sum());
System.out.printf("%.3f s%n", (System.nanoTime() - start) / 1e9);
}
}
java --version
openjdk 26.0.1 2026-04-21
OpenJDK Runtime Environment Temurin-26.0.1+8 (build 26.0.1+8)
OpenJDK 64-Bit Server VM Temurin-26.0.1+8 (build 26.0.1+8, mixed mode, sharing)
java .\Main.java
50012355
32,628 s
Если это легаси, то там и нету виртуальных потоков и там далеко не java 21увы, легаси
Ну да) Там шарпы. Или гоу, или плюсы, или раст, или питон, или пхп, или еще чет.Если это легаси, то там и нету виртуальных потоков и там далеко не java 21
Тогда задача не подходитТам шарпы
с нет фреймворка 4.5 async/await существует стабильно)Асинхронной версии метода не существует
гоурутины ещё в 2009 были, до предрелиза языка
В чём проблема на плюсах? С большой обновой стандарта языка 2011 тоже появились интересные фичи, данные копейки
в 2010-2015 можно писать аналог горутин спокойно
Ну с легаси согласен могут быть проблемы, но и пример использует Java 21+ который далеко не легаси
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?