Showing posts with label multi threading. Show all posts
Showing posts with label multi threading. Show all posts

Go Tutorial Part 4 (Interfaces, Routines, Channels, Concurrency in Go)

GO - Part 4

Credit goes to - Master the fundamentals and advanced features of the Go Programming Language (Golang), on Udemy by Stephen Grider.
I have tried some snippets on my own and some are referring to the snippets developed while going through the course.

Code Repo: https://github.com/namitsharma99/goLangTutorials




Interfaces
- - - - - - - - - 

Need:
Whenever there is a logic change in a function, it has to be re-written.


For example, maintaining 2 chatboxes :
A.
1. type hindiBot struct

2. func (hindiBot) getGreeting() string // return "namaste"

3. func printGreeting(hb hindiBot) // similar code - fmt.Println(hb.getGreeting())

B.
1. type englishBot struct

2. func (englishBot) getGreeting() string // return "hello"

3. func printGreeting(eb englishBot) // similar code - fmt.Println(eb.getGreeting())


Code --

package main

import "fmt"

type hindiBot struct {}
type englishBot struct {}

func main()  {

}

//func (hb hindiBot) getGreeting() string {
func (hindiBot) getGreeting() string {  // since no hb value is read
  return "namaste!!"
}

// func (eb englishBot) getGreeting() string {
func (eb englishBot) getGreeting() string { // since no eb value is read
  return "hello!!"
}

func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

func printGreeting(eb englishBot) {
  fmt.Println(eb.getGreeting())
}

ERROR::

$ go build main.go 
# command-line-arguments
./main.go:26:6: printGreeting redeclared in this block
previous declaration at ./main.go:22:23


FIX 1:

package main

import "fmt"

type hindiBot struct {}
type englishBot struct {}

func main()  {
  hb := hindiBot{}
  eb := englishBot{} // gives error

  printGreeting(hb)
  printGreeting(eb)

}

//func (hb hindiBot) getGreeting() string {
func (hindiBot) getGreeting() string {  // since no hb value is read
  return "namaste!!"
}

// func (eb englishBot) getGreeting() string {
func (eb englishBot) getGreeting() string { // since no eb value is read
  return "hello!!"
}

// ERROR
/* func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

func printGreeting(eb englishBot) {
  fmt.Println(eb.getGreeting())
} */
// printGreeting redeclared in this block

// using single print method
func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

ERROR::
$ go build main.go 
# command-line-arguments
./main.go:13:16: cannot use eb (type englishBot) as type hindiBot in argument to printGreeting


FIX 2: 

-- using interfaces

package main

import "fmt"

// the interface fix
type bot interface {
  getGreeting() string
}


type hindiBot struct {}
type englishBot struct {}

func main()  {
  hb := hindiBot{}
  eb := englishBot{}

  printGreeting(hb)
  printGreeting(eb)

}

//func (hb hindiBot) getGreeting() string {
func (hindiBot) getGreeting() string {  // since no hb value is read
  return "namaste!!"
}

// func (eb englishBot) getGreeting() string {
func (eb englishBot) getGreeting() string { // since no eb value is read
  return "hello!!"
}

// ERROR
/* func printGreeting(hb hindiBot) {
  fmt.Println(hb.getGreeting())
}

func printGreeting(eb englishBot) {
  fmt.Println(eb.getGreeting())
} */
// printGreeting redeclared in this block

func printGreeting(b bot) {
  fmt.Println(b.getGreeting())
}


O/P
- - -

$ ./main 
namaste!!
hello!!


WORKING:

Interface identifies based on the func associated.

type bot interface {
  getGreeting() string
}

This declares that any type, having getGreeting() func and returning the string, automatically becomes a member of this interface

Since hb and eb above, are now the members, they can call the printGreeting() func easily using the bot interface

Note:
------
Argument type can also be put in the clause along with the type check as follows :

type bot interface {
  getGreeting(string, int) string
  // and many other functions
}


- interfaces are not generic types
- interfaces are implicit, matched with signature clauses only
- interfaces just help in maintaining types, no further validation



net/ http 
- - - - - - - - 

package main

import "fmt"
import "net/http"
import "os"

func main() {
  resp, err := http.Get("http://www.google.com")
  if nil != err {
    fmt.Println("Error: ", err)
    os.Exit(1)
  }
  fmt.Println("response: ",resp)
}

O/P
- - -

$ ./main 
response:  &{200 OK 200 HTTP/1.1 1 1 map[Cache-Control:[private, max-age=0] Content-Type:[text/html; charset=ISO-8859-1] Date:[Wed, 20 May 2020 21:41:29 GMT] Expires:[-1] P3p:[CP="This is not a P3P policy! See g.co/p3phelp for more info."] Server:[gws] Set-Cookie:[1P_JAR=2020-05-20-21; expires=Fri, 19-Jun-2020 21:41:29 GMT; path=/; domain=.google.com; Secure NID=204=V-0Lr33CGR1OSs2-eLQShrsFqM23NpD66sBXPp4lJky4VKQ1LSL6Uoz6SpRpUuDB8HToPfN6YFbT_cgGjnO595t07MExURpcb5UeC_juxvM0dMZrQduvG8FUJ5gsIzCNGCWumrxNWyrEhntEvhUBw9rfjkE9w6u7zzoq3_05ltI; expires=Thu, 19-Nov-2020 21:41:29 GMT; path=/; domain=.google.com; HttpOnly] X-Frame-Options:[SAMEORIGIN] X-Xss-Protection:[0]] 0xc00011e0e0 -1 [] false true map[] 0xc0001ee000 <nil>}



Some more info on Interfaces
- - - - - - - - - - - - - - - - - - - - - - - - -

Reader Interface - provides common implementation to read any source as byte slice - []byte, which can work with anything further

package main

import "fmt"
import "net/http"
import "os"

func main() {
  resp, err := http.Get("http://www.google.com")
  if nil != err {
    fmt.Println("Error: ", err)
    os.Exit(1)
  }
  // fmt.Println("response: ",resp)
  bs := make([]byte, 99999)
  resp.Body.Read(bs)
  fmt.Println("byte slice: ", bs)
  fmt.Println("byte slice as string: ", string(bs))

}


O/P
---
< Many byte characters and huge response >

Another option to print response -
...
io.Copy(os.Stdout, resp.Body)
...


Writer Interface - opposite of Reader interface, takes byte slice and converts into desired format

io.Copy(os.Stdout, resp.Body) --> the params are implementation of Writer interface and Reader interface respectively

Copy() internally uses CopyBuffer()


package main

import "fmt"
import "net/http"
import "os"
import "io"

type logWriter struct {}

func main() {
  resp, err := http.Get("http://www.google.com")
  if nil != err {
    fmt.Println("Error: ", err)
    os.Exit(1)
  }
  // fmt.Println("response: ",resp)

  // bs := make([]byte, 99999)
  // resp.Body.Read(bs)
  // fmt.Println("byte slice: ", bs)
  // fmt.Println("byte slice as string: ", string(bs))

  // io.Copy(os.Stdout, resp.Body)

  lw := logWriter{}
  io.Copy(lw, resp.Body)

}

func (logWriter) Write(bs []byte) (int, error) {
  // return 1, nil
  fmt.Println(string(bs))
  fmt.Println("length ---- ", len(bs))
  return len(bs), nil
}

o/p
- - - 

< response body >



Channels and Go Routines
- - - - - - - - - - - - - - - - - - - - - - -

Example Code:

main.go
- - - - 
package main

import "fmt"
import "net/http"

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  for _, link := range links {
    checkLink(link)
  }

}

func checkLink(link string) {
  _, err := http.Get(link)
  if nil != err {
    fmt.Println(link, " is down!!")
    return
  }
  fmt.Println(link, " is up!!")
}

O/P
- - -

$ ./main 
http://www.google.com  is up!!
http://www.golang.org  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!



Note: As clear from the response, this is sequential and involves waiting. Now, need to see how to make the calls in parallel - Go Routines.

Go Routine lets the Main Routines knows, that there is a blocking function and Main should continue.

Syntax: just need to add go prefix against the function calls
go checkLink(link)

Go Scheduler takes care of the monitoring. Be default, it uses only one CPU core, but it can be customized to distribute the load.

Concurrency/ Parallelism can be achieved once multiple cores are involved.

Main routine doesn't give any priority to the child routines hence they are not executed. Therefore, need to use Channels.
It acts as a mediator to communicate between the Main routine and the child routines.
Channels are strictly defined per types, string channel can't support the int type.


How to send data with Channels

channel <- 9         : send the value 9 into channel
n <- channel         : waiting for channel to get the value and assigning it further to the variable n

fmt.Println(<- channel) : waiting for channel to get the value and hence printing it afterwards


Code:

main.go
- - - - - -
package main

import "fmt"
import "net/http"

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  c := make(chan string) // need to pass this channel in function call

  for _, link := range links {
    go checkLink(link, c)
  }
  fmt.Println(<- c)
}

func checkLink(link string, c chan string) {
  _, err := http.Get(link)
  if nil != err {
    fmt.Println(link, " is down!!")
    c <- "down !!"
    return
  }
  fmt.Println(link, " is up!!")
  c <- "up!!"
}


O/P
- - -
$ ./main 
http://www.google.com  is up!!
up!!


One issue - only one url is checked...

Because, Main Routine proceeds as soon as one of the child routines comes back.
Message receiving is also a blocking code.

Making the channel receiver again in Main routine -

...
  fmt.Println(<- c)
  fmt.Println(<- c)
...
O/P
- - -
$ ./main 
http://www.google.com  is up!!
up!!
http://www.stackoverflow.com  is up!!
up!!

All results can be seen if same number of receivers are created, but hangs if any additional is there !!

Quick hack -
...
  for i :=0; i< len(links); i++ {
    fmt.Println(<- c)
  }
...



How to repeat the Routines?
- - - - - - - - - - - - - - - - - - - - - - - 

Passing links instead of values, and handling at the receiver ends

package main

import "fmt"
import "net/http"

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  c := make(chan string) // need to pass this channel in function call

  for _, link := range links {
    go checkLink(link, c)
  }
  // fmt.Println(<- c)
  // fmt.Println(<- c)

  // for i :=0; i< len(links); i++ {
  //   fmt.Println(<- c)
  // }

  for {
    go checkLink(<- c, c)
  }

}

func checkLink(link string, c chan string) {
  _, err := http.Get(link)
  if nil != err {
    fmt.Println(link, " is down!!")
    // c <- "down !!"
    c <- link
    return
  }
  fmt.Println(link, " is up!!")
  // c <- "up!!"
  c <- link
}


O/P
- - - 
< Going on and on... forever>



Alternatively, making it more readable --
...
  for l := range c {
    go checkLink(l, c)
  }
...



How to add a pause in between the checks?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

Use Time package, sleep function
Avoid adding pauses in the main routines, it's a bad way - blocking and queuing 

Use function literals, i.e. unnamed functions

func main() {
  links := []string {
    "http://www.google.com",
    "http://www.golang.org",
    "http://www.stackoverflow.com",
    "http://www.amazon.com",
  }

  c := make(chan string) // need to pass this channel in function call

  for _, link := range links {
    go checkLink(link, c)
  }

  // Alternative syntax
  for l := range c {
    // time.Sleep(5*time.Second) // 5 seconds, bad way - blocking and queuing
    // go checkLink(l, c)
    go func() {  // function literals, like unnamed and anonymous functions
        time.Sleep(5*time.Second)
        go checkLink(l, c)
      }  ()
  }

}

Issue: l is not a trustworthy variable to monitor for the Main routine, as it's value might have changed.
Output can be like first run happening fine for all URLs but after that stuck with one url's copy. (Go being pass by copy)

Fix, pass l from the range -
...
  for l := range c {
    go func(link string) {  
        time.Sleep(5*time.Second)
        go checkLink(link, c)
      }  (l)
  }
...

O/P AS EXPECTED -
- - - - - - - - - 

$ ./main 
http://www.google.com  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!
http://www.golang.org  is up!!
http://www.google.com  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!
http://www.golang.org  is up!!
http://www.google.com  is up!!
http://www.stackoverflow.com  is up!!
http://www.amazon.com  is up!!
^C

Multi-person chat application : Java Socket Programming and Multi-threading


Hello Buddies.


Core java has 2 great things that are generally ignored these days in day to day coding - Multi-threading and socket programming, by the coders like us.
Since these 2 are not encountered by most of us in projects, let's prepare a small app that can give us the hands on experience on the usage.

The concept:

2 persons or players 
1 server
Chat app that serves the clients in a round robin fashion


1. Let's begin with the server first.

To implement the server in the easiest manner possible I would be using java.net.ServerSocket
Something like this -

static ServerSocket ss = null;
...
      ss = new ServerSocket(6666);
...

Socket s = ss.accept();    
...
new DataInputStream(s.getInputStream());

So the plan is simple, create a socket, dedicate a port, read the input stream striking at the port.


2. Now the next step is to have a client prototype.

It would be using the same socket's port to create an output stream of data. Something like this -
...
Socket s = new Socket("localhost", 6666);
...
DataOutputStream dout = new DataOutputStream(s.getOutputStream());
...
dout.writeUTF(playerInput);


So far, looks good.

Create a main() for server and one for the player. Run both, and you would see the message reaching the server.


Now, what if we are having multiple main() methods invoked for various players.

Do you think the app will run smoothly?

Well it won't!


Every main() has its own thread. As soon as that thread invokes socket connection via server socket's accept(), it acquires the lock over it, leading to other threads starving i.e. non availability of the resource.

So what can be done here?

As a good programmer, you can handle this scenario by identifying the risky code blocks and making them thread safe.

Use synchronized blocks, and run the threads for the server and all the players or clients interacting in a parallel fashion.

Have a look here .

I have put a sample project in this GIT repo which shows the generalized way of using multi-threading and socket programming, and avoids thread starvation.


Following is an extract from the readMe file of the same -


# simpleChatApplicationJava

Here's a brief console flow to understand how the chat application code works. Please note S, P1 and P2 denote the respective console screens for the server (or a common chat viewer), person1 and person2.



S:
<<<<<<<<<<<<<<< SERVER >>>>>>>>>>>>>>



P1:
<<<<<<<<<<<<<<< Player 1 >>>>>>>>>>>>>>

Hey player 1 Please enter your message for server - 



P2:
<<<<<<<<<<<<<<< Player 2 >>>>>>>>>>>>>>

Hey player 2 Please enter your message for server - 



S:
<<<<<<<<<<<<<<< SERVER >>>>>>>>>>>>>>

Player accepted!

Player accepted!



P1:
<<<<<<<<<<<<<<< Player 1 >>>>>>>>>>>>>>

Hey player 1 Please enter your message for server - 

Player1 here

Hey player 1 Please enter your message for server - 



S:
<<<<<<<<<<<<<<< SERVER >>>>>>>>>>>>>>

Player accepted!

Player accepted!

Player Input: Player1 here



P2:
<<<<<<<<<<<<<<< Player 2 >>>>>>>>>>>>>>

Hey player 2 Please enter your message for server - 

Player2 here

Hey player 2 Please enter your message for server - 



S:
<<<<<<<<<<<<<<< SERVER >>>>>>>>>>>>>>

Player accepted!

Player accepted!

Player Input: Player1 here

Player Input: Player2 here



P1:
<<<<<<<<<<<<<<< Player 1 >>>>>>>>>>>>>>

Hey player 1 Please enter your message for server - 

Player1 here

Hey player 1 Please enter your message for server - 

q

Player 1 no longer available!




S:
<<<<<<<<<<<<<<< SERVER >>>>>>>>>>>>>>

Player accepted!

Player accepted!

Player Input: Player1 here

Player Input: Player2 here

Player Input: q

ERROR: No input from player!




P2:
<<<<<<<<<<<<<<< Player 2 >>>>>>>>>>>>>>

Hey player 2 Please enter your message for server - 

Player2 here

Hey player 2 Please enter your message for server - 

q

Player 2 no longer available!




S:
<<<<<<<<<<<<<<< SERVER >>>>>>>>>>>>>>

Player accepted!

Player accepted!

Player Input: Player1 here

Player Input: Player2 here

Player Input: q

ERROR: No input from player!

Player Input: q

ERROR: No input from player!



When a person gives 'q' or 'Q' in the input, the connection breaks off.

You can use this basic functionality to incorporate more persons in the chat room or to create a backend for GUI based chat application.



Why don't you try playing around with this some more, and do let me know the challenges and experiences encountered!!

Happy coding!

Featured post

Oracle SQL Scheduled Jobs - An Interesting Approach

  Oracle SQL Scheduled Jobs A DB Scheduler is the best way to automate any backend database job. For instance, if you want to process the p...