在 https://start.spring.io/ 勾选Eureka Server
Application加上EnableEurekaServer:
@SpringBootApplication @EnableEurekaServer public class CloudApplication { public static void main(String[] args) { SpringApplication.run(CloudApplication.class, args); } }
application.properties配置文件:
server.port=8761 spring.application.name=eureka-server eureka.instance.hostname=localhost eureka.client.registerWithEureka=false eureka.client.fetchRegistry=false eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
完整的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.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>cloud</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cloud</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
起来后查看http://localhost:8761
使用Django生成一个HelloWorld的web项目。
django-admin startproject HelloWorld
创建完成后可以看到下面的目录树:
$ cd HelloWorld/ $ tree . |-- HelloWorld | |-- __init__.py | |-- settings.py | |-- urls.py | `-- wsgi.py `-- manage.py
说明:
接下来就可以启动了:
python3 manage.py runserver 0.0.0.0:8000
urls.py
"""HelloWorld URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path from django.conf.urls import url from . import view urlpatterns = [ url(r'^health$', view.health), url(r'^instance_info$', view.instance_info), url(r'^query/(.+)/info$', view.query_server_info), ]
view.py
from django.http import HttpResponse,JsonResponse import urllib import json # 健康检查API,用于Sidecar检查本Python服务的存活状态 def health(request): result = { "status":"UP" } print(result) return JsonResponse(result) # 对外服务API,用于测试其他服务调用本Python服务即查询当前实例的信息 def instance_info(request): result = { "status":0, "msg":"sucess", "data":{ "instance_name":"python_server" } } print(result) return JsonResponse(result) # 查看指定服务的实例信息API,用于测试本Python服务调用其他已注册到Eureka上是服务 def query_server_info(request,server_name): print(server_name) sidecar_service_url = "http://localhost:6666/{instance_name}/instance_info".replace('{instance_name}',server_name);#sidecar获取服务信息API(实际Sidecar会将此请求转发到Sidecar代理的Python服务上) print("request_url: "+sidecar_service_url) req = urllib.request.urlopen(sidecar_service_url) res_data = req.read() print(str(res_data, encoding = "utf-8")) return JsonResponse(json.loads(res_data))
在pom中加入依赖spring-cloud-netflix-sidecar,完整的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.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>cloud</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cloud</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</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.cloud</groupId> <artifactId>spring-cloud-netflix-sidecar</artifactId> <!-- <version>1.2.4.RELEASE</version><!–具体版本可自选–>--> </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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
应用主类Application上加上EnableSidecar,该注解包含@EnableCircuitBreaker, @EnableDiscoveryClient以及@EnableZuulProxy。
@SpringBootApplication @RestController @EnableSidecar public class CloudApplication { public static void main(String[] args) { SpringApplication.run(CloudApplication.class, args); } }
application.properties上加上注册中心的地址:
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ ##Sidecar注册到Eureka注册中心的端口 server.port=8666 ## 服务的名称,在Eureka注册中心上会显示此名称(在生产环境中,此名称最好与Sidecar所代理服务的名称保持一致) spring.application.name=sidecar ##Sidecar监听的非JVM服务端口 sidecar.port=8000 ##非JVM服务需要实现该接口,[响应结果](#原有服务实现健康检查API)后面会给出注册配置 sidecar.health-uri=http://localhost:8000/health #hystrix.command.default.execution.timeout.enabled: false hystrix.metrics.enabled=false
sidecar.port属性是非jre程序监听的端口号,以使得Sidecar将该服务正确注册到Eureka。sidecar.health-uri是非jre应用提供的一个对外暴露的可访问uri地址,在该地址对应的接口中需要实现一个模仿Spring Boot健康检查指示器的功能。它需要返回如下信息:
{ status: "UP" }
将Sidecar和非JVM服务部署在同一台机器上。
部署起来后可以看到sidecar注册到了Eureka并且状态是UP。
本文使用feign来调用python服务,以测试sidecar形式在微服务中的可用性。
pom中加入spring-cloud-starter-openfeign,完整的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.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>callpython</artifactId> <version>0.0.1-SNAPSHOT</version> <name>callpython</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud-services.version>2.1.4.RELEASE</spring-cloud-services.version> <spring-cloud.version>Hoxton.RC1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- <dependency>--> <!-- <groupId>org.springframework.cloud</groupId>--> <!-- <artifactId>spring-cloud-openfeign-dependencies</artifactId>--> <!-- </dependency>--> <dependency> <groupId>io.pivotal.spring.cloud</groupId> <artifactId>spring-cloud-services-starter-service-registry</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</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.batch</groupId> <artifactId>spring-batch-test</artifactId> <scope>test</scope> </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> <dependency> <groupId>io.pivotal.spring.cloud</groupId> <artifactId>spring-cloud-services-dependencies</artifactId> <version>${spring-cloud-services.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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
application.yml中加上配置eureka地址:
server: port: 8700 spring: application: name: gateway eureka: client: register-with-eureka: true fetch-registry: true serviceUrl: defaultZone: http://localhost:8761/eureka/
添加Feign调用类:配置上sidecar在eureka上注册的应用名
@FeignClient(name = "sidecar") public interface PythonFeign { @RequestMapping(value = "/instance_info", method = RequestMethod.GET) String getInstanceInfo() throws Exception; }
添加Controller:
@RestController public class InfoController { @Autowired private RestTemplate restTemplate; @Autowired private PythonFeign pythonFeign; @GetMapping(value = { "/jvm_instance_info" }) public Map getInstance() { Map<String, Object> retVal = new HashMap<>(); retVal.put("staus", 0); retVal.put("msg", "sucess"); Map<String, Object> instanceInfo = new HashMap<>(); instanceInfo.put("instance_name", "JVM_Gateway"); retVal.put("data", instanceInfo); return retVal; } @RequestMapping("/python_user") public String PythonUser() { String strTmp = restTemplate.getForEntity("http://127.0.0.1:8000/instance_info", String.class).getBody(); return strTmp; } @GetMapping("/python_instance_info") public String getPythonInstanceInfo(){ try { return pythonFeign.getInstanceInfo(); } catch (Exception e) { System.out.println(e.getMessage()); } return ""; } }
启动后,查看eureka可以看到服务已经注册上了:gateway那个应用。
启动后,访问http://127.0.0.1:8700/jvm_instance_info 测试本服务的可用性:
{ "msg": "sucess", "staus": 0, "data": { "instance_name": "JVM_Gateway" } }
调用 http://127.0.0.1:8700/python_instance_info
会调用到python服务:
{ status: 0, msg: "sucess", data: { instance_name: "python_server" } }
至此,可以看到Sidecar双向代理了python服务,把python服务纳入了基于SpringCloud的微服务中提供服务。