func (s *DefaultSubscriber) Connect() error {
	consumer, err := nsqlib.NewConsumer(s.topic, s.channel, s.cfg)
	if err != nil {
		return err
	}
	consumer.SetLogger(&logBridge{}, nsqlib.LogLevelInfo)
	for _, handler := range s.handlers {
		consumer.AddHandler(handler)
	}
	s.consumer = consumer
	go s.configLoop()
	return nil
}
Example #2
0
// NewServiceLoader returns a loader that reads config from config service
func NewServiceLoader(c *Config, addr, service, region, env string) (*Loader, error) {
	// define our hierarchy:
	// H2:BASE
	// H2:BASE:<service-name>
	// H2:REGION:<aws region>
	// H2:REGION:<aws region>:<service-name>
	// H2:ENV:<env>
	// H2:ENV:<env>:<service-name>

	hierarchy := []string{
		"H2:BASE",
		fmt.Sprintf("H2:BASE:%s", service),
		fmt.Sprintf("H2:REGION:%s", region),
		fmt.Sprintf("H2:REGION:%s:%s", region, service),
		fmt.Sprintf("H2:ENV:%s", env),
		fmt.Sprintf("H2:ENV:%s:%s", env, service),
	}

	// construct URL
	if !strings.Contains(addr, "://") {
		addr = "https://" + addr
	}
	addr = strings.TrimRight(addr, "/") + "/compile"
	u, err := url.Parse(addr)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse config service address: %v", err)
	}
	q := u.Query()
	q.Set("ids", strings.Join(hierarchy, ","))
	u.RawQuery = q.Encode()

	configUrl := u.String()

	log.Infof("[Config] Initialising service loader for service '%s' in region '%s' in '%s' environment via URL %s", service, region, env, configUrl)

	rdr := func() (io.ReadCloser, error) {
		rsp, err := http.Get(configUrl)
		if err != nil {
			log.Errorf("[Config] Failed to load config via %s: %v", configUrl, err)
			return nil, fmt.Errorf("Failed to load config via %s: %v", configUrl, err)
		}
		defer rsp.Body.Close()
		if rsp.StatusCode != 200 {
			log.Errorf("[Config] Failed to load config via %s - status code %v", configUrl, rsp.StatusCode)
			return nil, fmt.Errorf("Failed to load config via %s - status code %v", configUrl, rsp.StatusCode)
		}
		b, _ := ioutil.ReadAll(rsp.Body)

		loaded := make(map[string]interface{})
		err = json.Unmarshal(b, &loaded)
		if err != nil {
			log.Errorf("[Config] Unable to unmarshal loaded config: %v", err)
			return nil, fmt.Errorf("Unable to unmarshal loaded config: %v", err)
		}

		b, err = json.Marshal(loaded["config"])
		if err != nil {
			log.Errorf("[Config] Unable to unmarshal loaded config: %v", err)
			return nil, fmt.Errorf("Unable to unmarshal loaded config: %v", err)
		}
		rdr := ioutil.NopCloser(bytes.NewReader(b))
		return rdr, nil
	}

	changesChan := make(chan bool)
	l := NewLoader(c, changesChan, rdr)

	go func() {
		// wait until loaded
		l.reload()

		// look out for config changes PUBbed via NSQ -- subscribe via a random ephemeral channel
		channel := fmt.Sprintf("g%v#ephemeral", rand.Uint32())
		consumer, err := nsqlib.NewConsumer("config.reload", channel, nsqlib.NewConfig())
		if err != nil {
			log.Warnf("[Config] Failed to create NSQ reader to pickup config changes (fast reload disabled): ch=%v %v", channel, err)
			return
		}
		consumer.AddHandler(nsqlib.HandlerFunc(func(m *nsqlib.Message) error {
			changesChan <- true
			return nil
		}))

		// now configure it -- NOT via lookupd! There is a bug we think!
		subHosts := AtPath("hailo", "service", "nsq", "subHosts").AsHostnameArray(4150)
		if len(subHosts) == 0 {
			log.Warnf("[Config] No subHosts defined for config.reload topic (fast reload disabled)")
			return
		}

		log.Infof("[Config Load] Subscribing to config.reload (for fast config reloads) via NSQ hosts: %v", subHosts)
		if err := consumer.ConnectToNSQDs(subHosts); err != nil {
			log.Warnf("[Config Load] Failed to connect to NSQ for config changes (fast reload disabled): %v", err)
			return
		}

		// Wait for the Loader to be killed, and then stop the NSQ reader
		l.Wait()
		consumer.Stop()
	}()

	return l, nil
}