多语言客户端的实现
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集群进行注册,启动后会看到集群中已经有对应的注册信息:
在程序停止时,会自动解除注册
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注册信息:
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的注册信息:
在程序退出时需要执行以下解注册方法,否则只有在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
调用go
(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
调用go
(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
调用java
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
通过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";
}
可以看到调用没有问题:
上面的demo,已经上传到github上:https://github.com/huangtao1/multi-consul-clients