最近有一个使用 SpringCloud 的微服务项目,需要使用到 Flask 提供一些深度学习的模型接口。Java那边使用的 Nacos 作为注册中心,所以也需要把 Flask 的服务注册上去。
本文会谈到手写实现Flask服务注册到Nacos与基于nacos-sdk-python注册,以及SpringBoot调用Flask以及SpringGateway网关请求转发调用Flask。
- 请求地址:
http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=服务名&ip=服务ip地址&port=服务端口- 请求方式:POST
逻辑很简单,直接给出实现
import requests
from flask import Flask
app = Flask(__name__)
# 服务注册
def service_register():
url = "http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=pythonservice&ip=127.0.0.1&port=5000"
res = requests.post(url)
print("完成注册")
if __name__ == '__main__':
service_register()
app.run()
登录到Nacos的控制页面,可以看到确实已经注册上去了。
但当我们再隔一段时间后(准确来说是15秒),发现已经没有了健康实例。
这得从Nacos对服务实例的健康检查说起。我们刚才注册上去的服务是临时实例。
对于临时实例
Nacos服务端在15秒内如果没收到客户端的心跳请求,会将该实例设置为不健康,在30秒内没收到心跳,会将这个临时实例摘除。
对于非临时实例
Nacos会主动询问,也就是不再需要客户端主动完成心跳检测。
为了保证我们的服务注册上去以后,保持健康状态,即让Nacos服务端知道我们的服务并没有问题。因此需要定期(一般是5秒)向Nacos服务端发起心跳检测的请求。
- 请求地址:
http://127.0.0.1:8848/nacos/v1/ns/instance/beat?serviceName=服务名&ip=服务ip地址&port=服务端口- 请求方式:PUT
import requests
from flask import Flask
import time
# 用于异步处理心跳检测
import threading
app = Flask(__name__)
# 服务注册
def service_register():
url = "http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=pythonservice&ip=127.0.0.1&port=5000"
res = requests.post(url)
print("完成注册")
# 心跳检测
def service_beat():
while True:
url = "http://127.0.0.1:8848/nacos/v1/ns/instance/beat?serviceName=pythonservice&ip=127.0.0.1&port=5000"
res = requests.put(url)
print(f"心跳检测中... 响应状态码: {res.status_code}")
time.sleep(5)
if __name__ == '__main__':
service_register()
# 5 秒后执行心跳检测
threading.Timer(5, service_beat()).start()
app.run()
Nacos官方文档
pip install nacos-sdk-python
import nacos
import threading
from flask import Flask
app = Flask(__name__)
# nacos 服务地址
SERVER_ADDRESSES = "http://127.0.0.1:8848"
# 官方文档中更加详细的描述了此方法的形参,包括命名空间、sk、ak等
# 在此处,我只需要传 服务地址 即可
client = nacos.NacosClient(SERVER_ADDRESSES)
def service_register():
"""
ephemeral参数:是否是临时服务,应为false;
刚才上面也提到了,如果是 非临时实例,客户端就无需主动完成心跳检测。
因此此处将服务注册为 非临时实例
"""
client.add_naming_instance("pythonservice", "127.0.0.1", "5000", ephemeral=False)
if __name__ == '__main__':
threading.Timer(5, service_register).start()
app.run()
我先给出Java部分的代码结构,在此处,我们只会使用到 java-demo 模块,在下面一节,就会使用到 gateway 模块。
在此处,我们简单的套上了一个 Flask 接口。
@app.route("/py", methods=['get'])
def py1():
print("hello py")
return "hello, python service"
server:
port: 8081
spring:
application:
name: javaservice
cloud:
nacos:
server-addr: localhost:8848
@SpringBootApplication
@RestController
@RequestMapping("/java")
public class JavaDemoApplication {
@Autowired
private RestTemplate restTemplate;
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(JavaDemoApplication.class, args);
}
@GetMapping
public String javaService(){
System.out.println("java service start...");
String url = "http://pythonservice/py";
// 发送http请求,实现远程调用
String ps = restTemplate.getForObject(url, String.class);
System.out.println("Python service 返回内容: 【 " + ps + "】");
return "Java远程调用Python: " + ps;
}
}
回到我最开始的初衷,我们是想提供深度学习模型的接口,因此还是希望前端直接调用Flask的接口而不是先调Java,Java再调Flask。
而微服务中一般会使用SpringGateway来做请求转发,因此,我们就来做SpringGateway转发到我们的Flask接口。
server:
port: 10010
spring:
application:
name: gateway
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: javaservice
uri: lb://javaservice
predicates:
- Path=/java/**
- id: pythonservice
uri: lb://pythonservice
predicates:
- Path=/py/**
这里,我们将SpringBoot服务与Flask服务均做了请求转发配置