I am using Mongo-driver with gin framework. I have written code to connect mongodb in DB package and if I write query inside db/connect.go, it works but when I use same dbcon in other package it doesn't.
db/connect.go:
var dbcon *mongo.Database
func ConfigDB() (*mongo.Database) {
ctx := context.Background()
client, err := mongo.Connect(
ctx,
options.Client().ApplyURI("mongodb://localhost:27017/todo"),
)
if err != nil {
log.Fatal(err)
}
dbcon = client.Database("todo")
}
if I use the code below in same db/connect.go, then it works but when I use the same code in handler/task.go, then it won't.
func CreateTask() () {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
res, err := dbcon.Collection("ttest").InsertOne(ctx, bson.D{
{"task", "test4"},
{"createdAt", "test"},
{"modifiedAt","test3"},
})
if err != nil {
fmt.Println( err))
}
}
I have to implement a mongo-driver in my project, but due to above issue I am facing problem to implement.
You'll have to import to import the db/connect.go file into the handler/task.go. This is not working because they are in different packages.
In my opinion you could refactor your code like this
func ConfigDB() (*mongo.Database) {
ctx := context.Background()
client, err := mongo.Connect(
ctx,
options.Client().ApplyURI("mongodb://localhost:27017/todo"),
)
if err != nil {
log.Fatal(err)
}
return client.Database("todo")
}
import (
"db/connect"
)
func CreateTask() () {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
res, err := ConfigDB().Collection("test").InsertOne(ctx, bson.D{
{"task", "test4"},
{"createdAt", "test"},
{"modifiedAt","test3"},
})
if err != nil {
fmt.Println( err))
}
}
Here I post a complete working example.
I catch the mongo session connection in a global variable. So that, anywhere in the project you can access the active connection.
package main
import (
"fmt"
"net/http"
"os"
"github.com/gin-gonic/gin"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
// SESSION ensure global mongodb connection
var SESSION *mgo.Session
func init() {
// mongodb manual connection using host ip. Replace your host IP address there
session, err := mgo.Dial("172.17.0.2")
// session, err := mgo.Dial("<HostIP>")
Must(err)
fmt.Println(err)
SESSION = session
}
func main() {
port := os.Getenv("PORT")
gin.SetMode(gin.ReleaseMode)
// gin.SetMode(gin.DebugMode)
r := gin.Default()
r.Use(mapMongo)
if port == "" {
port = "8000"
}
r.POST("/api/v1/task", CreateTask)
http.ListenAndServe(":"+port, r)
}
// close connection
func mapMongo(c *gin.Context) {
s := SESSION.Clone()
defer s.Close()
c.Set("mongo", s.DB("mongotask"))
c.Next()
}
// Must to catch the mongo panic issues
func Must(err error) {
if err != nil {
panic(err.Error())
}
}
// NewTask Struct/model
type NewTask struct {
Id bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"`
Task string
}
// Mongo bson generate New unique Id each request
func (self *NewTask) Init() {
self.Id = bson.NewObjectId()
}
const (
// CollectionTask is the collection name
CollectionTask = "taskCollection"
)
// CreateTask to create new Task message
func CreateTask(c *gin.Context) {
var newTask NewTask
err := c.BindJSON(&newTask)
if err != nil {
c.Error(err)
return
}
mongodb := c.MustGet("mongo").(*mgo.Database)
con := mongodb.C(CollectionTask)
// fmt.Println(newTask)
con.Insert(newTask)
if err != nil {
c.Error(err)
}
}
Related
I need help with Golang websocket. I'm using Fiber with websocket and redis.
Here is the code:
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"log"
"test4/controllers"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
var ctx = context.Background()
var redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
func TestSocket() fiber.Handler {
socket := websocket.New(func(c *websocket.Conn) {
go deliverMessages(c)
var (
msg []byte
err error
)
for {
if _, msg, err = c.ReadMessage(); err != nil {
log.Println("read:", err)
break
}
if err := redisClient.Publish(ctx, "chat", msg).Err(); err != nil {
log.Println("publish:", err)
break
}
}
})
return socket
}
func deliverMessages(c *websocket.Conn) {
subscriber := redisClient.Subscribe(ctx, "chat")
user := User{}
for {
msg, err := subscriber.ReceiveMessage(ctx)
if err != nil {
log.Println("subscriber:", err)
panic(err)
}
if err := json.Unmarshal([]byte(msg.Payload), &user); err != nil {
log.Println("Unmarshal:", err)
panic(err)
}
text := []byte(fmt.Sprintf("{\"name\":\"%s\", \"email\":\"%s\"}", user.Name, user.Email))
if err = c.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println("write:", err)
break
}
}
}
func main() {
app := fiber.New(fiber.Config{
Prefork: true,
CaseSensitive: true,
StrictRouting: true,
DisableStartupMessage: true,
ServerHeader: "Test v3",
})
app.Get("/", controllers.Home)
app.Get("/ws", TestSocket())
log.Fatal(app.Listen("0.0.0.0:3000"))
}
How to produce the error:
Install Redis and run go run main.go
Now open http://127.0.0.1:3000/ in two tabs
click open on both tabs, and then you will see OPEN on right side of browser
click send on both tabs and you will get SEND and RESPONSE
Now close one tab and on go program terminal you will see error (see attached screenshot)
Now publish data to chat channel on redis-cli
Here is the error I am getting:
I think this is nil pointer websocket.Conn issue.
When close websocket connection, goroutine's c *websocket.Conn is loose data.
Pointer point nil.
solution
use channel, use local redisClient var
func TestSocket() fiber.Handler {
socket := websocket.New(func(c *websocket.Conn) {
var redisClient = redis.NewClient(&redis.Options{ // <-- use local redisClient var
Addr: "localhost:6379",
})
go deliverMessages(c)
var (
msg []byte
err error
)
defer func() {
redisClient.Close() // <-- then close, when websocket connection close
quitSubscribeGoRutine <- true // <-- change true, when websocket connection close
}()
for {
if _, msg, err = c.ReadMessage(); err != nil {
log.Println("read:", err)
...
func deliverMessages(c *websocket.Conn) {
subscriber := redisClient.Subscribe(ctx, "chat")
user := User{}
for {
select {
case <-quitSubscribeGoRutine: // <-- exit goroutine, when channel is true
return
default:
msg, err := subscriber.ReceiveMessage(ctx) // <-- exit goroutine, when redisClient close
if err != nil {
log.Println("subscriber:", err)
break // <-- use break instead of panic
}
if err := json.Unmarshal([]byte(msg.Payload), &user); err != nil {
log.Println("Unmarshal:", err)
panic(err)
}
text := []byte(fmt.Sprintf("{\"name\":\"%s\", \"email\":\"%s\"}", user.Name, user.Email))
if err = c.WriteMessage(websocket.TextMessage, text); err != nil {
log.Println("write:", err)
break
}
}
}
}
Here's what I have thus far:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
func subscribe(urls Urls) []byte {
req, err := http.NewRequest("GET", urls.Url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("authentication", "Bearer " + urls.Token)
http_client := &http.Client{}
res, err := http_client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
resourceResp, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(resourceResp))
var data map[string]interface{}
error := json.Unmarshal([]byte(resourceResp), &data)
if error != nil {
log.Fatal(error)
}
return subscribe(urls)
}
type Urls struct {
Url string
Token string
}
func main() {
var urls [2]Urls
urls[0] = Urls{
Url: "https://example.com/users/8",
Token: "abcdefg",
}
urls[1] = Urls{
Url: "https://example.com/users/9",
Token: "hijklmnop",
}
subscribe(urls[0])
subscribe(urls[1])
}
The end goal is to "subscribe" to the multiple urls and pull any updated data (eventually adding it to a queue, but one step at a time). After that, reestablish the connection. Right now, only the first subscribe gets run. Thanks!
I think what you're asking is for the subscribe functions to be run in parallel. One way is to wrap them in goroutines and wait for all the goroutines to finish:
func main() {
...
...
var wg sync.WaitGroup
wg.Add(len(urls))
for _, url := range(urls) {
go func() {
defer wg.Done()
subscribe(url)
}()
}
wg.Wait()
}
My code reads input from terminal and send those value to nats while it needs to have an http endpoint.
Separately it works but when I combine all of them it does not read from nats. If you could point me to a right direction I would appreciate.
package main
import (
"bufio"
"fmt"
nats "github.com/nats-io/nats.go"
"html/template"
"log"
"net/http"
"os"
)
func main() {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
tmpl := template.Must(template.ParseFiles(wd + "/template/main.html"))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := TodoPageData{
PageTitle: "Demo",
}
tmpl.Execute(w, data)
})
http.ListenAndServe(":8081", nil)
type message struct {
content string
}
var messages []message
nc, err := nats.Connect(
nats.DefaultURL,
)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Subscribe
if _, err := nc.Subscribe("updates", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
}); err != nil {
log.Fatal(err)
}
// io r/w
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if err := nc.Publish("updates", []byte(scanner.Text())); err != nil {
log.Fatal(err)
}
messages = append(messages, message{scanner.Text()})
for _, message := range messages {
fmt.Println(message.content)
}
}
if scanner.Err() != nil {
// handle error.
}
}
http.ListenAndServe is a blocking call. Start it on a new goroutine:
go http.ListenAndServe(":8081", nil)
I was performing some RPC tests, and stumbled across a problem I can't seem to solve. In my testing I create three separate RPC servers, all of which I try to close and shutdown. However upon performing my last test (TestRpcCodecServerClientComm), it seems my client connection is connecting to the first RPC server I started (I know this because I at some point attached IDs to the RPCHandlers), even though I attempted everything I could to make sure it was shutdown. Though the code is not there I have attempted to inspect every single error I could, but that did not bring about anything.
rpc.go
package rbot
import (
"io"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func RpcCodecClientWithPort(port string) (rpc.ClientCodec, error) {
conn, err := net.Dial("tcp", "localhost:"+port)
if err != nil {
return nil, err
}
return jsonrpc.NewClientCodec(conn), nil
}
func RpcCodecServer(conn io.ReadWriteCloser) rpc.ServerCodec {
return jsonrpc.NewServerCodec(conn)
}
rpc_test.go
package rbot
import (
"errors"
"fmt"
"net"
"net/rpc"
"testing"
)
type RPCHandler struct {
RPCServer net.Listener
conn rpc.ServerCodec
done chan bool
TestPort string
stop bool
GotRPC bool
}
func (r *RPCHandler) SetupTest() {
r.stop = false
r.GotRPC = false
r.done = make(chan bool)
r.TestPort = "5556"
}
// TODO: Create separate function to handle erroring
func (r *RPCHandler) CreateRPCServer() error {
rpc.RegisterName("TestMaster", TestAPI{r})
var err error
r.RPCServer, err = net.Listen("tcp", ":"+r.TestPort)
if err != nil {
return err
}
go func() {
for {
conn, err := r.RPCServer.Accept()
if err != nil || r.stop {
r.done <- true
return
}
r.conn = RpcCodecServer(conn)
rpc.ServeCodec(r.conn)
}
}()
return nil
}
func (r *RPCHandler) CloseRPCServer() error {
r.stop = true
if r.conn != nil {
err := r.conn.Close()
if err != nil {
fmt.Println(err)
}
}
err := r.RPCServer.Close()
<-r.done
return err
}
type TestAPI struct {
t *RPCHandler
}
func (tapi TestAPI) Send(msg string, result *string) error {
if msg == "Got RPC?" {
tapi.t.GotRPC = true
return nil
}
return errors.New("Didn't receive right message")
}
// Check if we can create and close an RPC server successfully using the RPC server codec.
func TestRpcCodecServer(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
err := r.CreateRPCServer()
if err != nil {
t.Fatalf("Could not create rpc server! %s:", err.Error())
}
err = r.CloseRPCServer()
if err != nil {
t.Fatalf("Could not close RPC server! %s:", err.Error())
}
}
// Check if we can create a client without erroring.
func TestRpcCodecClientWithPortt(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
r.CreateRPCServer()
defer r.CloseRPCServer()
RPCClient, err := RpcCodecClientWithPort(r.TestPort)
defer RPCClient.Close()
if err != nil {
t.Fatalf("Could not create an RPC client! %s:", err.Error())
}
}
// Let's double check and make sure our server and client can speak to each other
func TestRpcCodecServerClientComm(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
r.CreateRPCServer()
defer r.CloseRPCServer()
RPCCodec, _ := RpcCodecClientWithPort(r.TestPort)
RPCClient := rpc.NewClientWithCodec(RPCCodec)
defer RPCClient.Close()
var result string
err := RPCClient.Call("TestMaster.Send", "Got RPC?", &result)
if err != nil {
t.Fatalf("Error while trying to send RPC message: %s", err.Error())
}
if !r.GotRPC {
t.Fatalf("Could not send correct message over RPC")
}
}
Not sure if I'm just mishandling the connection or something of the like, any help would be much appreciated.
For the Record The RPC api does receive the correct string message
While not the source of your problems, your test configuration has a few race conditions which you should take care of before they cause problems. Always check for issues with the -race option. You should also let the OS allocate the port so you don't run into conflicts. See for example how httptest.Server works.
Your failure here is that you're not creating a new rpc.Server for each test, you're reusing the rpc.DefaultServer. The first call to CreateRPCServer registers a TestAPI under the name TestMaster. Each subsequent call uses the already registered instance.
If you create a new rpc.Server each time you setup the test and register a new TestAPI, the final test will pass.
srv := rpc.NewServer()
srv.RegisterName("TestMaster", testAPI)
...
// and then use srv to handle the new connection
srv.ServeCodec(RpcCodecServer(conn))
I'm trying to create a client and a server using Go but for some reason the server reports the connection as "closed". As the code is trivial I can't think of anything wrong with my code. Any help is appreciated.
package main
import (
log "github.com/golang/glog"
"net/http"
"golang.org/x/net/websocket"
"time"
"flag"
)
type server struct {
payload chan string
}
// srv pushes the messages received via ws into srv.payload
func (srv *server) serve(ws *websocket.Conn) {
go func() {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Exit(err)
}
srv.payload <- msg
}()
return
}
// This example demonstrates a trivial client/ server.
func main() {
flag.Parse()
srv := server{payload: make(chan string, 10)}
http.Handle("/echo", websocket.Handler(srv.serve))
go func() {
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Errorf("ListenAndServe: " + err.Error())
}
}()
// give the server some time to start listening
time.Sleep(3 *time.Second)
//dial and test the response.
ws, err := websocket.Dial("ws://localhost:12345/echo", "", "http://localhost/?x=45")
if err != nil {
log.Exit(err)
}
ms := "test"
if err := websocket.Message.Send(ws, ms); err != nil {
log.Exit(err)
}
msg := <-srv.payload
if msg != ms{
log.Errorf("msg %v is not %v", ms)
}
}
Error
t.go:21] read tcp 127.0.0.1:12345->127.0.0.1:43135:
Edit:
After some try and error I've found that if I remove the go routine from the serve method it works but it doesn't make sense to me. Any idea why it doesn't work when websocket.Message.Receive is in a separate go routine?
package main
import (
log "github.com/golang/glog"
"net/http"
"golang.org/x/net/websocket"
"time"
"flag"
)
type server struct {
payload chan string
}
// srv pushes the messages received via ws into srv.payload
func (srv *server) serve(ws *websocket.Conn) {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Exit(err)
}
srv.payload <- msg
return
}
// This example demonstrates a trivial client/ server.
func main() {
flag.Parse()
srv := server{payload: make(chan string, 10)}
go func() {
http.Handle("/echo", websocket.Handler(srv.serve))
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Errorf("ListenAndServe: " + err.Error())
}
}()
// give the server some time to start listening
time.Sleep(3 *time.Second)
//dial and test the response.
ws, err := websocket.Dial("ws://localhost:12345/echo", "", "http://localhost/?x=45")
if err != nil {
log.Exit(err)
}
ms := "test"
if err := websocket.Message.Send(ws, ms); err != nil {
log.Exit(err)
}
msg := <-srv.payload
if msg != ms{
log.Errorf("msg %v is not %v", ms)
}
}
The websocket server closes the connection when the handler returns.
Removing the Go routine is the correct fix.