使用consul作为python,go,java项目的注册中心

多语言客户端的实现

1.JAVA

创建spring-cloud项目,pom中配置如下插件:


    org.springframework.cloud
    spring-cloud-starter-consul-discovery

在配置文件中加入下面的配置:

cloud:
  consul:
    discovery:
      #健康检查间隔时间
      healthCheckInterval: 3s
      #健康检查地址
      healthCheckPath: /actuator/health
      #本机地址
      hostname: 192.168.9.52
      #注册instanceid
      instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
      register: true
      #注册服务名
      serviceName: ${spring.application.name}
      tags: url-prefix-/${spring.application.name}
    enabled: true
    #consul客户端地址与端口
    host: 192.168.8.227
    port: 8500

 

这样此项目就可以向consul集群进行注册,启动后会看到集群中已经有对应的注册信息:

使用consul作为python,go,java项目的注册中心_第1张图片

在程序停止时,会自动解除注册

2.python

python客户端使用fastapi框架。需要使用到python-consul包

app = FastAPI()
service_name = "consul-py"
service_id = service_name + '-' + str(uuid.uuid1())
# 实例consul类
c = consul.Consul(host="192.168.9.50", port=8500)
# 向server注册
service = c.agent.service
service.register(name=service_name, address="192.168.9.52", port=8000, service_id=service_id,
                 check=consul.Check().tcp(host="192.168.9.52", port=8000, interval="3s", deregister="1m"))

在程序退出时需要执行以下解注册方法,否则只有在1分钟后服务才会解注册

# 关闭前自动解注册
@app.on_event("shutdown")
async def shutdown_event():
    service.deregister(service_id)
    logger.info("解注册成功!!")

启动后可以在ui中看到service注册信息:

使用consul作为python,go,java项目的注册中心_第2张图片

3.go

go客户端使用gin框架。需要使用github.com/hashicorp/consul/api包

func initConsul() *api.Client {
   config := api.DefaultConfig()
   //定义一个consul client地址
   config.Address = "192.168.9.50:8500"
   client, err := api.NewClient(config)
   if err != nil {
      log.Fatal("connect consul failed:" + err.Error())
   }
   return client
}


//注册consul
func registerConsul(client *api.Client) {
   serviceId = "consul-go" + "-" + uuid.NewV4().String()
   register := api.AgentServiceRegistration{}
   register.Name = "consul-go"
   register.Port = 8777
   register.Address = "192.168.9.52"
   register.ID = serviceId
   //定义心跳检查
   check := api.AgentServiceCheck{}
   check.TCP = fmt.Sprintf("%s:%d", register.Address, register.Port)
   check.Interval = "3s"
   check.DeregisterCriticalServiceAfter = "1m"
   register.Check = &check
   //向consul注册
   err := client.Agent().ServiceRegister(®ister)
   if err != nil {
      log.Fatal("register consul failed:", err.Error())
   }
}

启动后可以在ui中看到service的注册信息:

使用consul作为python,go,java项目的注册中心_第3张图片

在程序退出时需要执行以下解注册方法,否则只有在1分钟后服务才会解注册

//启动channel监听程序退出
//监听退出操作
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
<-quit
log.Println("Shutdown Server ...")
//consul deregister
err := client.Agent().ServiceDeregister(serviceId)
if err != nil {
   log.Fatal("Deregister failed:", err)
}
log.Println("解除注册成功!!!!")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
   log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")

4.多语言客户端之间的调用

(1)java

直接使用feign调用即可

@FeignClient(value = "consul-py", fallback = FeignFallBack.class)
public interface ConsulJavaService {
    @RequestMapping("/python_user")
    String helloPy();
}

@FeignClient(value = "consul-go", fallback = FeignFallBack.class)
public interface ConsulGoService {
    @RequestMapping("/go_user")
    String helloGo();
}

调用python

使用consul作为python,go,java项目的注册中心_第4张图片

调用go

使用consul作为python,go,java项目的注册中心_第5张图片

(2)python

直接使用consul接口获取所有service下的instance,然后随机取出一个地址来进行访问

def get_other_instance_result(service_name, prefix_url):
    # 调用consul api
    consul_service = c.catalog.service(service_name)
    consul_service_instances = []
    # 获取实例的真实ip
    if len(consul_service[1]) > 0:
        instance_address_infos = consul_service[1]
        for instance_address_info in instance_address_infos:
            consul_service_instances.append(
                "http://" + instance_address_info["ServiceTaggedAddresses"]["lan_ipv4"]["Address"] + ":" +
                str(instance_address_info["ServiceTaggedAddresses"]["lan_ipv4"]["Port"]) + "/")
    else:
        return "no instance"
    # 随机返回一个可用实例
    instance_url = choice(consul_service_instances)
    logger.info(instance_url)
    try:
        # 发起请求
        result = requests.get(instance_url + prefix_url).text
    except Exception as e:
        result = "ERROR:" + str(e)
    return result

# 调用go
@app.get("/hello_go")
async def hello_go():
    result = get_other_instance_result("consul-go", "go_user")
    if result == "no instance" or result.startswith("ERROR:"):
        return {"message": "调用失败:%s" % result}
    else:
        return {"message": result}

# 调用java
@app.get("/hello_java")
async def hello_java():
    result = get_other_instance_result("consul-java", "java_user")
    if result == "no instance" or result.startswith("ERROR:"):
        return {"message": "调用失败:%s" % result}
    else:
        return {"message": result}

调用java

使用consul作为python,go,java项目的注册中心_第6张图片

调用go

使用consul作为python,go,java项目的注册中心_第7张图片

(3)go

使用consul api来获取所有service下的instance,然后随机取出一个地址来进行访问

//调用其他服务
func connectService(serviceName, requestUrl string, client *api.Client) (result string) {
   queryOption := api.QueryOptions{}
   services, _, err := client.Catalog().Service(serviceName, "", &queryOption)
   if err != nil {
      return "ERROR:" + err.Error()
   }
   //随机获取一个实例
   rand.Seed(time.Now().UnixNano())
   index := rand.Intn(len(services))
   serviceInstance := services[index]
   serviceUrl := fmt.Sprintf("http://%s:%d/%s", serviceInstance.ServiceAddress, serviceInstance.ServicePort, requestUrl)
   log.Println(serviceUrl)
   //发起请求
   result = Get(serviceUrl)
   return result
}
//调用python
router.GET("/hello_py", func(context *gin.Context) {
   s := connectService("consul-py", "python_user", client)
   context.String(http.StatusOK, s)
})
//调用java
router.GET("/hello_java", func(context *gin.Context) {
   s := connectService("consul-java", "java_user", client)
   context.String(http.StatusOK, s)
})

 

调用python

使用consul作为python,go,java项目的注册中心_第8张图片

调用java

使用consul作为python,go,java项目的注册中心_第9张图片

5.使用spring-cloud-gateway作为统一网关

可以通过网关访问到所有注册到consul上的服务,这样后台就有了统一的入口,前端请求的时候只需要配置网关地址即可。

新建一个spring-cloud项目,配置gateway与consul依赖


   org.springframework.cloud
   spring-cloud-starter-consul-discovery


   org.springframework.cloud
   spring-cloud-starter-gateway

spring-gateway做转发需要在配置文件中进行路由的配置:

gateway:
  discovery:
    locator:
      enabled: true
  routes:
    -   filters:
          - StripPrefix=1
        id: java
        predicates:
          - Path=/java/**
        uri: lb://consul-java
    -   filters:
          - StripPrefix=1
        id: go
        predicates:
          - Path=/go/**
        uri: lb://consul-go
    -   filters:
          - StripPrefix=1
        id: python
        predicates:
          - Path=/python/**
        uri: lb://consul-py

注册到consul

使用consul作为python,go,java项目的注册中心_第10张图片

通过gateway调用其他服务的接口:

调用python服务python_user接口:

在python服务中接口定义如下:

@app.get("/python_user")
async def hello():
    return {"message": "this is python"}

可以看到调用没有问题:

调用go服务的go_user接口:

在go服务中接口定义如下:

router.GET("/go_user", func(context *gin.Context) {
   context.JSON(http.StatusOK,gin.H{"message":"this is go"})
})

可以看到调用没有问题:

调用java服务的java_user接口:

在java服务中接口定义如下:

@RequestMapping("/java_user")
public String javaUser() {
    return "this is java";
}

可以看到调用没有问题:

使用consul作为python,go,java项目的注册中心_第11张图片

 

上面的demo,已经上传到github上:https://github.com/huangtao1/multi-consul-clients

你可能感兴趣的:(devops)