Golang微服务(二)

Posted on 2023-02-26 03:41  呱呱呱呱叽里呱啦  阅读(48)  评论(0编辑  收藏  举报

Golang微服务(二)

注册中心

选型

注册中心要综合考虑:

  • 功能是否完善
  • 是否易用
  • 是否可配置
  • 是否支持健康检查
  • 是否支持多数据中心
  • 是否支持服务发现

基于上述考虑,consul是不错的选择

consul环境

文档:https://developer.hashicorp.com/consul/docs

使用docker准备上手consul的环境

docker run -d -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8600:8600/udp consul consul agent -dev -client=0.0.0.0
# docker容器自启动
docker container update --restart=always 

Web界面运行在8500端口

consul容器8600端口可以做DNS服务

dig @192.168.xxx.xxx -p 8600 consul.service.consul SRV

consul常用API

  • 服务的增删查、健康检查

    package main
    
    import (
    	"fmt"
    
    	"github.com/hashicorp/consul/api"
    )
    
    func GetClient() (*api.Client, error) {
    	cfg := api.DefaultConfig()
    	cfg.Address = "192.168.xxx.xxx:8500"
    	client, err := api.NewClient(cfg)
    	if err != nil {
    		fmt.Println(err)
    		return nil, err
    	}
    	return client, nil
    }
    
    // Register 注册服务
    func Register(name string, address string, port int, tags []string, id string) {
    	client, err := GetClient()
    	if err != nil {
    		panic(err)
    	}
    	registration := &api.AgentServiceRegistration{
    		Name:    name,
    		Address: address,
    		Port:    port,
    		Tags:    tags,
    		ID:      id,
    		// 健康检查配置
    		Check: &api.AgentServiceCheck{
    			HTTP:                           "http://192.168.xxx.xxx:xxxx/h",
    			Timeout:                        "5s",
    			Interval:                       "10s",
    			DeregisterCriticalServiceAfter: "20s", // 注销服务
    		},
    	}
    	err = client.Agent().ServiceRegister(registration)
    	if err != nil {
    		panic(err)
    	}
    }
    
    // GetAllServices 查询所有服务
    func GetAllServices() {
    	client, err := GetClient()
    	if err != nil {
    		panic(err)
    	}
    	services, err := client.Agent().Services()
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    	for name, service := range services {
    		fmt.Println(name, service.ID)
    	}
    }
    
    // FilterServices 查询并过滤服务
    func FilterServices(filter string) {
    	client, err := GetClient()
    	if err != nil {
    		panic(err)
    	}
    	services, err := client.Agent().ServicesWithFilter(filter) // 查询语句使用services response中的字段==具体值
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    	for name, service := range services {
    		fmt.Println(name, service.ID)
    	}
    }
    
    // Deregister 注销服务
    func Deregister(serviceID string) {
    	client, err := GetClient()
    	if err != nil {
    		panic(err)
    	}
    	err = client.Agent().ServiceDeregister(serviceID)
    	if err != nil {
    		panic(err)
    	}
    }
    func main() {
    	Register("test", "192.168.xxx.xxx", xxx, []string{"web"}, "test")
    	GetAllServices()
    	FilterServices(`Service=="test"`)
    	Deregister("test")
    }
    
  • gRPC的健康检查

    import (
    	"google.golang.org/grpc/health"
    	"google.golang.org/grpc/health/grpc_health_v1"
    )
    
    server := grpc.NewServer()
    grpc_health_v1.RegisterHealthServer(server, health.NewServer()) // 注册健康检查server
    
    // 注册时的check实例也需要修改
    Check: &api.AgentServiceCheck{
    			GRPC:                           "192.168.xxx.xxx:xxxx",
    			Timeout:                        "5s",
    			Interval:                       "10s",
    			DeregisterCriticalServiceAfter: "20s", // 注销服务
    		},
    
  • 服务的负载均衡(相同服务多实例注册)

    做负载均衡的层有很多,从最初接触请求的nginx到api网关到web层都可以做负载均衡,方式也有包括集中式、进程内、独立进程等多种方式,gRPC使用的是进程内负载均衡的方式(也支持三方负载均衡),这种方式缺点是需要多语言单独实现,但是相对于集中式的超大流量和独立进程的额外维护等缺点,还是可以接受的。

    负载均衡的策略也有多种。包括轮询法、随机法、源地址哈希法、加权轮询法、加权随机法、最小连接法等。

    使用三方库grpc-consul-resolver完成负载均衡

    import _ "github.com/mbobakov/grpc-consul-resolver"
    func main() {
        conn, err := grpc.Dial(
            "consul://127.0.0.1:8500/whoami?wait=14s&tag=manual",
            grpc.WithInsecure(),
            grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
        )
        if err != nil {
            log.Fatal(err)
        }
        defer conn.Close()
        ...
    }
    

配置中心

常见配置中心要求:实时推送、权限、集群、配置回滚、环境隔离

选型:nacos

nacos环境

# docker搭建用于上手的nacos环境
docker run --name nacos-standalone -e MODE=standalone -e JVM_XMS=512m -e JVM_XMX=512m -e JVM_XMN=256m -p 8848:8848 -d nacos/nacos-server:latest
# 访问http://192.168.112.131:8848/nacos/index.html进入web管理界面

nacos常用功能或概念

命名空间,可以为配置集提供隔离性,一般用来区分微服务

组,一般用来区分开发、测试、生产环境

Data ID 配置集标识,一些需要聚合的配置组成的集合,可以参考一个配置文件

clientConfig := constant.ClientConfig{
		NamespaceId:         "e525eafa-f7d7-4029-83d9-008937f9d468", //we can create multiple clients with different namespaceId to support multiple namespace.When namespace is public, fill in the blank string here.
		TimeoutMs:           5000,
		NotLoadCacheAtStart: true,
		LogDir:              "/tmp/nacos/log",
		CacheDir:            "/tmp/nacos/cache",
		LogLevel:            "debug",
	}
serverConfigs := []constant.ServerConfig{
		{
			IpAddr:      "console1.nacos.io",
			ContextPath: "/nacos",
			Port:        80,
			Scheme:      "http",
		}
	}
_, _ := clients.CreateConfigClient(map[string]interface{}{
	"serverConfigs": serverConfigs,
	"clientConfig":  clientConfig,
})
content, err := configClient.GetConfig(vo.ConfigParam{
    DataId: "dataId",
    Group:  "group"}