说到Python
这门语言,应该都会很熟悉,近几年来Python
大火,几乎都到了人人喊着:“人生苦短,我学Python
”的境地,确实Python
在机器学习方面有着得天独厚的优势,在Python
语言中拥有很多现成的机器学习函数库,然后在Web
开发中还是有着很多人使用Java
作为服务器的后台语言,尤其是近几年来微服务的兴起,也有着越来越多的人使用SpringCloud
(基于Java
语言实现的微服务框架),因此就产生了这么一个需求:能否将Python
的机器学习算法集成到SpringCloud
中,作为我们Web
系统的服务,当我们自己使用Python实现了一个新的机器学习算法的时候,可以同时为他人提供服务。用户在Web端提供数据,而Web平台提供算法进行计算,同时反馈给用户)。 要想实现上面的需求,其实就是存在着整合SpringCloud
整合Python
的必要性。因此我查了SpringCloud
的官网,还真的提供了整合Python
语言的解决方案(其实是整合第三方语言的解决方案)。这个解决方案叫做:sidecar
。通过使用**sidecar
**我们可以将Python
接口注册为SpringCloud
的一个服务,实现Java
(因为SpringCloud
是Java
语言的一款框架)和Python
的双向通信,即:Python
可以调用Java
语言的接口,同时Java
也可以调用Python
语言的接口。
sidecar
? 我们刚刚说了使用sidecar
可以将SpringCloud
和第三方语言整合,那什么是sidecar
呢?说白了sidecar
就是springcloud
提供的一个工具,使用该工具将第三方的rest
接口集成到springcloud
中来。那么如何使用sidecar
呢?首先我们看一下官网的描述。
Polyglot
支持Sidecar
(官网描述) Spring Cloud Netflix Sidecar
包含一个简单的http api
来获取给定服务的所有实例(即主机和端口)。然后可以通过从Eureka
获取其路由条目的嵌入式Zuul
代理来代理服务调用。可以通过主机查找或通过Zuul
代理访问Spring Cloud Config
服务器。但是第三方程序必须执行健康检查,以便Sidecar
可以向应用程序启动或关闭时向eureka
报告**(意思就是说:第三方程序必须提供一个接口告诉Spring Cloud
自身是否还在运行?)**。如何使用Sidecar
呢?官网给出了如下的步骤:
Sidecar
步骤如果要在项目中包含Sidecar
,需要使用org.springframework.cloud
和artifact id spring-cloud-netflix-sidecar
的依赖。
Sidecar
使用@EnableSidecar
创建Spring Boot
应用程序。此注释包括@EnableCircuitBreaker
,@EnableDiscoveryClient
和@EnableZuulProxy
。
配置Sidecar
,应该将sidecar.port
和sidecar.health-uri
添加到application.yml
。sidecar.port
属性是非jvm
应用程序正在侦听的端口。这样,Sidecar
可以使用Eureka
正确注册该应用。sidecar.health-uri
是可以在非jvm
应用程序上访问的,可以模拟Spring Boot
健康指标。它应该返回一个json
文档,如下所示:
{
"status":"UP"
}
以下是Sidecar
应用程序的application.yml
示例:
server:
port: 5678
spring:
application:
name: sidecar
sidecar:
port: 8000
health-uri: http://localhost:8000/health.json
我们使用Sidecar
将第三方程序接口(比如Python
)注册到SpringCloud
之中,如何使用Python
接口呢?这时候就非常简单了,此时我们就可以将Python
接口当作Java
接口进行调用(其实时通过springcloud
去调用Sidecar
,然后通过Sidecar
去转发我们的程序请求)。
因为非JVM
应用 被注册到SpringCloud
之中,对于第三方应用程序来说,整个SpringCloud
的内容,我们都可以进行调用了,比如我们有一个Java服务叫做customers
,那么我们就可以通过url来调用,比如http://localhost:5678/customers
(假设Sidecar
在端口5678
上),因为配置服务器(configserver
)也属于SpringCloud的一个服务,因此非JVM
语言也可以调用配置服务器的配置,比如使用如下的url
: http:// localhost:5678/configserver
Sidecar
总结 看了官网的描述,似乎我们还是抓不住重点,到底应该如何结合Sidecar
和第三方程序呢?依旧是如此的茫然。下面我着重说一下官网提供的重点:
Sidecar
是一个用于监听非JVM应用程序(可以是Python
或者Node
或者Php
等等)的一个工具,通过Sidecar
可以实现Java
和第三方应用程序的双向交互Sidecar
报告自己的状态,告诉Sidecar
自己还在运行着。Sidecar
应用程序必须和第三方应用程序运行在同一台电脑上,也就是说他们之间是localhost
,不能是ip
访问(官网未提及)Python
和SpringCloud
整合实例 前面说了这么多理论性的东西,似乎我们只知道了一个事情:通过Sidecar
可以实现SpringCloud和第三方应用程序的整合,就比如我们的Python,可以利用Sidecar
将Python
集成到我们的微服务中来,然后我们就可以利用Python
强大的机器学习算法了。
说了这么多,往往不如一个实例来的更加直接,现在我就使用Python
来和SpringCloud
整合。在我的程序结构如下所示:
python
接口中会提供一个服务功能:获取Python
用户名和密码,用户名为:python
,密码为python
。同时python
应该还提供一个健康接口,报告自己的健康状态Java
服务会提供两个接口(JavaUser,PythonUser)
,JavaUser
方法用于获得Java
用户名和密码,用户名为:java
,密码为java
。PythonUser
方法会调用python
服务,获得Python
用户名和密码。 在Python
中,我使用flask
提供Web
服务,代码如下:
import json
from flask import Flask, Response
app = Flask(__name__)
@app.route("/health")
def health():
result = {'status': 'UP'}
return Response(json.dumps(result), mimetype='application/json')
@app.route("/getUser")
def getUser():
result = {'username': 'python', 'password': 'python'}
return Response(json.dumps(result), mimetype='application/json')
app.run(port=3000, host='0.0.0.0')
代码解释:
Python
服务监听3000
接口health
方法用于给Sidecar
提供健康接口,用于实时向Sidecar
提供自己的健康状态。getUser
是Python
向外界提供的服务,提供Python
用户的用户名和密码SpringCloud
注册中心application.properties
配置文件
server.port=8000
spring.application.name=eureka-server
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
main
方法
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
代码解释:
8000
sidecar
main
方法
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.sidecar.EnableSidecar;
@EnableSidecar
@SpringBootApplication
public class PySidecarApplication {
public static void main(String[] args) {
SpringApplication.run(PySidecarApplication.class, args);
}
}
application.properties
配置文件
spring.application.name=py-sidecar
server.port=8001
sidecar.port=3000
sidecar.health-uri=http://localhost:${sidecar.port}/health
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
ribbon.ConnectTimeout=5000
ribbon.ReadTimeout=5000
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
代码解释:
main
方法使用了@EnableSidecar
注解sidecar.port
代表第三方程序运行的端口(比如上方的Python
),所以监听端口为3000server.port
代表sidecar
运行的端口spring.application.name
代表sidecar
的应用名字**(后面要用到)**sidecar.health-uri
是Python
健康接口,应指向python
的健康服务Java服务
package com.example.demo;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class JavaController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/java-user")
public String JavaUser() {
return "{'username': 'java', 'password': 'java'}" ;
}
@RequestMapping("/python-user")
public String PythonUser() {
return restTemplate.getForEntity("http://py-sidecar/getUser", String.class).getBody();
}
}
main
方法
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringCloudApplication
public class RibbonConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
}
application.properties
spring.application.name=java-service
server.port=8002
eureka.client.service-url.defaultZone=http://localhost:8000/eureka/
代码解释:
server.port
代表Java
服务运行的端口spring.application.name
代表Java
服务应用的名字**(后面要用到)**PythonUser
方法中,Java
调用了Python
服务,将Python
用户返回**(注意:在这里Java
语言调用了Python
的接口,实现了Java
和Python
的通信)**JavaUser
方法中,直接返回了Java
用户的信息在服务调用之前,我们先回顾一下代码都做了什么样子的事情:
Python
服务,运行在端口3000
sidecar
,运行在短就8001
,监听Python端口3000
,应用名字为py-sidecar
Java
服务,运行在端口8002
,应用名字为java-service
py-sidecar
,java-service
对于Java原生服务的调用没有什么特别,直接访问URL即可,如:http://localhost:8002/java-user
在java-service
服务的PythonUser
中,Java
调用了Python
的服务,使用restTemplate
模板,通过:http://{service_id}/{method}
实现了对Python
服务的调用**(这里我们调用的时Java
的端口,返回的是Python
的数据内容)**
sidecar
调用Java
服务 我们使用了sidecar
,所以我们可以使用sidecar
实现对Java
服务的调用,比如:http://{host}:{port}/{service_id}/{method}
,其中:
host
是sidecar
的主机地址port
是sidecar
的端口地址service_id
是Java
服务的id
method
是Java
服务的具体方法地址 如果我们想通过sidecar
获得Java
用户的对象,可以这么写:http://localhost:8001/java-service/java-user
Demo
一个Star
,您的支持是我最大的动力)