Irrespective of the language, these sort of delays are always at the mercy of the scheduling jitter of the underlying operating system.
The operating system is at liberty to preempt any application at any time, and then resume it some arbitrary time later. If you ask the OS to sleep for
1ms, the OS will deschedule the process, and then resume at some point in time after 1ms. Exactly how long after depends
on what else is running on the processor, contention for cores etc. Once the process gets to run again, the Go runtime
does its own scheduling, it may have many more goroutines ready to run than it has available OS threads. So the gorountine which
is waiting on the ticker could wait an arbitrary amount of time beyond the minimum 1ms delay.
The demand for all delays to be within 5% of 1ms is hard to achieve in any language running on top of a general purpose (non-realtime) OS, and is double hard in Go which adds an additional layer of scheduling.
A better approach would be to start with a 1ms ticker, but then at each tick calculate the amount of time which has actually elapsed, and then send the required number of requests to catch-up with the required request rate. This will mean a slightly uneven and jittery request rate at the microscopic level, and the amount of jitter will be affected by how heavily loaded the machine is. But the request rate will even out when looking over longer time periods. This will be the best you can do in Go, or in user-space in any language running on a non-real-time OS.