English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Bei der Entwicklung von Server-Systemen, um den Anforderungen großer Datenverarbeitung zu entsprechen, müssen wir oft die asynchrone Speicherung der Daten durchführen, insbesondere wenn wir verteilte Systeme entwickeln. In diesem Fall können wir nicht warten, bis die Datenbank in die Datenbank eingefügt wurden, um den automatischen ID zu erhalten, sondern müssen eine globale eindeutige ID vor der Eingabe in die Datenbank generieren. Eine globale eindeutige ID kann im Spielserver verwendet werden, um zukünftige Serverkombinationen zu erleichtern und Keinkonflikt zu vermeiden. Es kann auch zukünftiges Wachstum des Geschäfts ermöglichen, z.B. wenn ein Benutzergegenstand in einem bestimmten Schicht gespeichert werden muss, und dieser Schicht kann nach dem Bereichswert der Benutzer-ID bestimmt werden, z.B. Benutzer-ID größer als1000 ist kleiner als100000-Nutzer in einem Schicht. Derzeit werden folgende Arten häufig verwendet:
1Java-benutzerdefinierte UUID.
UUID.randomUUID().toString(), kann durch lokale Generierung durch den Dienstprogramm erzeugt werden, die Generierung der ID hängt nicht von der Implementierung der Datenbank ab.
Vorteile:
Lokale Generierung von IDs, ohne Remote-Aufrufe durchzuführen.
globale Einzigartigkeit und Wiederholungsfreiheit.
sehr gute horizontale Skalierbarkeit.
Nachteile:
Die ID hat128 bits, der erforderliche Speicherplatz ist groß und muss als String-Typ gespeichert werden, was die Index-Effizienz extrem niedrig macht.
Die generierten IDs enthalten keine Timestamps und können keine trendige Inkrementierung gewährleisten, was bei der Datenbank-Verteilung und Tabelle nicht gut abhängig gemacht werden kann.
2Basierend auf der incr-Methode von Redis
Redis ist selbst ein Einthread-Operation, und incr gewährleistet eine atomare inkrementelle Operation. Und es unterstützt die Einstellung des Inkrement-Schritts.
Vorteile:
Einfache Deployment und Verwendung, es genügt, eine Redis-API aufzurufen.
Mehrere Server können eine gemeinsame Redis-Dienstleistung teilen, was die Entwicklungszeit für gemeinsame Daten reduziert.
Redis kann in Clustern bereitgestellt werden, um das Problem des Single-Point-of-Failure zu lösen.
Nachteile:
Wenn das System zu umfangreich ist, kann die gleichzeitige Anfrage von mehreren Dienstleistungen an Redis zu einer Leistungslücke führen.
3Lösung von Flicker
Diese Lösung basiert auf dem automatischen Zähler der Datenbank-ID und verwendet eine separate Datenbank zur Generierung von IDs. Details können Sie im Internet finden. Persönlich halte ich das für recht umständlich und empfehle es nicht.
4,Twitter Snowflake
Snowflake ist ein von Twitter bereitgestellter Open-Source-Distributed-ID-Generierungsalgorithmus, dessen Kernidee darin besteht,41Bit als Millisekunden,10Bit als Maschinennummer,12Bit als Millisekunden-Sekvensnummer. Diese Algorithmus kann theoretisch pro Sekunde auf einem Single-Node-Computer1000*(2^12) Stücke, was ungefähr400W IDs, die den Geschäftsanforderungen vollständig gerecht werden können.
Nach dem Prinzip des Snowflake-Algorithms können wir basierend auf unseren Geschäftsuseinen eigenen globalen eindeutigen ID erzeugen. Da die Länge des long-Typs in Java64bits, daher müssen wir die von uns entworfenen IDs steuern64bits。
Vorteil: Hochleistungsfähig, geringe Latenz; unabhängige Anwendung; zeitlich geordnet.
Nachteil: Erfordert独立的 Entwicklung und Deployment.
Beispielsweise enthält der von uns entworfene ID die folgenden Informationen:
| 41 bits: Timestamp | 3 bits: Region | 10 bits: Maschinennummer | 10 bits: Sequenznummer |
Java-Code zum Generieren eines eindeutigen IDs:
/** * Benutzerdefinierter ID-Generator * ID-Generierungsregeln: ID ist so lang 64 bits * * | 41 bits: Timestamp (Millisekunden) | 3 bits: Region (Rechenzentrum) | 10 bits: Maschinennummer | 10 bits: Sequenznummer | */ public class GameUUID{ // Referenzzeit private long twepoch = 1288834974657L; //Do, 04 Nov 2010 01:42:54 GMT // Anzahl der Bits für die Regional-ID private final static long regionIdBits = 3L; // Anzahl der Bits für die Maschinen-ID private final static long workerIdBits = 10L; // Anzahl der Bits für die Sequenz-ID private final static long sequenceBits = 10L; // Maximaler Wert der Regional-ID private final static long maxRegionId = -1L ^ (-1L << regionIdBits); // Maximaler Wert der Maschine-ID private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); // Maximaler Wert des Sequenz-ID private final static long sequenceMask = -1L ^ (-1L << sequenceBits); // Maschine-ID nach links verschieben10位 private final static long workerIdShift = sequenceBits; // 业务ID偏左移20位 private final static long regionIdShift = sequenceBits + workerIdBits; // 时间毫秒左移23位 private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits; private static long lastTimestamp = -1L; private long sequence = 0L; private final long workerId; private final long regionId; public GameUUID(long workerId, long regionId) { // 如果超出范围就抛出异常 if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); } if (regionId > maxRegionId || regionId < 0) { throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0"); } this.workerId = workerId; this.regionId = regionId; } public GameUUID(long workerId) { // 如果超出范围就抛出异常 if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); } this.workerId = workerId; this.regionId = 0; } public long generate() { return this.nextId(false, 0); } /** * 实际产生代码的 * * @param isPadding * @param busId * @return */ private synchronized long nextId(boolean isPadding, long busId) { long timestamp = timeGen(); long paddingnum = regionId; if (isPadding) {}} paddingnum = busId; } if (timestamp < lastTimestamp) { try { throw new Exception("Die Uhr hat sich zurückbewegt. Erzeugen von ID wird abgelehnt " + (lastTimestamp - timestamp) + " milliseconds"); } catch (Exception e) { e.printStackTrace(); } } //Wenn die generierte Zeit von letzter Zeit gleich ist, sind sie im selben Millisekundenbereich if (lastTimestamp == timestamp) { //sequence wird erhöht, da sequence nur10bit, daher wird es mit sequenceMask AND'ed, um die höheren Bits zu entfernen sequence = (sequence + 1) & sequenceMask; //Überprüfen Sie, ob es überfließt, das bedeutet, dass es in jeder Millisekunde über1024wenn es ist1024wenn es mit sequenceMask AND'ed wird, ist sequence gleich 0 if (sequence == 0) { //Warten Sie auf den nächsten Millisekunden timestamp = tailNextMillis(lastTimestamp); } } else { // Wenn die generierte Zeit von letzter Zeit unterschiedlich ist, setzen Sie sequence zurück, das bedeutet, dass sequence ab dem nächsten Millisekundenbeginn neu von 0 an beginnt zu addieren, // Um die Zufälligkeit des Nachfolgenden Ziffernfelds zu erhöhen, setzen Sie die letzte Ziffer auf eine Zufallszahl sequence = new SecureRandom().nextInt(10; } lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId << workerIdShift) | sequence; } // Verhindern Sie, dass die generierte Zeit kleiner als die vorherige Zeit ist (aufgrund von NTP-Rückspielen und ähnlichen Problemen), um den Trends der Zunahme beizubehalten. private long tailNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } // 获取当前的时间戳 protected long timeGen() { return System.currentTimeMillis(); } }
使用自定义的这种方法需要注意以下几点:
为了保持增长趋势,要避免有些服务器的时间早,有些服务器的时间晚。需要控制好所有服务器的时间,并且要避免NTP时间服务器回拨服务器的时间;在跨毫秒时,序列号总是归0,这会导致序列号为0的ID较多,使得生成的ID取模后不均匀。因此,序列号不是每次都归0,而是归一个0到9的随机数。
上面提到的这些方法,我们可以根据自己的需求进行选择。在游戏服务器开发中,可以根据自己的游戏类型进行选择,例如手机游戏,可以使用简单的redis方式,简单且不容易出错。由于这种游戏单服并发新建id量并不大,完全可以满足需求。而对于大型世界游戏服务器,它本身以分布式为主,因此可以使用snowflake的方式。上面的snowflake代码只是一个示例,需要根据实际需求进行定制,因此有额外的开发量,并且需要注意上述提到的注意事项。
以上所述是小编为大家介绍的在Java代码中实现游戏服务器生成全局唯一ID的方法汇总,希望对大家有所帮助。如果大家有任何疑问,请给我留言,我会及时回复大家的。在此也非常感谢大家对呐喊教程网站的支持!
声明:本文内容来源于网络,版权归原作者所有。内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未进行人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,请发送邮件至:notice#w。3在发邮件时,请将#更换为@进行举报,并提供相关证据。一经查实,本站将立即删除涉嫌侵权内容。