SpringCloud通过Sidecar整合异构平台微服务(Python Flask)

本文详细介绍如何使用SpringCloud的Sidecar组件整合Python Flask微服务,实现跨语言平台的微服务架构。通过Sidecar,SpringCloud应用能像调用Java微服务一样调用Python服务。
部署运行你感兴趣的模型镜像

SpringCloud通过Sidecar整合异构平台微服务

背景介绍

Spring Cloud微服务架构体系中,可以通过Sidecar(边车)来实现整合Python、Go等其他语言平台的微服务。其基本原理是,通过Sidecar将第三方平台程序api注册到 Eureka等服务注册中心。这样就可以实现将第三方服务接口当Java接口一样调用,即:Spring Cloud内的程序调用Sidecar,Sidecar再将请求转发给第三方服务。

Sidecar的主要特点

  • 与应用部署在同一台机器上,你的第三方平台或应用在哪里启动,Sidecar就部署在哪里
  • 功能与应用独立,即Sidecar不使用应用程序的运行环境与语言
  • 进程间通信,应用与Sidecar之间一般采用http接口传送json格式数据,以保证其跨语言的通用性

本文主要验证基于Spring Cloud的Sidecar组件实现整合Python Flask微服务到Spring Cloud微服务架构中。Flask api将通过Sidecar被注册到Eureka服务注册中心,并允许其他微服务访问调用。

实验过程

本文基于 Spring Cloud Hoxton.SR3 版本。

程序包含四个部分:eureka服务端、Flask微服务提供方、Sidecar服务代理方、Consumer服务消费方。首先,Flask微服务启动并正常运行,通过其/health接口,可返回其健康状况;然后,Sidecar服务为Flask微服务提供代理并注册到eureka服务端;最后,Consumer服务调用 Sidecar代理的Flask微服务。

搭建 Eureka服务端

首先,通过 Spring Initializr 快速搭建 Eureka服务端(具体过程略),搭建完毕后,工程如下图示:
在这里插入图片描述
其中,Eureka服务端启动程序 EurekaServerApplication

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }

    @EnableWebSecurity
    static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable(); // 配置屏蔽csrf过滤机制
        }
    }
}

需要注意的是:在新版本的security中,添加了csrf过滤。eureka开启安全策略后,可导致微服务的注册被过滤掉!因此,需要在Eureka服务端的代码中增加配置,将csrf过滤机制屏蔽掉

pom文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>eureka_server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka_server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- SpringCloud Hystrix 微服务容错监控组件:断路器,依赖隔离,服务降级,服务监控 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!-- 权限控制依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Eureka服务端的应用配置文件application.yml,已包含在文件截图中。其中,spring.security.user的相关配置,用于Eureka登录验证用户名密码,以提高其安全性。

搭建 Flask 微服务提供方

Flask是Python语言中的一个轻量级Web服务框架,我们用它来模拟基于Python的微服务。
以下是已创建完毕的Flask主应用app.py代码:

import json

from flask import Flask, Response, make_response, jsonify

app = Flask(__name__)


@app.route('/health')
def health():
    result = {'status': 'UP'}
    return Response(json.dumps(result), mimetype='application/json')


@app.route('/getUser')
def get_user():
    return Response(json.dumps({'username': 'python', 'password': 'python'}),
                    mimetype='application/json')


@app.errorhandler(404)
def not_found(error):
    return make_response(jsonify({'error': 'Not found'}), 404)


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

注意:如果通过 Pycharm编写 Flask应用代码,在运行时需要通过 Terminal 窗口单独执行。若使用Pycharm的Flask模板自动创建 Flask应用,执行时需要配置命令行参数:
在这里插入图片描述
在 Additional options处,添加:--host=0.0.0.0 --port=5000,这是因为,使用Pycharm的Flask模板自动创建的Flask应用,在Pycharm中点运行时,会自动忽略app.py代码中写入的 host 和 port端口设置,你需要在 Run Configurations中重新设置(如上图红框所示)。如果不这样,Flask应用相当于处于本机调试模式,只允许localhost访问到,外部IP是无法访问的。这将导致后续的Consumer服务端无法访问Sidecar代理的这个Flask微服务1

Flask微服务启动成功后,在浏览器访问http://localhost:5000/health,将得到{"status": "UP"},证明服务是正常的。

搭建 Sidecar服务代理方

Sidecar的程序比较简单,主要功能通过修改配置文件实现。主应用:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.sidecar.EnableSidecar;

@EnableSidecar
@SpringBootApplication
public class SidecarApplication {

    public static void main(String[] args) {
        SpringApplication.run(SidecarApplication.class, args);
    }
}

其中,@EnableSidecar注解即启用Sidecar。

配置文件application.yml

server:
  port: 8326

spring:
  application:
    name: sidecar-server
  profiles:
    active: "dev"
  cloud:
    loadbalancer:
      ribbon:
        enabled: false
    client:
      ipAddress: localhost

sidecar:
  port: 5000
  health-uri: http://localhost:${sidecar.port}/health

eureka:
  client:
    service-url:
      default-zone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ipAddress}:${spring.application.name}:${server.port}
    status-page-url: http://${spring.cloud.client.ipAddress}:${server.port}/

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000

ribbon:
  ConnectTimeout: 5000
  ReadTimeout: 5000

其中,sidecar.port端口,即被代理的Flask应用所占用的端口;health-uri即Flask应用提供的/health健康检查接口地址。

搭建 Consumer服务消费方

Consumer-server的主应用:

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableFeignClients
@SpringCloudApplication
public class ConsumerServerApplication {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(ConsumerServerApplication.class, args);
    }
}

其中,@LoadBananced,即启用负载均衡器Ribbon;若注释掉,则可使用 http实际地址访问 Flask服务API,以获取返回结果。
定义 Controller 服务接口

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 UserController {

    @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://sidecar-server/getUser", String.class).getBody();
    }
}

此处暴露两个url:java-user,用于返回Java服务端的用户信息;python-user,用于返回Python服务端的用户信息。关键在于http://sidecar-server/getUser,其中的sidecar-server即注册到Eureka上的 Sidecar服务名称。
Consumer服务方应用的配置文件:

spring:
  application:
    name: consumer-server
  cloud:
    client:
      ipAddress: localhost

server:
  port: 8325

eureka:
  client:
    healthcheck:
      enabled: true
    register-with-eureka: true
    fetch-registry: true
    service-url:
      default-zone: http://${registry.host:localhost}:${registry.port:8761}/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ipAddress}:${spring.application.name}:${server.port}
    status-page-url: http://${spring.cloud.client.ipAddress}:${server.port}/

registry:
  host: localhost
  port: 8761

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>consumer-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>consumer-server</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--Hystrix 依赖 主要是用  @HystrixCommand-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 健康检查 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

依次启动服务

按照 Flask -> Eureka -> Sidecar -> Consumer的顺序,依次启动微服务。启动完毕后,登录 Eureka可以看到两个已成功注册的微服务:
在这里插入图片描述
此时,访问Consumer服务地址,请求 /java-user/python-user,返回正常:
在这里插入图片描述
在这里插入图片描述

后记

通过使用 Sidecar,为后续搭建混合Java和 Python开发的微服务平台提供了可能。相对于直接在Python世界中查找或重构 Eureka注册组件,这种方式可以更好地满足核心应用扩展、实现业务功能内聚、降低代码复杂度。

参考文档

本文在实验过程中,主要参考了如下文章,基于最新的Spring Cloud微服务版本,完成了编码和测试。

  1. SpringCloud 整合 Python - Flask
  2. SpringCloud 整合Python
  3. SideCar模式

  1. 这种情况可能还与本地计算机的网卡数、网络设置有关。如果Sidecar注册到Eureka的IP地址仍然是localhost或127.0.0.1,则这种情况下的微服务调用可能仍会是正常的。 ↩︎

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pierre_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值