¿Existe un método para generar un UUID con go language?

109

Tengo un código que se parece a esto:

u := make([]byte, 16)
_, err := rand.Read(u)
if err != nil {
    return
}

u[8] = (u[8] | 0x80) & 0xBF // what does this do?
u[6] = (u[6] | 0x40) & 0x4F // what does this do?

return hex.EncodeToString(u)

Devuelve una cadena con una longitud de 32, pero no creo que sea un UUID válido. Si es un UUID real, ¿por qué es un UUID y cuál es el propósito del código que modifica el valor de u[8]y u[6].

¿Existe una forma mejor de generar UUID?

hardPass
fuente
1
Esta respuesta parece más adecuada ahora.
ViKiG

Respuestas:

32
u[8] = (u[8] | 0x80) & 0xBF // what's the purpose ?
u[6] = (u[6] | 0x40) & 0x4F // what's the purpose ?

Estas líneas limitan los valores de los bytes 6 y 8 a un rango específico. rand.Readdevuelve bytes aleatorios en el rango 0-255, que no son todos valores válidos para un UUID. Por lo que puedo decir, esto debería hacerse para todos los valores en el segmento.

Si está en Linux, también puede llamar a /usr/bin/uuidgen.

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    out, err := exec.Command("uuidgen").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", out)
}

Cuyos rendimientos:

$ go run uuid.go 
dc9076e9-2fda-4019-bd2c-900a8284b9c4
Jimt
fuente
23
En particular, este enfoque es lento; en un MacBook Air de 2012, esta estrategia puede producir solo 170 uuids / segundo.
Jay Taylor
12
Y usando la biblioteca nu7hatch / gouuid, pude generar 172,488 uuids / segundo.
Jay Taylor
2
Buena explicación de los bytes u[6]y u[8].
chowey
3
En mi sistema (Ubuntu 15.10) también necesitaba ejecutar la salida del comando a través de strings.Trim (string (out)) para eliminar el carácter de nueva línea, de lo contrario, ¿se ingresó como un final? personaje en el sistema de archivos.
gregtczap
39
Llamar a un programa externo que puede o no existir es una forma terrible de hacer esta tarea bastante simple.
Timmmm
96

Puede generar UUID utilizando la biblioteca go-uuid . Esto se puede instalar con:

go get github.com/nu7hatch/gouuid

Puede generar UUID aleatorios (versión 4) con:

import "github.com/nu7hatch/gouuid"

...

u, err := uuid.NewV4()

El UUIDtipo devuelto es una matriz de 16 bytes, por lo que puede recuperar el valor binario fácilmente. También proporciona la representación de cadena hexadecimal estándar a través de su String()método.

El código que tiene también parece que también generará un UUID de versión 4 válido: la manipulación bit a bit que realiza al final establece la versión y los campos de variante del UUID para identificarlo correctamente como versión 4 . Esto se hace para distinguir los UUID aleatorios de los generados a través de otros algoritmos (por ejemplo, los UUID de la versión 1 basados ​​en su dirección MAC y hora).

James Henstridge
fuente
2
@Flimzy para personas que no saben lo que están haciendo, lo más probable es que sea cierto. Introducir dependencias innecesarias siempre es algo malo.
Erik Aigner
31
@ErikAigner Mientras sean 50 líneas no tengo que pensar, escribir y probar, las llevaré gracias .. Tengo otras cosas que hacer y luego reinventar la rueda.
RickyA
3
Esta biblioteca parece que en realidad no es compatible con RFC4122: github.com/nu7hatch/gouuid/issues/28 (problema abierto actualmente a partir del 2/1/2016)
Charles L.
1
@ErikAigner reinventar la rueda también es algo innecesario. Si existe una biblioteca y la hace bien, ¿por qué molestarse en hacer la suya si no la está haciendo para aprender a hacerlo?
Sir
4
@ErikAigner me parece ridículo. Nadie reinventa las cosas que ya están hechas a menos que pueda hacerlo mejor o necesite algo específico para su programa, si inspecciona el código y ve que lo hace bien, ¿por qué molestarse en hacerlo usted mismo? No solo desperdicia tiempo y costos de desarrollo, también está potencialmente van a traer errores o simplemente implementaciones incorrectas si no sabe completamente lo que está haciendo, estas bibliotecas generalmente están hechas de personas que saben lo que están haciendo. No es un novato usar bibliotecas de terceros, su único novato simplemente asume que funciona y no inspecciona el código primero ..
Sir
70

La go-uuidbiblioteca NO cumple con RFC4122. Los bits de variante no están configurados correctamente. Ha habido varios intentos por parte de los miembros de la comunidad para corregir esto, pero las solicitudes de extracción para la corrección no se aceptan.

Puede generar UUID utilizando la biblioteca Go uuid que reescribí en función de la go-uuidbiblioteca. Hay varias correcciones y mejoras. Esto se puede instalar con:

go get github.com/twinj/uuid

Puede generar UUID aleatorios (versión 4) con:

import "github.com/twinj/uuid"

u := uuid.NewV4()

El tipo de UUID devuelto es una interfaz y el tipo subyacente es una matriz.

La biblioteca también genera UUID v1 y genera correctamente UUID v3 y 5. Hay varios métodos nuevos para ayudar con la impresión y el formateo y también nuevos métodos generales para crear UUID basados ​​en datos existentes.

twinj
fuente
4
Me gusta este paquete. Lo adopté oficialmente para todas mis aplicaciones. Descubrí que el paquete nu7hatch no era compatible con RFC4122.
Richard Eng
+1 De acuerdo, las actualizaciones y las extensiones de impresión / formato ya están incluidas.
eduncan911
4
¿Falta el descargo de responsabilidad? : p
chakrit
3
¿Qué es la biblioteca "debajo"? Debe evitar usar arriba y abajo en SO, ya que eso puede cambiar con bastante rapidez.
Stephan Dollberg
También hay otro equivalente, satori / go.uuid . No lo probé todavía, pero lo usaré como reemplazo del proyecto muerto nu7hatch ...
shadyyx
52

"crypto / rand" es un paquete multiplataforma para la generación de bytes aleatorios

package main

import (
    "crypto/rand"
    "fmt"
)

// Note - NOT RFC4122 compliant
func pseudo_uuid() (uuid string) {

    b := make([]byte, 16)
    _, err := rand.Read(b)
    if err != nil {
        fmt.Println("Error: ", err)
        return
    }

    uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

    return
}
Ken Cloud
fuente
3
pseudo_uuidporque faltan los identificadores no aleatorios como la dirección MAC y cualquier otra cosa que especifique RFC4122? Entonces, en realidad, es más aleatorio.
Xeoncross
2
buena respuesta; Lo expandí en stackoverflow.com/a/48134820/1122270 , y creo que mucha gente en realidad no necesita usar UUID específicamente (ni el sha1 / sha256 que pensé que necesitaba usar para mi propio aleatorio) id problema), pero simplemente quiere algo aleatorio y único, y su muestra proporciona un buen comienzo para una solución
cnst
¡Gracias! Bastante simple
Karl Pokus
1. Esto no cumple con ningún estándar 2. El uso solo %xtiene problemas con valores de bytes inferiores a 128, debe aplicar relleno, es decir, %04xpara un par de bytes
Ja͢ck
38

Hay una implementación oficial de Google: https://github.com/google/uuid

La generación de un UUID de la versión 4 funciona así:

package main

import (
    "fmt"
    "github.com/google/uuid"
)

func main() {
    id := uuid.New()
    fmt.Println(id.String())
}

Pruébelo aquí: https://play.golang.org/p/6YPi1djUMj9

shutefan
fuente
1
El godoc recomienda usar New()y es equivalente auuid.Must(uuid.NewRandom())
Jim
@Jim: ¡tienes razón! Actualicé mi respuesta en consecuencia.
shutefan
Tenga en cuenta que New () puede ser "fatal" (lo cual está bien en algunos casos). En los casos en que no desee que su programa falle, simplemente use uuid.NewRandom (), que devuelve un UUID y un error.
Tomer
@Tomer: ¡cierto! Aunque me pregunto bajo qué circunstancias esto sucedería realmente. Esta es la parte relevante del código: github.com/google/uuid/blob/… Por defecto, el lector es un rand.Reader. No estoy seguro si ese alguna vez devolvería un error o si esto solo puede suceder con un lector personalizado ...
shutefan
1
Hola @shutefan: estoy de acuerdo en que puede ser raro. rand.Reader llama a las funciones del Kernel ( golang.org/src/crypto/rand/rand.go ). Estos pueden fallar en ciertos escenarios.
Tomer
12

De la publicación de Russ Cox :

No hay biblioteca oficial. Ignorando la comprobación de errores, parece que funcionaría bien:

f, _ := os.Open("/dev/urandom")
b := make([]byte, 16)
f.Read(b)
f.Close()
uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])

Nota: En la versión original, anterior a Go 1, la primera línea era:

f, _ := os.Open("/dev/urandom", os.O_RDONLY, 0)

Aquí se compila y ejecuta, solo /dev/urandomdevuelve todos los ceros en el patio de recreo. Debería funcionar bien a nivel local.

En el mismo hilo se encuentran algunos otros métodos / referencias / paquetes.

zzzz
fuente
12
Sin embargo, esto no generará un UUID válido: los UUID de la versión 4 (el tipo basado en datos aleatorios) requieren que se establezcan algunos bits de cierta manera para evitar conflictos con los formatos de UUID no aleatorios.
James Henstridge
4
Es mejor usarlo import "crypto/rand"en mi opinión, pero +1 para uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]). Combinado con el código del OP, y esto funciona muy bien.
chowey
2
Usando el paquete crypto / rand: play.golang.org/p/7JJDx4GL77 . El código de zzzz hace lo que hace crypt / rand, excepto que también cubre plataformas que no son compatibles con / dev / urandom (Windows).
Dibujó el
Cabe señalar que esto es específico de la plataforma
Dan Esparza
2
@Matt: el problema es que los otros formatos UUID obtienen su singularidad al delegar en alguna otra autoridad (por ejemplo, que su dirección MAC Ethernet es única), y luego combinar eso con algo más (por ejemplo, el tiempo más un contador). Si produce un UUID aleatorio que no está correctamente formateado como V4, está debilitando el sistema.
James Henstridge
8

Como parte de la especificación de uuid, si genera un uuid de forma aleatoria, debe contener un "4" como carácter 13 y un "8", "9", "a" o "b" en el 17 ( fuente ).

// this makes sure that the 13th character is "4"
u[6] = (u[6] | 0x40) & 0x4F
// this makes sure that the 17th is "8", "9", "a", or "b"
u[8] = (u[8] | 0x80) & 0xBF 
Eric chiang
fuente
4

El gorand paquete tiene un método UUID que devuelve un UUID Versión 4 (generado aleatoriamente) en su representación de cadena canónica ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") y cumple con RFC 4122.

También utiliza el paquete crypto / rand para garantizar la generación más segura criptográficamente de UUID en todas las plataformas compatibles con Go.

import "github.com/leonelquinteros/gorand"

func main() {
    uuid, err := gorand.UUID()
    if err != nil {
        panic(err.Error())
    }

    println(uuid)
} 
peiiion
fuente
4

En Linux, puede leer desde /proc/sys/kernel/random/uuid:

package main

import "io/ioutil"
import "fmt"

func main() {
    u, _ := ioutil.ReadFile("/proc/sys/kernel/random/uuid")
    fmt.Println(string(u))
}

¡Sin dependencias externas!

$ go run uuid.go 
3ee995e3-0c96-4e30-ac1e-f7f04fd03e44
almas
fuente
4
Votado en contra porque depender directamente de una plataforma de host en un lenguaje de programación que se usa para aplicaciones multiplataforma es peor que una dependencia externa.
Adiós
1
El lenguaje de programación puede ser multiplataforma, pero son soluciones muy comunes específicas de Linux que nunca estarán en otra plataforma, por lo que es una respuesta válida en mi opinión.
ton
1

Para Windows, hice recientemente esto:

// +build windows

package main

import (
    "syscall"
    "unsafe"
)

var (
    modrpcrt4 = syscall.NewLazyDLL("rpcrt4.dll")
    procUuidCreate = modrpcrt4.NewProc("UuidCreate")
)

const (
    RPC_S_OK = 0
)

func NewUuid() ([]byte, error) {
    var uuid [16]byte
    rc, _, e := syscall.Syscall(procUuidCreate.Addr(), 1,
             uintptr(unsafe.Pointer(&uuid[0])), 0, 0)
    if int(rc) != RPC_S_OK {
        if e != 0 {
            return nil, error(e)
        } else {
            return nil, syscall.EINVAL
        }
    }
    return uuid[:], nil
}
kostix
fuente
2
Votado en contra porque depender directamente de una plataforma de host en un lenguaje de programación que se usa para aplicaciones multiplataforma es peor que una dependencia externa.
Adiós
1
@ Adiós, me pregunto por qué se considera a sí mismo una autoridad para decidir qué "es peor" (y qué no) para hojear todas las respuestas dadas a esta pregunta y rechazar todas las que son "dependientes del sistema". Estas respuestas se dieron para a) ampliar el horizonte de todas las opciones posibles yb) presentar colectivamente una imagen completa. Así que por favor deje de "jugar a SO" infantilmente y piense antes de actuar.
kostix
Respuesta corta. Redacción de código mantenible. Su respuesta no se puede migrar a una plataforma diferente. Entonces, si el OP decidiera mover su aplicación a otra plataforma, la aplicación se rompería. He tenido una buena cantidad de personas que escribieron código dependiente de la plataforma donde es totalmente innecesario y crea más problemas de lo que vale la pena. No escribes código solo para ti. Escribe código para las personas que lo mantendrán después de que usted se haya ido. Por eso esta respuesta no es apropiada. No hay razón para recurrir a ad hominems y llamarme infantil.
Adiós
1
@ Adiós, reaccioné exageradamente, así que discúlpeme por el ataque. Aún no estoy convencido de tus razones, pero supuestamente es ese caso de "acordemos estar en desacuerdo".
kostix
1

Esta biblioteca es nuestro estándar para la generación y el análisis de uuid:

https://github.com/pborman/uuid

James McGill
fuente
Tenga en cuenta que la propia biblioteca de Google ( github.com/google/uuid ) se basa parcialmente en github.com/pborman/uuid , que, a su vez, ha incorporado algunos de los cambios que realizó Google. Sin embargo, supuestamente, si desea contribuir a cualquiera de estos proyectos, debe firmar (o haber firmado) un Acuerdo de licencia de colaborador (CLA). Aparentemente, este no fue el caso en agosto de 2015, cuando se agregó su respuesta; @pborman agregó eso solo el 16 de febrero de 2016 .
Gwyneth Llewelyn