parent
5272a4d821
commit
c91dca764f
2 changed files with 76 additions and 13 deletions
|
@ -3,3 +3,29 @@
|
||||||
Run the following command to restore dependencies to `vendor/` directory:
|
Run the following command to restore dependencies to `vendor/` directory:
|
||||||
|
|
||||||
dep ensure --vendor-only
|
dep ensure --vendor-only
|
||||||
|
|
||||||
|
## Dynamic catalog reloading / artificial delay
|
||||||
|
|
||||||
|
This service has a "dynamic catalog reloading" feature that is purposefully
|
||||||
|
not well implemented. The goal of this feature is to allow you to modify the
|
||||||
|
`products.json` file and have the changes be picked up without having to
|
||||||
|
restart the service.
|
||||||
|
|
||||||
|
However, this feature is bugged: the catalog is actually reloaded on each
|
||||||
|
request, introducing a noticeable delay in the frontend. This delay will also
|
||||||
|
show up in profiling tools: the `parseCatalog` function will take more than 80%
|
||||||
|
of the CPU time.
|
||||||
|
|
||||||
|
You can trigger this feature (and the delay) by sending a `USR1` signal and
|
||||||
|
remove it (if needed) by sending a `USR2` signal:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Trigger bug
|
||||||
|
kubectl exec \
|
||||||
|
$(kubectl get pods -l app=productcatalogservice -o jsonpath='{.items[0].metadata.name}') \
|
||||||
|
-c server -- kill -USR1 1
|
||||||
|
# Remove bug
|
||||||
|
kubectl exec \
|
||||||
|
$(kubectl get pods -l app=productcatalogservice -o jsonpath='{.items[0].metadata.name}') \
|
||||||
|
-c server -- kill -USR2 1
|
||||||
|
```
|
||||||
|
|
|
@ -22,7 +22,10 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto"
|
pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto"
|
||||||
|
@ -41,18 +44,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
catalogJSON []byte
|
cat pb.ListProductsResponse
|
||||||
log *logrus.Logger
|
catalogMutex *sync.Mutex
|
||||||
|
log *logrus.Logger
|
||||||
|
|
||||||
port = flag.Int("port", 3550, "port to listen at")
|
port = flag.Int("port", 3550, "port to listen at")
|
||||||
|
|
||||||
|
reloadCatalog bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
c, err := ioutil.ReadFile("products.json")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to open product catalog json file: %v", err)
|
|
||||||
}
|
|
||||||
catalogJSON = c
|
|
||||||
log = logrus.New()
|
log = logrus.New()
|
||||||
log.Formatter = &logrus.JSONFormatter{
|
log.Formatter = &logrus.JSONFormatter{
|
||||||
FieldMap: logrus.FieldMap{
|
FieldMap: logrus.FieldMap{
|
||||||
|
@ -63,7 +64,11 @@ func init() {
|
||||||
TimestampFormat: time.RFC3339Nano,
|
TimestampFormat: time.RFC3339Nano,
|
||||||
}
|
}
|
||||||
log.Out = os.Stdout
|
log.Out = os.Stdout
|
||||||
log.Info("successfully parsed product catalog json")
|
catalogMutex = &sync.Mutex{}
|
||||||
|
err := readCatalogFile(&cat)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("could not parse product catalog")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -71,6 +76,22 @@ func main() {
|
||||||
go initProfiling("productcatalogservice", "1.0.0")
|
go initProfiling("productcatalogservice", "1.0.0")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGUSR1, syscall.SIGUSR2)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
sig := <-sigs
|
||||||
|
log.Printf("Received signal: %s", sig)
|
||||||
|
if sig == syscall.SIGUSR1 {
|
||||||
|
reloadCatalog = true
|
||||||
|
log.Infof("Enable catalog reloading")
|
||||||
|
} else {
|
||||||
|
reloadCatalog = false
|
||||||
|
log.Infof("Disable catalog reloading")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
log.Infof("starting grpc server at :%d", *port)
|
log.Infof("starting grpc server at :%d", *port)
|
||||||
run(*port)
|
run(*port)
|
||||||
select {}
|
select {}
|
||||||
|
@ -146,12 +167,28 @@ func initProfiling(service, version string) {
|
||||||
|
|
||||||
type productCatalog struct{}
|
type productCatalog struct{}
|
||||||
|
|
||||||
func parseCatalog() []*pb.Product {
|
func readCatalogFile(catalog *pb.ListProductsResponse) error {
|
||||||
var cat pb.ListProductsResponse
|
catalogMutex.Lock()
|
||||||
|
defer catalogMutex.Unlock()
|
||||||
if err := jsonpb.Unmarshal(bytes.NewReader(catalogJSON), &cat); err != nil {
|
catalogJSON, err := ioutil.ReadFile("products.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to open product catalog json file: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := jsonpb.Unmarshal(bytes.NewReader(catalogJSON), catalog); err != nil {
|
||||||
log.Warnf("failed to parse the catalog JSON: %v", err)
|
log.Warnf("failed to parse the catalog JSON: %v", err)
|
||||||
return nil
|
return err
|
||||||
|
}
|
||||||
|
log.Info("successfully parsed product catalog json")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCatalog() []*pb.Product {
|
||||||
|
if reloadCatalog || len(cat.Products) == 0 {
|
||||||
|
err := readCatalogFile(&cat)
|
||||||
|
if err != nil {
|
||||||
|
return []*pb.Product{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cat.Products
|
return cat.Products
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue