I built a simple web app with a VueJS front (client) and a Go back (server), using gRPC and protobuf. In order to communicate, an envoy proxy has to be set up between them to convert web client HTTP/1.1 to HTTP/2. I have no issue deploying this app on Linux, and have dockerized all three services.
However, I cannot manage to get my services to communicate on MacOS.
The app repository is available here: https://github.com/42Projects/nPuzzle
The deploy folder contains the docker-compose file, the envoy.yaml file required for the envoy container, as well as all three dockerfiles.
On MacOS the last line of the envoy.yaml needs to be updated, localhost
has to be changed to host.docker.internal
.
The client contains a ping function (in client/src/App.vue), the window.location.hostname might need to be changed to the docker-machine IP, unsure about that, my tries made no difference:
created () {
this.client = new NpuzzleClient('http://' + window.location.hostname + ':8080', null, null);
const ping = () => {
let message = new Message();
message.setMessage('ping');
this.client.greets(message, {}, err => this.serverOnline = !err);
};
ping();
window.setInterval(ping, 1000);
},
which is executed every second, and will display that the server is online on the top right corner. I have been so far unsuccessful in reaching my server while deploying on MacOS. It works perfectly fine on Linux.
Here is the code for the go server, I think it is correct to listen on localhost:9090 as the server will be dockerized, but I might be wrong and the address might need to be changed.
package main
import (
"context"
"flag"
"fmt"
pb "github.com/42Projects/nPuzzle/proto"
npuzzle "github.com/42Projects/nPuzzle/src"
"google.golang.org/grpc"
"log"
"net"
"time"
)
var port = flag.Int("port", 9090, "the server port")
type server struct{}
func (s *server) Greets(ctx context.Context, message *pb.Message) (*pb.Message, error) {
return &pb.Message{Message: "pong!"}, nil
}
func (s *server) Parse(ctx context.Context, message *pb.Message) (*pb.Matrix, error) {
log.Printf("received parsing request: %#v", message.Message)
m, err := npuzzle.ParseMatrix(message.Message)
if err != nil {
log.Println(err)
return &pb.Matrix{
Success: false,
Error: err.Error(),
}, nil
}
rows := make([]*pb.Matrix_Row, len(m))
for index, row := range m {
/* We need unsigned 32bits integer for protobuf */
uIntRow := make([]uint32, len(m))
for rowIndex, num := range row {
uIntRow[rowIndex] = uint32(num)
}
rows[index] = &pb.Matrix_Row{Num: uIntRow}
}
return &pb.Matrix{
Success: true,
Rows: rows,
}, nil
}
func (s *server) Solve(ctx context.Context, problem *pb.Problem) (*pb.Result, error) {
/* Choose heuristic function */
var heuristic npuzzle.Heuristic
switch problem.Heuristic {
case "hamming":
heuristic = npuzzle.HammingDistance
case "manhattan":
heuristic = npuzzle.ManhattanDistance
case "manhattan + linear conflicts":
heuristic = npuzzle.ManhattanPlusLinearConflicts
}
/* Choose between greedy search and uniform-cost search */
var goal npuzzle.Goal
var search npuzzle.Search
switch problem.Search {
case "greedy":
goal = npuzzle.GreedyGoalReached
search = npuzzle.GreedySearch
case "uniform-cost":
goal = npuzzle.UniformCostGoalReached
search = npuzzle.UniformCostSearch
}
/* Convert protobuf unsigned 32bits integer to regular integer */
size := len(problem.Rows)
m := make(npuzzle.Matrix, size)
for y, row := range problem.Rows {
m[y] = make([]int, size)
for x, num := range row.Num {
m[y][x] = int(num)
}
}
log.Printf("received problem:
- heuristic: %v
- search: %v
- matrix: %v
", problem.Heuristic, problem.Search, m)
if npuzzle.IsSolvable(m) == false {
log.Println("failed to solve problem: unsolvable")
return &pb.Result{
Success: false,
Error: "unsolvable",
}, nil
}
begin := time.Now()
log.Printf("starting solve on %v...", m)
res, totalNumberOfStates, maxNumberOfStates, err := m.Solve(heuristic, search, goal, 30*time.Second)
if err != nil {
log.Printf("timed ouf after %v", 30*time.Second)
return &pb.Result{
Success: false,
Error: fmt.Sprintf("timed ouf after %v", 30*time.Second),
}, nil
}
duration := time.Since(begin)
log.Printf("solved %v in %v seconds", m, duration)
var path string
if res.Parent == nil {
path = "already solved!"
} else {
path = npuzzle.StringifyPath(res)
}
return &pb.Result{
Success: true,
Time: duration.String(),
Moves: int32(res.Cost),
TotalStates: int32(totalNumberOfStates),
MaxStates: int32(maxNumberOfStates),
Path: path,
}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v
", err)
}
s := grpc.NewServer()
pb.RegisterNpuzzleServer(s, &server{})
log.Printf("starting server on port %v
", *port)
log.Fatalf("failed to serve: %v
", s.Serve(lis))
}
I tried to launch the client and the server locally, and only containerize the envoy proxy, while trying different addresses (my docker machine IP address and localhost), but it didn't work. I also tried to launch all three containers but no result here either. I'm unsure what to change to successfully manage to make my app work properly on MacOS.