Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running with and without the embedded NATS Server #3

Closed
gedw99 opened this issue Dec 12, 2022 · 17 comments
Closed

Running with and without the embedded NATS Server #3

gedw99 opened this issue Dec 12, 2022 · 17 comments

Comments

@gedw99
Copy link
Contributor

gedw99 commented Dec 12, 2022

How would you like to facilitate the option of running with and without the embedded NATS Server ?

This is simple commented out code to allow to use an external. Seems to work in testing.


func Create(ah *app.Handler) {
	var srv AppServer

	/*
			opts := &server.Options{
				ServerName:     "Your friendly backend",
				Host:           "127.0.0.1",
				Port:           8501,
				NoLog:          true,
				NoSigs:         true,
				MaxControlLine: 4096,
				Accounts: []*server.Account{
					{
						Name: "cluster",
					},
				},
				Websocket: server.WebsocketOpts{
					Host:             "127.0.0.1",
					Port:             8502,
					NoTLS:            true,
					SameOrigin:       false,
					Compression:      false,
					HandshakeTimeout: 5 * time.Second,
				},
				DisableShortFirstPing: true,
			}
			// Initialize new server with options
			ns, err := server.NewServer(opts)
			if err != nil {
				panic(err)
			}

			// Start the server via goroutine
			go ns.Start()

			// Wait for server to be ready for connections
			if !ns.ReadyForConnections(2 * time.Second) {
				panic("could not start the server (is another instance running on the same port?)")
			}

			fmt.Printf("Nats AppServer Name: %q\n", ns.Name())
			fmt.Printf("Nats AppServer Addr: %q\n", ns.Addr())



		// Connect to server
		nc, err := nats.Connect(ns.ClientURL())
		if err != nil {
			panic(err)
		}

	*/

	// ws://localhost:8080
	nc, err := nats.Connect("ws://localhost:8502")
	if err != nil {
		panic(err)
	}

	// This is the chat broker
	_, err = nc.Subscribe("chat.say", func(msg *nats.Msg) {
		fmt.Printf("Got: %q\n", msg.Data)
		err = nc.Publish("chat.room", msg.Data)
		if err != nil {
			panic(err)
		}
	})
	if err != nil {
		panic(err)
	}

	_, err = nc.Subscribe("govatar.female", func(msg *nats.Msg) {
		if err != nil {
			panic(err)
		}
		var img image.Image
		// always female and random
		img, err = govatar.Generate(govatar.FEMALE)
		if err != nil {
			panic(err)
		}
		var buf bytes.Buffer
		err = jpeg.Encode(&buf, img, &jpeg.Options{Quality: 80})
		if err != nil {
			panic(err)
		}
		err = msg.Respond([]byte("data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(buf.Bytes())))
		if err != nil {
			log.Printf("Respond error: %s", err)
		}
		//noErr(err)
	})
	if err != nil {
		panic(err)
	}

	r := chi.NewRouter()
	srv.mux = r

	r.Use(middleware.CleanPath)
	r.Use(middleware.Logger)
	compressor := middleware.NewCompressor(flate.DefaultCompression,
		"application/wasm", "text/css", "image/svg+xml" /*, "application/javascript"*/)
	r.Use(compressor.Handler)
	r.Use(middleware.Recoverer)

	listener, err := net.Listen("tcp", "127.0.0.1:8500")
	if err != nil {
		panic(err)
	}

	mainServer := &http.Server{
		Addr: listener.Addr().String(),
	}

	// try to build the final browser location
	var hostURL = url.URL{
		Scheme: "http",
		Host:   mainServer.Addr,
	}

	log.Printf("Serving at %s\n", hostURL.String())

	// This is the frontend handler (go-app) and will "pick up" any route
	// which is not handled by the backend. It then will load the frontend
	// and navigate it to this router.
	srv.mux.Handle("/*", ah)

	// registering our stack as the handler for the http server
	mainServer.Handler = srv.Mux()

	// Creating some graceful shutdown system
	shutdown := make(chan struct{})
	go func() {
		listener = netutil.LimitListener(listener, 100)
		defer func() { _ = listener.Close() }()

		err := mainServer.Serve(listener)
		if err != nil {
			if err == http.ErrServerClosed {
				fmt.Println("AppServer was stopped!")
			} else {
				log.Fatal(err)
			}
		}
		// when run by "mage watch/run" it will break mage
		// before actually exiting the server, which just looks
		// strange but is okay after all
		//time.Sleep(2 * time.Second) // just for testing
		close(shutdown)
	}()

	// Setting up signal capturing
	stop := make(chan os.Signal, 1)
	signal.Notify(stop, os.Interrupt)
	// Waiting for SIGINT (kill -SIGINT <pid> or Ctrl+c )
	<-stop
	shutdownServer(mainServer, shutdown)
	fmt.Println("Program ended")
}
@oderwat
Copy link
Owner

oderwat commented Dec 12, 2022

We do not have any NATS services in the backend part of the PWA in that case. There is just "nothing NATS" then. There is no need for a backend (so far).

What is more interesting (and I have some examples of that too) is, that you use the internal NATS server but also connect it as a leaf node to the cluster. This way you can add some extra services for the PWA which is separated from the cluster but still use the cluster for everything else. We plan to use something like that for a desktop version of the PWA. Our framework let us run the PWA as a Web view on Windows and on OSX, so that the backend is also installed at the client computer. But we are still prototyping and trying to find the best solution for production. It's just that we prototype in multiple directions right now.

P.S.: I think that the code @mlctrez wrote (using your own socket proxy on the same port) is an excellent variant when you really run the "backend nats-server". I also may create my example with the option on doing that. For what we do, it is not relevant, though.

@mlctrez
Copy link

mlctrez commented Dec 12, 2022

Thanks @oderwat ..

I've published v1 of goapp-natsws for when you get around to updating your example. I'll be updating my spin-off example to use it shortly. The spin-off example has been updated.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 12, 2022

@oderwat

we are thinking along the same lines.

the Leaf node concept is exactly what I was going to try next.
Btw I also just said the same thing on @mlctrez repo also.

My project is for scientists and on desktop and / or Lab server they would be doing just like what you said - running with Embedded Leaf node.

In my use case a public server is envisaged to allow the general public to make calls into the Lab server ( no public IP address ). This is legally required because the Lab Server holds patient data - data must stay on site.

So I might have to run sish / ngrok style Setup.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 12, 2022

Anyone know if a Leaf Node NATS Server on a private network can be reachable from a Public nats server ? If yes then I could use this approach to reach Leaf Nats Server running in the Lab.

@oderwat
Copy link
Owner

oderwat commented Dec 12, 2022

@gedw99 the leaf server connects to the cluster on a special port. So, you need to be able to connect to the internet from inside the lab. You do not need a connection onto the lab at all.

@oderwat
Copy link
Owner

oderwat commented Dec 12, 2022

I do not know if you saw it, but my example lets you connect to the internal nats server (using nats cli tool) and send a request there using the random PWA name (the echo subject). In that case, the PWA is kind of what runs in the lab (as it can't be connected otherwise. It runs in the sandbox of the browser). Still, you can send your data there and let it answer. I find that kind of fascinating, you can "talk with the app in the browser of a visitor of the website", just by sending a message to their service, even from a complete different system (maybe super-clusters and/or cascaded leaf nodes away).

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 14, 2022

@oderwat thanks for the tips.

Yes the NATS Cli trick works for me well. It is fascinating. NATS is really amazing...

I describe my topology here: https://github.com/gedw99/sc-gio#network-topology

Next steps to prove the network architecture is viable:

  1. Put up Public NATS Server on fly.io under a https domain.

  2. Then NATS CLI can then be set with that Public NATS Server context, and send a message to it, which should flow to the NATS LEAF Server, and up into the go-app GUI in the browser.

NATS CLI (laptop ) --> NATS Public Server ( fly ) --> NATS Leaf Server ( laptop) --> Go-app GUI ( browser on laptop ).

  1. Then the opposite flow of sending a message from Browser to Public NATS:

Go-app GUI ( browser on laptop ) --> NATS Leaf server ( laptop) --> NATS Public server (fly) --> CLI ( laptop )

Hopefully it works :)

@oderwat
Copy link
Owner

oderwat commented Dec 14, 2022

Put up Public NATS Server on fly.io under a https domain.

  1. Nats does not run under HTTPS. It uses "nats" protocol which is crude TCP (similar to SMTP or POP3). And that gets TLS on top. The only "web" like way to run the nats server is running it and exposing the websocket.
  2. You should also add some access control. User/Pass in URL will be OK. But probably you did not mean "public" after all :)

I am uncertain if fly.io is okay for the DSVGO, though. They have European regions but depending on the data you need this agreement about data processing (I believe). We run a 3 node cluster for NATS on dedicated servers, and use that as well for replicas of the data (kv / jetstream). I would be interested in the network through output and core utilization for fly.io.

For prototyping, I would get a real root server (8 cores 16+ GB RAM, and it can be used for the production later too) or do everything local in docker or with another server in the local net. Doing it in docker does not "show the real thing" because the network will be much faster than in a real setup.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 14, 2022

About points 1 and 2. Yes i know what you mean.. I will see if i can gen the TLS certs using mkcert.

Good point about DSVGO. !!! I want the scientists to put up the Public NATS Server themselves. It's their system. I suggested Fly because its very easy compared to many others.
Fly throughput, will be sucky, but quite good enough for their needs in general i hope.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 14, 2022

Just realised how hardcoded the IP:PORTS are. Would you be open to having ENV driven IP and PORT ?

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 14, 2022

ah. also just realised i need to modify the config for NATS in order for the embedded NATS server to act as a leaf node ..

example:
https://github.com/ConnectEverything/nats-by-example/blob/main/examples/topologies/simple-leafnode/cli/main.sh

What if modify and PR the code in "back.go" to allow configuring itself as a NATS Leaf Server and / or NATS Server ?
The intent is to allow both scenarios depending on needs.

@oderwat
Copy link
Owner

oderwat commented Dec 14, 2022

As this code does not use any of our real framework code, I do not want to modify anything here. Read the disclaimer, I was not joking there ;-). Actually, I also just accidentally posted this under my private account instead of the one from the company, and now it is a bit too late to change that :)

I do appreciate your input and like to share my thoughts and watch your enthusiasm :)

We do have PWAs prototypes with leaf code and stuff. This here is just a "copy & paste" I did in a "free" hour, grabbing from some code I wrote for internal projects. I posted this to the NATS Slack / devs after they took my PR to make it possible to use the nats-server web socket from inside of WASM. They were curious for what I need it.

@oderwat
Copy link
Owner

oderwat commented Dec 14, 2022

I want the scientists to put up the Public NATS Server themselves.

If paying for a service is an option, they may go with NGS and use a European region instead of running your own little one node server.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 15, 2022

I want the scientists to put up the Public NATS Server themselves.

If paying for a service is an option, they may go with NGS and use a European region instead of running your own little one node server.

yep that's another option - good option actually.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 15, 2022

As this code does not use any of our real framework code, I do not want to modify anything here. Read the disclaimer, I was not joking there ;-). Actually, I also just accidentally posted this under my private account instead of the one from the company, and now it is a bit too late to change that :)

I do appreciate your input and like to share my thoughts and watch your enthusiasm :)

We do have PWAs prototypes with leaf code and stuff. This here is just a "copy & paste" I did in a "free" hour, grabbing from some code I wrote for internal projects. I posted this to the NATS Slack / devs after they took my PR to make it possible to use the nats-server web socket from inside of WASM. They were curious for what I need it.

ok i can see what your mean. I will work up a repo. I appreciate the advice and this repo for helping me.

@gedw99
Copy link
Contributor Author

gedw99 commented Dec 15, 2022

If you know where the PR is that enabled WASM for NATs, let me know. Would be interesting to see the changes that made it possible just out of curiosiity.

@oderwat
Copy link
Owner

oderwat commented Dec 15, 2022

I needed I way to suppress that the NATS Go client does the TLS handshake when the server requests TLS because the handshake was already done through the custom dialer (because it uses wss).

nats-io/nats.go#1146

@oderwat oderwat closed this as completed Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants