spring cloud微服务器cloud怎么注册同一个服务多个实例

如何使用Spring Cloud – 简单服务流程(服务发现与API调用) - 推酷
如何使用Spring Cloud – 简单服务流程(服务发现与API调用)
说起Spring Cloud那肯定要带上Spring Boot,业内人士对这两个东西必定不陌生。关于Spring Cloud的介绍,这里就不再过多的介绍。关于Spring Cloud搜索引擎搜索出来的资料并不乐观,可能向我一样的初学者,最需要的就是一份demo,先跑起来,至少做到麻雀虽小五脏俱全。
在这里还是要介绍以下Spring Cloud整个的工作流程。首先看一下Spring Cloud的工作流程,
Service Consumer -& Proxy Server -& Client(Load Balancer) -& Servie Discovery -& Target Service
这里就是要一个简单Service API调用的流程。下面看一下Spring Cloud中的组件与上面流程的对应关系。
Spring Cloud
Restful API (Spring Boot)
Target Service
Servie Discovery
Client & Load Balancer
Spring RestTemplate & Ribbon
Proxy(Edge) Server
Service Consumer
Browser,Curl,Other Apps,etc
下面一步步介绍完成以上的简单demo
###数据结构及基础服务:
在这里使用Spring Boot快速的创建三个基础服务,并且其中三者之间存在逻辑依赖关系。
Company Service
public class Company {
// Constructor、Getter、Setter
@RestController
public class CompanyService {
@RequestMapping(&/company/{id}&)
public Company getCompanyById(@PathVariable(&id&) Long id){
return new Company(id, &Company&);
//利用时间等待模拟Serivce调用时长
private void sleep() {
Random rand = new Random();
int time = rand.nextInt(2000);
Thread.sleep(time);
} catch (Exception e) {
e.printStackTrace();
public class Company {
&&&&private L
&&&&private S
&&&&// Constructor、Getter、Setter
@RestController
public class CompanyService {
&&&&@RequestMapping(&/company/{id}&)
&&&&public CompanygetCompanyById(@PathVariable(&id&) Long id){
&&&&&&&&sleep();
&&&&&&&&return new Company(id, &Company&);
//利用时间等待模拟Serivce调用时长
&&&&private void sleep() {
&&&&&&&&Randomrand = new Random();
&&&&&&&&int time = rand.nextInt(2000);
&&&&&&&&try {
&&&&&&&&&&&&Thread.sleep(time);
&&&&&&&&} catch (Exception e) {
&&&&&&&&&&&&e.printStackTrace();
Employee Service
public class Employee {
private Long companyId;
// Constructor、Getter、Setter
@RestController
public class EmployeeService {
@RequestMapping(&/employee/{id}&)
public Employee getEmployeeById(@PathVariable(&id&) Long id) {
return new Employee(id,1L,&张三&);
@RequestMapping(&/employee&)
public List&Employee& getEmployeesByCompanyId(@RequestParam(&companyId&) Long companyId){
List&Employee& employees = new ArrayList&&();
employees.add(new Employee(1L, companyId, &张三&));
employees.add(new Employee(2L, companyId, &李四&));
employees.add(new Employee(3L, companyId, &王五&));
private void sleep() {
Random rand = new Random();
int time = rand.nextInt(2000);
Thread.sleep(time);
} catch (Exception e) {
e.printStackTrace();
public class Employee {
&&&&private L
&&&&private Long companyId;
&&&&private S
&&&&// Constructor、Getter、Setter
@RestController
public class EmployeeService {
&&&&@RequestMapping(&/employee/{id}&)
&&&&public EmployeegetEmployeeById(@PathVariable(&id&) Long id) {
&&&&&&&&sleep();
&&&&&&&&return new Employee(id,1L,&张三&);
&&&&@RequestMapping(&/employee&)
&&&&public List&Employee& getEmployeesByCompanyId(@RequestParam(&companyId&) Long companyId){
&&&&&&&&List&Employee& employees = new ArrayList&&();
&&&&&&&&employees.add(new Employee(1L, companyId, &张三&));
&&&&&&&&employees.add(new Employee(2L, companyId, &李四&));
&&&&&&&&employees.add(new Employee(3L, companyId, &王五&));
&&&&&&&&sleep();
&&&&private void sleep() {
&&&&&&&&Randomrand = new Random();
&&&&&&&&int time = rand.nextInt(2000);
&&&&&&&&try {
&&&&&&&&&&&&Thread.sleep(time);
&&&&&&&&} catch (Exception e) {
&&&&&&&&&&&&e.printStackTrace();
Product Service
public class Product {
private Long companyId;
// Constructor、Getter、Setter
@RestController
public class ProductService {
private static final Logger LOG = LoggerFactory.getLogger(ProductService.class);
@RequestMapping(&/product/{id}&)
public Product getProductById(@PathVariable(&id&) Long id) {
return new Product(id, 1L, &T001&);
@RequestMapping(&/product&)
public List&Product& getProductsByCompanyId(@RequestParam(&companyId&) Long companyId) {
List&Product& products = new ArrayList&&();
products.add(new Product(1L, companyId, &T001&));
products.add(new Product(2L, companyId, &T002&));
products.add(new Product(3L, companyId, &T003&));
private void sleep() {
Random rand = new Random();
int time = rand.nextInt(3000);
Thread.sleep(time);
} catch (Exception e) {
e.printStackTrace();
public class Product {
&&&&private L
&&&&private Long companyId;
&&&&private S
&&&&// Constructor、Getter、Setter
@RestController
public class ProductService {
&&&&private static final LoggerLOG = LoggerFactory.getLogger(ProductService.class);
&&&&@RequestMapping(&/product/{id}&)
&&&&public ProductgetProductById(@PathVariable(&id&) Long id) {
&&&&&&&&sleep();
&&&&&&&&return new Product(id, 1L, &T001&);
&&&&@RequestMapping(&/product&)
&&&&public List&Product& getProductsByCompanyId(@RequestParam(&companyId&) Long companyId) {
&&&&&&&&List&Product& products = new ArrayList&&();
&&&&&&&&products.add(new Product(1L, companyId, &T001&));
&&&&&&&&products.add(new Product(2L, companyId, &T002&));
&&&&&&&&products.add(new Product(3L, companyId, &T003&));
&&&&private void sleep() {
&&&&&&&&Randomrand = new Random();
&&&&&&&&int time = rand.nextInt(3000);
&&&&&&&&try {
&&&&&&&&&&&&Thread.sleep(time);
&&&&&&&&} catch (Exception e) {
&&&&&&&&&&&&e.printStackTrace();
这三个服务都应该是正常独立服务的。
所有的服务都需要在注册中心进行对当前服务的注册,因此要在三个服务的启动程序上加上注解 @EnableDiscoveryClient
在配置文件bootstrap.yml中加入以下配置
application:
name: company-service
uri: ${vcap.services.${PREFIX:}configserver.credentials.uri:http://user:password@localhost:8888}
&&application:
&&&&name: company-service
&&&&config:
&&&&&&uri: ${vcap.services.${PREFIX:}configserver.credentials.uri:http://user:password@localhost:8888}
###服务中心
注册中心,在Demo中只用到最简单的服务发现与注册,只需要在启动程序加上注解 @EnableEurekaServer
@EnableDiscoveryClient
在application.yml中对注册服务进行配置
port: 8761
password: ${eureka.password}
registerWithEureka: false
fetchRegistry: false
waitTimeInMsWhenSyncEmpty: 0
password: ${SECURITY_USER_PASSWORD:password}
&&port: 8761
&&&&password: ${eureka.password}
&&&&registerWithEureka: false
&&&&fetchRegistry: false
&&&&waitTimeInMsWhenSyncEmpty: 0
&&password: ${SECURITY_USER_PASSWORD:password}
###远程调用Client Adapter
使用服务发现来寻找真实的服务地址,并获取API返回的数据。
在这里需要创建三个model,用来转换API结构
public class CompanyAll {
private List&ProductDetail& productL
private List&EmployeeDetail& employeeL
public CompanyAll(Company company, List&Product& productList, List&Employee& employeeList) {
this.id = company.getId();
this.name = company.getName();
if (employeeList != null) {
this.productList = productList.stream().map(product -&
new ProductDetail(product.getId(), product.getSku())
).collect(Collectors.toList());
if (employeeList != null) {
this.employeeList = employeeList.stream().map(employee -&
new EmployeeDetail(employee.getId(), employee.getName())
).collect(Collectors.toList());
//Getter、Setter
public class EmployeeDetail {
// Constructor、Getter、Setter
public class ProductDetail {
// Constructor、Getter、Setter
public class CompanyAll {
&&&&private L
&&&&private S
&&&&private List&ProductDetail& productL
&&&&private List&EmployeeDetail& employeeL
&&&&public CompanyAll(Companycompany, List&Product& productList, List&Employee& employeeList) {
&&&&&&&&this.id = company.getId();
&&&&&&&&this.name = company.getName();
&&&&&&&&if (employeeList != null) {
&&&&&&&&&&&&this.productList = productList.stream().map(product -&
&&&&&&&&&&&&&&&&&&&&new ProductDetail(product.getId(), product.getSku())
&&&&&&&&&&&&).collect(Collectors.toList());
&&&&&&&&if (employeeList != null) {
&&&&&&&&&&&&this.employeeList = employeeList.stream().map(employee -&
&&&&&&&&&&&&&&&&&&&&new EmployeeDetail(employee.getId(), employee.getName())
&&&&&&&&&&&&).collect(Collectors.toList());
&&&&//Getter、Setter
public class EmployeeDetail {
&&&&private L
&&&&private S
&&// Constructor、Getter、Setter
public class ProductDetail {
&&&&private L
&&&&private S
&&&&// Constructor、Getter、Setter
创建工具类AppUtil
在工具类中会调用到LoadBalancerClient,这个就是有Netflix Ribbon提供的Client。他会根据ServiceId(配置文件中的Service Name)向Eureka(注册服务器)获取服务地址。在这里也可以提供一个容错机制,极限情况下,全宕机时使用fallbackUri。
@Component
public class AppUtil {
@Autowired
private LoadBalancerClient loadB
public URI getRestUrl(String serviceId, String fallbackUri) {
ServiceInstance instance = loadBalancer.choose(serviceId);
uri = instance.getUri();
} catch (RuntimeException e) {
uri = URI.create(fallbackUri);
public &T& ResponseEntity&T& createOkResponse(T body) {
return createResponse(body, HttpStatus.OK);
public &T& ResponseEntity&T& createResponse(T body, HttpStatus httpStatus) {
return new ResponseEntity&&(body, httpStatus);
public &T& T json2Object(ResponseEntity&String& response, Class&T& clazz) {
return (T) JSON.parseObject(response.getBody(), clazz);
} catch (Exception e) {
throw new RuntimeException(e);
public &T& List&T& json2Objects(ResponseEntity&String& response, Class&T& clazz) {
return JSON.parseArray(response.getBody(), clazz);
} catch (Exception e) {
throw new RuntimeException(e);
@Component
public class AppUtil {
&&&&@Autowired
&&&&private LoadBalancerClientloadB
&&&&public URIgetRestUrl(String serviceId, String fallbackUri) {
&&&&&&&&URIuri =
&&&&&&&&try {
&&&&&&&&&&&&ServiceInstanceinstance = loadBalancer.choose(serviceId);
&&&&&&&&&&&&uri = instance.getUri();
&&&&&&&&} catch (RuntimeException e) {
&&&&&&&&&&&&uri = URI.create(fallbackUri);
&&&&public &T& ResponseEntity&T& createOkResponse(T body) {
&&&&&&&&return createResponse(body, HttpStatus.OK);
&&&&public &T& ResponseEntity&T& createResponse(T body, HttpStatushttpStatus) {
&&&&&&&&return new ResponseEntity&&(body, httpStatus);
&&&&public &T& T json2Object(ResponseEntity&String& response, Class&T& clazz) {
&&&&&&&&try {
&&&&&&&&&&&&return (T) JSON.parseObject(response.getBody(), clazz);
&&&&&&&&} catch (Exception e) {
&&&&&&&&&&&&throw new RuntimeException(e);
&&&&public &T& List&T& json2Objects(ResponseEntity&String& response, Class&T& clazz) {
&&&&&&&&try {
&&&&&&&&&&&&return JSON.parseArray(response.getBody(), clazz);
&&&&&&&&} catch (Exception e) {
&&&&&&&&&&&&throw new RuntimeException(e);
当然这里还需要一个Client来对Api进行访问及Response数据处理
@Component
public class RemoteServiceClient {
@Autowired
AppUtil appU
private RestTemplate restTemplate = new RestTemplate();
public ResponseEntity&Company& getCompanyById(Long id) {
URI uri = appUtil.getRestUrl(&COMPANY-SERVICE&, &http://localhost:57773/&);
String url = uri.toString() + &/company/& +
ResponseEntity&String& resultStr = restTemplate.getForEntity(url, String.class);
Company company = appUtil.json2Object(resultStr, Company.class);
return appUtil.createOkResponse(company);
public ResponseEntity&List&Employee&& getEmployeesByCompanyId(Long id) {
URI uri = appUtil.getRestUrl(&EMPLOYEE-SERVICE&, &http://localhost:58017&);
String url = uri.toString() + &/employee?companyId=& +
ResponseEntity&String& resultStr = restTemplate.getForEntity(url, String.class);
List&Employee& employees = appUtil.json2Objects(resultStr, Employee.class);
return appUtil.createOkResponse(employees);
} catch (Throwable t) {
public ResponseEntity&List&Product&& getProductsByCompanyId(Long id) {
URI uri = appUtil.getRestUrl(&PRODUCT-SERVICE&, &http://localhost:57750&);
String url = uri.toString() + &/product?companyId=& +
ResponseEntity&String& resultStr = restTemplate.getForEntity(url, String.class);
List&Product& employees = appUtil.json2Objects(resultStr, Product.class);
return appUtil.createOkResponse(employees);
} catch (Throwable t) {
@Component
public class RemoteServiceClient {
&&&&@Autowired
&&&&AppUtilappU
&&&&private RestTemplaterestTemplate = new RestTemplate();
&&&&public ResponseEntity&Company& getCompanyById(Long id) {
&&&&&&&&URIuri = appUtil.getRestUrl(&COMPANY-SERVICE&, &http://localhost:57773/&);
&&&&&&&&String url = uri.toString() + &/company/& +
&&&&&&&&ResponseEntity&String& resultStr = restTemplate.getForEntity(url, String.class);
&&&&&&&&Companycompany = appUtil.json2Object(resultStr, Company.class);
&&&&&&&&return appUtil.createOkResponse(company);
&&&&public ResponseEntity&List&Employee&& getEmployeesByCompanyId(Long id) {
&&&&&&&&try {
&&&&&&&&&&&&URIuri = appUtil.getRestUrl(&EMPLOYEE-SERVICE&, &http://localhost:58017&);
&&&&&&&&&&&&String url = uri.toString() + &/employee?companyId=& +
&&&&&&&&&&&&ResponseEntity&String& resultStr = restTemplate.getForEntity(url, String.class);
&&&&&&&&&&&&List&Employee& employees = appUtil.json2Objects(resultStr, Employee.class);
&&&&&&&&&&&&return appUtil.createOkResponse(employees);
&&&&&&&&} catch (Throwable t) {
&&&&&&&&&&&&
&&&&public ResponseEntity&List&Product&& getProductsByCompanyId(Long id) {
&&&&&&&&try {
&&&&&&&&&&&&URIuri = appUtil.getRestUrl(&PRODUCT-SERVICE&, &http://localhost:57750&);
&&&&&&&&&&&&String url = uri.toString() + &/product?companyId=& +
&&&&&&&&&&&&ResponseEntity&String& resultStr = restTemplate.getForEntity(url, String.class);
&&&&&&&&&&&&List&Product& employees = appUtil.json2Objects(resultStr, Product.class);
&&&&&&&&&&&&return appUtil.createOkResponse(employees);
&&&&&&&&} catch (Throwable t) {
&&&&&&&&&&&&
最终还是需要对外暴露一个端口来返回数据,这里也是一个Restful接口
@RestController
public class RemoteAdapter {
private static final Logger LOG = LoggerFactory.getLogger(RemoteAdapter.class);
@Autowired
RemoteServiceC
@Autowired
AppUtil appU
@RequestMapping(&/&)
public String welcome() {
return &welcome to use my demo&;
@RequestMapping(&/company/{id}&)
public ResponseEntity&CompanyAll& getCompany(@PathVariable(&id&) Long id) {
ResponseEntity&Company& companyResult = client.getCompanyById(id);
if (!companyResult.getStatusCode().is2xxSuccessful()) {
return appUtil.createResponse(null, companyResult.getStatusCode());
List&Product& products =
ResponseEntity&List&Product&& productsResult = client.getProductsByCompanyId(id);
if (!productsResult.getStatusCode().is2xxSuccessful()) {
LOG.error(&远程调用Product API 失败&);
products = productsResult.getBody();
} catch (Throwable t) {
LOG.error(&远程调用Product API 异常 &, t);
List&Employee& employees =
ResponseEntity&List&Employee&& employeeResult =
employeeResult = client.getEmployeesByCompanyId(id);
if (!employeeResult.getStatusCode().is2xxSuccessful()) {
LOG.error(&远程调用Employee API 失败&);
employees = employeeResult.getBody();
} catch (Throwable t) {
LOG.error(&远程调用Employee API 失败&);
return appUtil.createOkResponse(new CompanyAll(companyResult.getBody(), products, employees));
@RestController
public class RemoteAdapter {
&&&&private static final LoggerLOG = LoggerFactory.getLogger(RemoteAdapter.class);
&&&&@Autowired
&&&&RemoteServiceC
&&&&@Autowired
&&&&AppUtilappU
&&&&@RequestMapping(&/&)
&&&&public String welcome() {
&&&&&&&&return &welcome to use my demo&;
&&&&@RequestMapping(&/company/{id}&)
&&&&public ResponseEntity&CompanyAll& getCompany(@PathVariable(&id&) Long id) {
&&&&&&&&ResponseEntity&Company& companyResult = client.getCompanyById(id);
&&&&&&&&if (!companyResult.getStatusCode().is2xxSuccessful()) {
&&&&&&&&&&&&return appUtil.createResponse(null, companyResult.getStatusCode());
&&&&&&&&List&Product& products =
&&&&&&&&try {
&&&&&&&&&&&&ResponseEntity&List&Product&& productsResult = client.getProductsByCompanyId(id);
&&&&&&&&&&&&if (!productsResult.getStatusCode().is2xxSuccessful()) {
&&&&&&&&&&&&&&&&LOG.error(&远程调用Product API 失败&);
&&&&&&&&&&&&} else {
&&&&&&&&&&&&&&&&products = productsResult.getBody();
&&&&&&&&&&&&}
&&&&&&&&} catch (Throwable t) {
&&&&&&&&&&&&LOG.error(&远程调用Product API 异常 &, t);
&&&&&&&&&&&&
&&&&&&&&List&Employee& employees =
&&&&&&&&try {
&&&&&&&&&&&&ResponseEntity&List&Employee&& employeeResult =
&&&&&&&&&&&&employeeResult = client.getEmployeesByCompanyId(id);
&&&&&&&&&&&&if (!employeeResult.getStatusCode().is2xxSuccessful()) {
&&&&&&&&&&&&&&&&LOG.error(&远程调用Employee API 失败&);
&&&&&&&&&&&&} else {
&&&&&&&&&&&&&&&&employees = employeeResult.getBody();
&&&&&&&&&&&&}
&&&&&&&&} catch (Throwable t) {
&&&&&&&&&&&&LOG.error(&远程调用Employee API 失败&);
&&&&&&&&&&&&
&&&&&&&&return appUtil.createOkResponse(new CompanyAll(companyResult.getBody(), products, employees));
Ok,这里最后还是需要application.yml配置文件,对相关进行配置。当然bootstrap.yml的注册配置还是不能少的
leaseRenewalIntervalInSeconds: 10
metadataMap:
instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}}
registryFetchIntervalSeconds: 5
&&instance:
&&&&leaseRenewalIntervalInSeconds: 10
&&&&metadataMap:
&&&&&&instanceId: ${vcap.application.instance_id:${spring.application.name}:${spring.application.instance_id:${random.value}}}
&&&&registryFetchIntervalSeconds: 5
###代理(边界)服务
边界服务在这里也demo中没有运用到太多的功能,这里只使用了路由转发的作用,因此也不用做过多的配置。只需要在启动程序上添加注解 @Controller @EnableZuulProxy即可
在application.yml需要对服务进行一些配置,由于在上述代码中提到了线程等待时间来模拟API调用时间,因此需要对ribbon的TimeOut时间设置到60秒。对zuul的配置最常用的是路由,对Client暴漏的服务接口进行路由转发
component: Zuul Server
endpoints:
enabled: true
enabled: true
sensitive: false
execution:
enabled: false
ReadTimeout: 60000
ConnectTimeout: 6000
ignoredServices: &*&
service-adapter:
path: /app/**
port: 8765
ROOT: INFO
org.springframework.web: INFO
&&component: Zuul Server
endpoints:
&&restart:
&&&&enabled: true
&&shutdown:
&&&&enabled: true
&&&&sensitive: false
&&command:
&&&&default:
&&&&&&execution:
&&&&&&&&timeout:
&&&&&&&&&&enabled: false
&&ReadTimeout: 60000
&&ConnectTimeout: 6000
&&ignoredServices: &*&
&&&&service-adapter:
&&&&&&path: /app/**
&&port: 8765
&&&&ROOT: INFO
&&&&org.springframework.web: INFO
## 结果预览
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致使用 Cloud Foundry 开发 Spring 应用程序
本部分为使用 Cloud Foundry 部署其应用程序的 Spring 开发人员提供了实用信息。本部分还特别说明了特定于 Cloud Foundry 环境的 Spring 编程和打包主题,以及如何才能使用所提供的服务(如 MySQL 和 RabbitMQ)。
(如果您希望使用 Spring Insight 来监控 Cloud Foundry 上的 Java 应用程序,请阅读。)
(如果您使用 Spring、Roo 或 Grails,且希望阅读一些代码示例,请点击下面的有用链接:)
假设您已安装了 VMC、SpringSource STS 或 Eclipse,且您已使用其中一个工具在 Cloud Foundry 中部署了简单的 HelloWorld 应用程序,Cloud Foundry 可以是本地 Micro 版本,也可以是
中的托管服务。另外还假设,您是一位精通 Spring 应用程序的开发人员。有关其他信息,请参见以下内容:
编程和打包 Spring 应用程序
一般而言,您在编程或打包希望部署到 Cloud Foundry 的 Spring 应用程序时无需执行任何“特殊”操作。也就是说,如果您的 Spring 应用程序是在 TC Server 或 Apache Tomcat 上运行,那么它也将在 Cloud Foundry 上运行,并且不会有任何改变。
通常情况下,最好是将您的 Spring 应用程序打包到一个 *.war 文件中,这样,vmc push 将自动检测它是否为 Spring 应用程序,但如果您愿意,也可以将它部署为一个分解式目录。
但是,还有一些有关 Cloud Foundry 环境的问题应引起您的重视,因为它可能会影响到您所部署的应用程序的运行情况:
本地磁盘存储稍纵即逝。换句话说,本地磁盘存储不保证在应用程序的整个生命周期中都能持续存在。这是因为 Cloud Foundry 会在您每次重新启动应用程序时创建一个新的虚拟磁盘。另外,Cloud Foundry 会在更新其自身环境之后重新启动所有应用程序。这就是说,即使您的应用程序能够 在运行时写入本地文件,这些文件也将在应用程序重新启动之后消失。如果您的应用程序写入的文件是临时文件,也就不会出现问题。但如果您的应用程序需要保留文件中的数据,那您就必须使用其中一个数据服务来管理这些数据。在这种情况下,MongoDB 就是一个很好的选择,因为它是一款面向文档的数据库。
Cloud Foundry 使用 Apache Tomcat 作为其应用程序服务器并在 root 上下文中运行您的应用程序。这与正常的 Apache Tomcat 有所不同,在正常的 Apache Tomcat 中,上下文路径由打包应用程序的 *.war 文件的名称决定。
HTTP 会话未复制,但 HTTP 通信具有粘性。这就是说,如果您的应用程序崩溃或重新启动,则 HTTP 会话将丢失。
外部用户只能通过由 vmc push 命令(或等效的 STS 命令)提供的 URL 才能访问您的应用程序。即使您的应用程序可以在内部侦听其他端口(例如,MBean 服务器的 JMX 端口),您的应用程序的外部用户也无法侦听这些端口。
在 Spring 应用程序中使用 Cloud Foundry 服务
如果您的 Spring 应用程序必须使用服务(如数据库或 RabbitMQ),您可以将应用程序部署到 Cloud Foundry,无需更改任何代码.
在这种情况下,Cloud Foundry 会自动重新配置相关 Bean 定义,以将它们绑定到云服务。有关详细信息,请参见。
如果您的 Spring 应用程序无法利用 Cloud Foundry 的自动重新配置功能,或您希望拥有更多的配置控制权,您还需要执行其他一些操作,这些操作非常简单。请参见。
确定您的应用程序是否能够自动配置
您能够将很多已有的 Spring 应用程序部署到 Cloud Foundry,而无需更改任何代码,即使您的应用程序需要使用关系数据库或 RabbitMQ 之类的服务也是如此。这是因为,Cloud Foundry 会自动检测您的应用程序所需的服务类型,如果其配置属于一小部分,Cloud Foundry 将自动对其进行重新配置,以便它绑定到 Cloud Foundry 自行创建和维护的服务实例。
通过自动重新配置功能,Cloud Foundry 会使用其自身的属性(例如 host、port、username 等)值自行创建数据库或连接工厂。例如,如果您的应用程序上下文中含有一个单一的 javax.sql.DataSource Bean,且 Cloud Foundry 对其进行重新配置并绑定到其自身的数据库服务,则 Cloud Foundry 不会使用您最初指定的用户名、密码和驱动程序 URL。相反,它会使用其自身的内部值。这些值对于应用程序来说是透明的,因为这些应用程序关心的只是拥有能够写入数据的关系数据库,而不关心使用的是什么特定属性来创建数据库。
下面的部分针对每个支持的服务阐明了在发生自动配置的情况下 Cloud Foundry 检测的 Bean 类型。
关系数据库(MySQL 和 vFabric Postgres)
如果 Cloud Foundry 检测到 javax.sql.DataSource Bean 就会发生自动重新配置。Spring 应用程序上下文文件中的以下代码段展示了一个示例,该示例定义了 Cloud Foundry 将依次检测并进行可能的自动重新配置的 Bean 类型:
&bean class="mons.dbcp.BasicDataSource" destroy-method="close" id="dataSource"&
&property name="driverClassName" value="org.h2.Driver" /&
&property name="url" value="jdbc:h2:mem:" /&
&property name="username" value="sa" /&
&property name="password" value="" /&
Cloud Foundry 实际使用的关系数据库取决于您在部署应用程序时直接绑定到应用程序的服务实例:MySQL 或 vFabric Postgres。Cloud Foundry 会创建一个 Commons DBCP 或 Tomcat 数据源。
Cloud Foundry 将在内部生成以下属性的值:driverClassName、url、username、password、validationQuery。
您必须使用
1.0 M4 或更高版本才能进行自动重新配置。
如果 Cloud Foundry 检测到 org.springframework.data.document.mongodb.MongoDbFactory Bean 就会发生自动重新配置。Spring 应用程序上下文文件中的以下代码段展示了一个示例,该示例定义了 Cloud Foundry 将依次检测并进行可能的自动重新配置的 Bean 类型:
&mongo:db-factory
id="mongoDbFactory"
dbname="pwdtest"
host="127.0.0.1"
port="1234"
username="test_user"
password="test_pass"
Cloud Foundry 将创建一个 SimpleMOngoDbFactory,其中含有其自身针对以下属性的值:host、port、username、password、dbname。
您必须使用
1.0 M4 或更高版本才能进行自动重新配置。
如果 Cloud Foundry 检测到 org.springframework.data.redis.connection.RedisConnectionFactory Bean 就会发生自动重新配置。Spring 应用程序上下文文件中的以下代码段展示了一个示例,该示例定义了 Cloud Foundry 将依次检测并进行可能的自动重新配置的 Bean 类型:
&bean id="redis"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:hostName="localhost" p:port="6379"
Cloud Foundry 将创建一个 JedisConnectionFactory,其中含有其自身针对以下属性的值:host、port、password。这就是说,您必须在您的应用程序中打包 Jedis JAR。Cloud Foundry 当前不支持 JRedis 和 RJC 的实施。
您必须使用
1.0 或更高版本才能进行自动重新配置。Spring AMQP 可提供发布、多线程用户生成和消息转换器。它还可以促进 AMQP 资源的管理,同时提升依赖项输入和声明配置。
如果 Cloud Foundry 检测到 org.springframework.amqp.rabbit.connection.ConnectionFactory Bean 就会发生自动重新配置。Spring 应用程序上下文文件中的以下代码段展示了一个示例,该示例定义了 Cloud Foundry 将依次检测并进行可能的自动重新配置的 Bean 类型:
&rabbit:connection-factory
id="rabbitConnectionFactory"
host="localhost"
password="testpwd"
port="1238"
username="testuser"
virtual-host="virthost" /&
Cloud Foundry 将创建一个 org.springframework.amqp.rabbit.connection.CachingConnectionFactory,其中含有其自身针对以下属性的值:host、virtual-host、port、username、password。
自动重新配置的限制
Cloud Foundry 只有在以下项针对您的应用程序为 true 时才能对应用程序进行自动重新配置:
您只能将一种给定服务类型的一个 服务实例绑定到您的应用程序。在此上下文中,MySQL 和 vFabric Postgres 被认为是相同的服务类型(关系数据库),因此如果您已经将 MySQL 和 vFabric Postgres 服务绑定到您的应用程序,自动重新配置将不会 发生。
您只能在您的 Spring 应用程序上下文文件中包含一种匹配类型的一个 Bean。例如,您只能有一个 javax.sql.DataSource 类型的 Bean。
另请注意,如果发生了自动重新配置,但您已对服务进行了自定义配置(例如池大小或连接属性),则 Cloud Foundry 会忽略自定义配置。
选择退出自动重新配置机制
有些时候,您可能不希望 Cloud Foundry 使用本部分中描述的方法对您的 Spring 应用程序进行自动重新配置。有两种方法可以退出自动重新配置机制:
当您使用 VMC 或 STS 部署应用程序时,指定框架为 JavaWeb 而不是 Spring。注意,在此情况下,您的应用程序将无法利用 Spring 配置文件功能。
使用 Spring 应用程序上下文文件中的 &cloud:& 命名空间元素直接创建代表服务的 Bean。这将使得自动重新配置机制不再必要。请参见。
直接配置应用程序使用 Cloud Foundry 服务
本部分说明了您在 Spring 应用程序中必须执行的简单配置步骤,以使这些应用程序可以使用 Cloud Foundry 服务。请参见,以便获取受支持 Cloud Foundry 服务的完整列表。
在 Spring 应用程序中使用 Cloud Foundry 服务的最简单方法就是声明 &cloud:& 命名空间,将其指向 Cloud Foundry 架构,然后使用在 &cloud& 命名空间中定义的特定于服务的元素。例如,仅使用应用程序上下文文件中的单一 XML 行,您可以创建一个 JDBC 数据源或 RabbitMQ 连接工厂,以便在特定 Bean 定义中使用。如果您的应用程序需要,您可以配置多个数据源或连接工厂。如果您愿意,还可以进一步配置这些云服务,但这完全是自愿的,因为 Cloud Foundry 会使用常用的配置值来创建典型服务实例,而这些服务实例对于大多数用途来说完全足够。总体而言,使用 &cloud:& 命名空间会针对您的应用程序所使用的 Cloud Foundry 服务的数量和类型为您提供尽可能多的控制。
更新您的 Spring 应用程序以使用任意 Cloud Foundry 服务的基本步骤如下所示:
更新您的应用程序构建流程,向 org.cloudfoundry.cloudfoundry-runtime 项目添加依赖项。例如,如果您使用 Maven 来构建您的应用程序,则以下 pom.xml 代码段展示了如何添加此依赖项:
&dependencies&
&dependency&
&groupId&org.cloudfoundry&/groupId&
&artifactId&cloudfoundry-runtime&/artifactId&
&version&0.8.1&/version&
&/dependency&
&!-- additional dependency declarations --&
&/dependencies&
更新您的应用程序构建流程,添加 Spring Framework Milestone Repository。以下 pom.xml 代码段展示了如何在 Maven 中执行此操作:
&repositories&
&repository&
&id&org.springframework.maven.milestone&/id&
&name&Spring Maven Milestone Repository&/name&
&url&http://maven.springframework.org/milestone&/url&
&snapshots&
&enabled&false&/enabled&
&/snapshots&
&/repository&
&!-- additional repository declarations --&
&/repositories&
在您的 Spring 应用程序中,更新将包含 Cloud Foundry 服务声明(例如数据源)的所有应用程序上下文文件,方法是:添加 &cloud:& 命名空间声明和 Cloud Foundry 服务架构的位置,如下面的代码段所示:
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring.xsd
&!-- bean declarations --&
您现在就可以使用 &cloud:& 命名空间和特定元素的名称(例如 data-source)在 Spring 应用程序上下文文件中指定 Cloud Foundry 服务。Cloud Foundry 可为每个受支持的服务提供元素:数据库(MySQL 和 vFabric Postgres)、Redis、MongoDB 和 RabbitMQ。
下面的示例展示了将使用 &cloud:data-source& 元素输入到 JdbcTemplate 中的简单数据源配置。
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring.xsd
&cloud:data-source id="dataSource" /&
&bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"&
&property name="dataSource" ref="dataSource" /&
&!-- additional beans in your application --&
当您稍后使用 VMC 或 STS 部署应用程序时,您可以将特定数据服务(如 MySQL 或 vFabric Postgres)绑定到应用程序,且 Cloud Foundry 会创建一个服务实例。注意,在上面的示例中,您并未指定典型的数据源属性(例如 driverClassName、url 或 username)- 这是因为 Cloud Foundry 会自动为您处理这些属性。
有关所有您可在 Spring 应用程序上下文文件中用来访问 Cloud Foundry 服务的 &cloud:& 元素的完整信息,您可以参见以下部分:
在您完成指定要在应用程序中使用的所有 Cloud Foundry 服务的工作后,您可以使用标准 Cloud Foundry 客户端命令(VMC、SpringSource 工具套件或 Eclipse 插件)来创建这些服务的实例,将其绑定到您的应用程序,然后将您的应用程序部署到托管的 Cloud Foundry () 或您的本地 Micro Cloud Foundry 实例。有关如何使用这些工具的详细信息,请参见。
&cloud:data-source&
&cloud:data-source& 元素可以为您提供一种简单的方法,为您的 Spring 应用程序配置 JDBC 数据源。稍后,当您实际部署应用程序时,您可以将特定数据库服务实例绑定到应用程序,例如 MySQL 或 vFabric Postgres。
以下示例展示了一种简单的方法,用于配置将输入到 org.springframework.jdbc.core.JdbcTemplate Bean 的 JDBC 数据源:
&cloud:data-source id="dataSource" /&
&bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"&
&property name="dataSource" ref="dataSource" /&
在前面的示例中,请注意,未提供有关数据源的特定信息,例如 JDBC 驱动程序类名、访问数据库的特定 URL 以及数据库用户。Cloud Foundry 会在运行时使用您绑定到应用程序的特定数据库服务类型的适当信息处理所有这些信息。
下表列出了 &cloud:data-source& 元素的属性。
此数据源的 ID。JdbcTemplate Bean 在引用数据源时使用此 ID。默认值为所绑定服务实例的名称。
service-name
数据源服务的名称。您可以仅在绑定多个数据库服务到应用程序且希望指定将哪个特定服务实例绑定到特定 Spring Bean 时指定此属性。默认值为所绑定服务实例的名称。
高级数据源配置
上面的内容展示了如何配置一个非常简单的 JDBC 数据源,而当 Cloud Foundry 在运行时实际创建数据源时,会使用最为常用的配置选项。但是,您可以使用下面的两个 &cloud:data-source& 子元素来指定其中的某些配置选项:&cloud:connection& 和 &cloud:pool&。
在建立新的数据库连接时,&cloud:connection& 子元素会使用您用来指定希望发送给 JDBC 驱动程序的连接属性的单一字符串属性 (properties)。字符串的格式必须为使用分号分隔的名称/值对 ([propertyName=])。
&cloud:pool& 子元素会使用下面的两个属性:
指定连接池的大小。将此值设置为池中的最大连接数,或连接数的上限与下限之间的范围,使用短线隔开。
默认的最小值为 0,最大值为 8。这些默认值与 Apache Commons Pool 中的默认值相同。
max-wait-time
如果没有可用的连接,此属性可指定在出现异常之前,连接池等待连接返回的最长时间(以毫秒为单位)。指定 `-1` 表示连接池应永久等待。
默认值为 `-1`(永久)。
下面的示例展示了如何使用这些高级数据源配置选项:
&cloud:data-source id="mydatasource"&
&cloud:connection properties="charset=utf-8" /&
&cloud:pool pool-size="5-10" max-wait-time="2000" /&
&/cloud:data-source&
在前面的示例中,JDBC 驱动程序收到了指定它应使用 UTF-8 字符集的属性。在任意给定时间点,池中连接数的上下限应分别为 10 和 5。在没有可用连接的情况下,连接池等待返回连接的最大时间长度为 2000 毫秒(2 秒),在这之后,JDBC 连接池会出现异常。
&cloud:mongo-db-factory &
&cloud:mongo-db-factory& 可提供一种简单的方法,为您的 Spring 应用程序配置 MongoDB 连接工厂。
下面的示例展示了将输入到 org.springframework.data.mongodb.core.MongoTemplate 对象的 MongoDbFactory 配置:
&cloud:mongo-db-factory id="mongoDbFactory" /&
&bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"&
&constructor-arg ref="mongoDbFactory"/&
下表列出了 &cloud:mongo-db-factory& 元素的属性。
此 MongoDB 连接工厂的 ID。MongoTemplate Bean 在引用连接工厂时使用此 ID。默认值为所绑定服务实例的名称。
service-name
MongoDB 服务的名称。您可以仅在绑定多个 MongoDB 服务到应用程序且希望指定将哪个特定服务实例绑定到特定 Spring Bean 时指定此属性。默认值为所绑定服务实例的名称。
write-concern
控制写入数据存储的行为。此属性的值与 `com.mongodb.WriteConcern` 类的值相对应。
如果您不指定此属性,则不会为数据库连接设置 `WriteConcern` 且所有的写入行为都会默认为 NORMAL。
此属性的可能值如下所示:
NONE:不抛出异常,甚至不抛出网络错误异常。
NORMAL:仅抛出网络错误异常,不抛出服务器错误异常。
SAFE:MongoDB 服务等待服务器完成写操作。在出现网络错误和服务器错误时出现异常。
FSYNC_SAVE:MongoDB 服务等待服务器将数据刷新到磁盘,然后再执行写操作。在出现网络错误和服务器错误时出现异常。
高级 MongoDB 配置
上面的内容展示了如何使用选项的默认值来配置简单的 MongoDB 连接工厂。这对于很多环境来说已经够用。但是,您也可以通过指定 &cloud:mongo-db-factory& 的可选 &cloud:mongo-options& 子元素来进一步配置连接工厂。
&cloud:mongo-options& 子元素会使用下面的两个属性:
connections-per-host
指定 MongoDB 实例允许为每个主机建立的最大连接数目。这些连接在空闲时将保留在池中。池中的连接用完后,需要连接的任何操作都将阻滞,等待可用连接。
max-wait-time
指定线程等待连接变为可用状态的最长等待时间(以毫秒为单位)。
120,000(2 分钟)
下面的示例展示了如何使用高级 MongoDB 选项:
&cloud:mongo-db-factory id="mongoDbFactory" write-concern="FSYNC_SAFE"&
&cloud:mongo-options connections-per-host="12" max-wait-time="2000" /&
&/cloud:mongo-db-factory&
在上面的示例中,最大连接数设置为 12,线程等待连接的最大时间长度设置为 1 秒。同时指定 WriteConcern 为尽可能最安全的状态 (FSYNC_SAFE)。
&cloud:redis-connection-factory &
&cloud:redis-connection-factory& 可提供一种简单的方法,为您的 Spring 应用程序配置 Redis 连接工厂。
下面的示例展示了将输入到 org.springframework.data.redis.core.StringRedisTemplate 对象的 RedisConnectionFactory 配置:
&cloud:redis-connection-factory id="redisConnectionFactory" /&
&bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"&
&property name="connection-factory" ref="redisConnectionFactory"/&
下表列出了 &cloud:redis-connection-factory& 元素的属性。
此 Redis 连接工厂的 ID。RedisTemplate Bean 在引用连接工厂时使用此 ID。默认值为所绑定服务实例的名称。
service-name
Redis 服务的名称。您可以仅在绑定多个 Redis 服务到应用程序且希望指定将哪个特定服务实例绑定到特定 Spring Bean 时指定此属性。默认值为所绑定服务实例的名称。
高级 Redis 配置
上面的内容展示了如何配置一个非常简单的 Redis 连接工厂,而当 Cloud Foundry 在运行时实际创建工厂时,会使用最为常用的配置选项。但是,您可以使用 &cloud:redis-connection-factory& 的 &cloud:pool& 子元素来更改其中的某些配置选项。
&cloud:pool& 子元素会使用下面的两个属性:
指定连接池的大小。将此值设置为池中的最大连接数,或连接数的上限与下限之间的范围,使用短线隔开。
默认的最小值为 0,最大值为 8。这些默认值与 Apache Commons Pool 中的默认值相同。
max-wait-time
如果没有可用的连接,此属性可指定在出现异常之前,连接池等待连接返回的最长时间(以毫秒为单位)。指定 `-1` 表示连接池应永久等待。
默认值为 `-1`(永久)。
下面的示例展示了如何使用这些高级 Redis 配置选项:
&cloud:redis-connection-factory id="myRedisConnectionFactory"&
&cloud:pool pool-size="5-10" max-wait-time="2000" /&
&/cloud:redis-connection-factory&
在上面的示例中,在任意给定时间点,池中连接数的上下限应分别为 10 和 5。在没有可用连接的情况下,连接池等待返回连接的最大时间长度为 2000 毫秒(2 秒),在这之后,Redis 连接池会出现异常。
&cloud:rabbit-connection-factory &
&cloud:rabbit-connection-factory& 可提供一种简单的方法,为您的 Spring 应用程序配置 RabbitMQ 连接工厂。
以下 Spring 应用程序上下文文件的完整示例展示了将注入 rabbitTemplate 对象的 RabbitConnectionFactory 配置。在该示例的后面还说明了该示例使用 &rabbit:& 命名空间执行特定于 RabbitMQ 的配置:
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
http://schema.cloudfoundry.org/spring http://schema.cloudfoundry.org/spring/cloudfoundry-spring.xsd"&
&!-- Obtain a connection to the RabbitMQ via cloudfoundry-runtime: --&
&cloud:rabbit-connection-factory id="rabbitConnectionFactory" /&
&!-- Set up the AmqpTemplate/RabbitTemplate: --&
&rabbit:template id="rabbitTemplate"
connection-factory="rabbitConnectionFactory" /&
&!-- Request that queues, exchanges and bindings be automatically declared on the broker: --&
&rabbit:admin connection-factory="rabbitConnectionFactory"/&
&!-- Declare the "messages" queue: --&
&rabbit:queue name="messages" durable="true"/&
&!-- additional beans in your application --&
在上面的示例中,说明了 XML 文件顶部的 &rabbit:& 命名空间的定义和位置。然后,使用此命名空间将 RabbitTemplate 和 RabbitAdmin 对象配置为 Spring AMQP 的主要入口并使用 RabbitMQ 代理声明名为 messages 的队列。
有关在您的 Spring 应用程序中使用 RabbitMQ 的详细信息,请参见 。
下表列出了 &cloud:rabbit-connection-factory& 元素的属性。
此 RabbitMQ 连接工厂的 ID。RabbitTempalte Bean 在引用连接工厂时使用此 ID。默认值为所绑定服务实例的名称。
service-name
RabbitMQ 服务的名称。您可以仅在绑定多个 RabbitMQ 服务到应用程序且希望指定将哪个特定服务实例绑定到特定 Spring Bean 时指定此属性。默认值为所绑定服务实例的名称。
高级 RabbitMQ 配置
上面的内容展示了如何配置一个非常简单的 RabbitMQ 连接工厂,而当 Cloud Foundry 在运行时实际创建工厂时,会使用最为常用的配置选项。但是,您可以使用 &cloud:rabbit-connection-factory& 的 &cloud:rabbit-options& 子元素来更改其中的某些配置选项。
&cloud:rabbit-options& 子元素可以定义一个名为 channel-cache-size 的属性,您可以将该属性设置为指定通道缓存的大小。默认值为 1。
下面的示例展示了如何使用这些高级 RabbitMQ 配置选项:
&cloud:rabbit-connection-factory id="rabbitConnectionFactory" &
&cloud:rabbit-options channel-cache-size="10" /&
&/cloud:rabbit-connection-factory&
在前面的示例中,RabbitMQ 连接工厂的通道缓存大小设置为 10。
&cloud:service-scan&
&cloud:service-scan& 元素会扫描绑定到应用程序的所有服务,并为每个含有 @org.springframework.beans.factory.annotation.Autowired 注释的服务创建适当类型的 Spring Bean。&cloud:service-scan& 元素会作为核心 Spring 中 &context:component-scan& 的云等效组件,它可扫描 CLASSPATH 以寻找含有特定注释的 Bean 并为每个注释创建一个 Bean。
在开发应用程序的初始阶段,&cloud:service-scan& 非常有用,因为您可以直接访问服务 Bean,而无需将 &cloud:& 元素直接添加到您绑定每个新服务的 Spring 应用程序上下文文件中。
&cloud:service-scan& 元素不含任何属性或子元素,例如:
&cloud:service-scan /&
在您的 Java 代码中,您必须使用 @Autowired 注释每个依赖项,从而自动为相应服务创建一个 Bean。例如:
package cf.examples;
import org.springframework.beans.factory.annotation.Autowired;
@Autowired DataSource dataSource;
@Autowired ConnectionFactory rabbitConnectionFactory;
@Autowired RedisConnectionFactory redisConnectionFactory;
@Autowired MongoDbFactory mongoDbFactory;
如果对于每个服务类型您都只绑定了一个 服务到您的应用程序,那么仅使用 @Autowired 注释就已足够。如果您绑定了多个服务(例如,您将两个不同的 MySQL 服务实例绑定到同一应用程序),那您必须使用 @Qualifier 注释来将 Spring Bean 与特定服务示例匹配。
例如,假设您将两个 MySQL 服务(名为 inventory-db 和 pricing-db)绑定到您的应用程序,请按照下面的示例使用 @Qualifier 注释指定将哪个服务实例应用到哪个 Spring Bean:
@Autowired @Qualifier("inventory-db") DataSource inventoryDataSource;
@Autowired @Qualifier("pricing-db") DataSource pricingDataSource;
&cloud:properties&
&cloud:properties& 元素会将有关应用程序及其绑定服务的基本信息作为属性公开。然后,您的应用程序就可以通过 Spring 属性占位符支持来使用这些属性。
&cloud:properties& 元素仅有一个属性 (id),它可指定 Properties Bean 的名称。将此 ID 作为对 &context:property-placeholder& 的引用,您可以将它用于保留所有由 Cloud Foundry 公开的属性。然后,您就可以在您的其他 Bean 定义中使用此属性了。
注意,如果您使用的是 Spring Framework 3.1(或更高版本),这些属性将自动变为可用状态,而无需在您的应用程序上下文文件中添加 &cloud:properties&。
下面的示例展示了如何在您的 Spring 应用程序上下文文件中使用此元素:
&cloud:properties id="cloudProperties" /&
&context:property-placeholder properties-ref="cloudProperties" /&
&bean class="com.mchange.boPooledDataSource"&
&property name="user"
value="${cloud.services.mysql.connection.username}" /&
在前面的示例中,cloud.services.mysql.connection.username 是一个由 Cloud Foundry 公开的属性。
有关由 Cloud Foundry 公开的属性的完整列表以及更加详细的示例,请参见。
RabbitMQ 和 Spring:其他编程信息
本部分提供了有关在您部署到 Cloud Foundry 的 Spring 应用程序中使用 RabbitMQ 的其他信息。本部分并不能作为 RabbitMQ 和 Spring 的完整教程,有关 RabbitMQ 和 Spring 的完整教程,请参见以下资源:
,包含有关在您的应用程序中创建消息的基本内容。
、和 RabbitMQ
RabbitMQ 服务可通过 (版本 0.8 和 0.9.1)访问,您的应用程序将需要访问 AMQP 客户端库才能使用此服务。Spring AMQP 项目允许 AMQP 应用程序使用 Spring 结构构建。
下面的示例 pom.xml 文件展示了除上面描述的 cloudfoundry-runtime 依赖项以外的 RabbitMQ 依赖项和代码库:
&repositories&
&repository&
&id&org.springframework.maven.milestone&/id&
&name&Spring Maven Milestone Repository&/name&
&url&http://maven.springframework.org/milestone&/url&
&snapshots&
&enabled&false&/enabled&
&/snapshots&
&/repository&
&/repositories&
&dependency&
&groupId&cglib&/groupId&
&artifactId&cglib-nodep&/artifactId&
&version&2.2&/version&
&/dependency&
&dependency&
&groupId&org.springframework.amqp&/groupId&
&artifactId&spring-rabbit&/artifactId&
&version&1.0.0.RC2&/version&
&/dependency&
&dependency&
&groupId&org.cloudfoundry&/groupId&
&artifactId&cloudfoundry-runtime&/artifactId&
&version&0.7.1&/version&
&/dependency&
然后按下面的方法更新您的应用程序控制器/逻辑:
添加消息传送库:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.amqp.core.AmqpTemplate;
按照下面的 Java 代码段读写消息:
@Controller
public class HomeController {
@Autowired AmqpTemplate amqpTemplate;
@RequestMapping(value = "/")
public String home(Model model) {
model.addAttribute(new Message());
return "WEB-INF/views/home.jsp";
@RequestMapping(value = "/publish", method=RequestMethod.POST)
public String publish(Model model, Message message) {
// Send a message to the "messages" queue
amqpTemplate.convertAndSend("messages", message.getValue());
model.addAttribute("published", true);
return home(model);
@RequestMapping(value = "/get", method=RequestMethod.POST)
public String get(Model model) {
// Receive a message from the "messages" queue
String message = (String)amqpTemplate.receiveAndConvert("messages");
if (message != null)
model.addAttribute("got", message);
model.addAttribute("got_queue_empty", true);
return home(model);
使用 Spring 配置文件有条件地设置 Cloud Foundry 配置
前面的部分说明了如何使用 &cloud:& 命名空间轻松为部署到 Cloud Foundry 的 Spring 应用程序配置服务(例如数据源和 RabbitMQ 连接工厂)。但是,您可能并不总是希望将您的应用程序部署到 Cloud Foundry,例如,您有时可能希望使用本地环境在迭代开发的过程中测试应用程序。在这种情况下,有条件地设置 应用程序配置就非常有用,因为这样仅在特定条件为 true 时才会激活特定片段。有条件地设置应用程序配置使您的应用程序能够方便地适用于很多不同的环境,这样您就不需要在将应用程序部署到您的本地环境(甚至是 Cloud Foundry)时手动更改配置。要启用此功能,请使用 Spring Framework 3.1 或更高版本中的 Spring配置文件 功能。
基本理念是使用适当 Spring 应用程序上下文文件中的嵌套 &beans& 元素的 profile 属性为特定环境的配置分组。您可以创建自己的自定义配置文件,但与 Cloud Foundry 的上下文最相关的配置文件是 default 和 cloud。
当您将 Spring 应用程序部署到 Cloud Foundry 时,Cloud Foundry 会自动启用 cloud 配置文件。这样,特定于 Cloud Foundry 的应用程序配置就可以有一个预定义的方便位置。然后,对 cloud 配置文件块内的 &cloud:& 命名空间的所有具体使用情况进行分组,以使应用程序可以在 Cloud Foundry 环境外运行。如果您将应用程序部署到非 Cloud Foundry 环境,那么在完成上述步骤之后,使用 default 配置文件(或自定义配置文件)来分组将使用的非 Cloud Foundry 配置。
下面的示例显示,Spring MongoTemplate 用两个连接工厂中的数据加以填充,这两个连接工厂是分别采用两种备选配置方式中的一种进行配置的。在 Cloud Foundry 上运行时(cloud 配置文件),连接工厂是自动配置的。不在 Cloud Foundry 上运行时(default 配置文件),连接工厂是使用运行 MongoDB 实例的连接设置手动配置的。
&?xml version="1.0" encoding="UTF-8"?&
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
http://schema.cloudfoundry.org/spring
http://schema.cloudfoundry.org/spring/cloudfoundry-spring.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd"&
&bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"&
&constructor-arg ref="mongoDbFactory" /&
&beans profile="default"&
&mongo:db-factory id="mongoDbFactory" dbname="pwdtest" host="127.0.0.1" port="27017" username="test_user" password="efgh" /&
&beans profile="cloud"&
&cloud:mongo-db-factory id="mongoDbFactory" /&
注意,&beans profile="value"& 元素嵌套于标准根 &beans& 元素中。cloud 配置文件中的 MongoDB 连接工厂使用 &cloud:& 命名空间,default 配置文件中的连接工厂配置使用 &mongo:& 命名空间。您现在就可以将此应用程序部署到两个不同的环境中,而无需在从一个环境转换到另一个环境时手动更改其配置。
有关使用 Spring 配置文件(Spring Framework 3.1 中的新功能)的详细信息,请参见。
通过部署的 Spring 应用程序向 Cloud Foundry 发送电子邮件
为了防范垃圾邮件及其他滥用行为,会阻止从在 Cloud Foundry 上运行的应用程序发送的 SMTP 消息。但是,如本部分中所述,如果将您的应用程序部署到 Cloud Foundry,应用程序仍可以发送电子邮件。
服务提供商(如 )可以通过 HTTP Web 服务代表您发送电子邮件,当您将应用程序部署到 Cloud Foundry 时就可使用这种方法。但是,如果您的应用程序也在数据中心中运行,在这种情况下,您可能希望使用公司的 SMTP 服务器。这是一个使用 有条件地设置应用程序发送电子邮件方式的示例,具体取决于部署应用程序的具体位置。这使您的应用程序能够方便地适用于不同的环境,而无需手动更新其配置。
以下 Spring 应用程序上下文的代码段展示了如何指定当应用程序在 Cloud Foundry 中运行时,您的应用程序应使用 SendGrid 发送电子邮件;说明了 cloud 配置文件的使用情况:
&?xml version="1.0" encoding="UTF-8"?&
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cloud="http://schema.cloudfoundry.org/spring"
&beans profile="cloud"&
&bean name="mailSender" class="example.SendGridMailSender"&
&property name="apiUser" value="" /&
&property name="apiKey" value="secureSecret" /&
&!-- additional beans in your application --&
在此示例中,example.SendGridMailSender 是使用 SendGrid 服务提供商发送电子邮件的 Spring Bean,但是,此 Bean 仅在将应用程序部署到 Cloud Foundry 时才激活。如果您的应用程序实际在数据中心运行,则使用您的默认电子邮件服务器。
访问 Cloud Foundry 属性
Cloud Foundry 会将很多应用程序和服务属性直接公开到其部署的应用程序中。您部署的应用程序可以依次使用这些属性。这些由 Cloud Foundry 公开的属性包含有关该应用程序的基本信息(例如其名称和云提供商),以及当前绑定到该应用程序的所有服务的详细连接信息。
服务属性通常会使用以下任一格式:
cloud.services.{service-name}.connection.{property}
cloud.services.{service-name}.{property}
其中,{service-name} 引用您在部署时将服务绑定到应用程序所使用的服务名称。可以使用的特定连接属性取决于服务类型,请参见本部分结尾的表格,获取完整列表。
例如,假设在 VMC 中,您创建了一个名为 my-postgres 的 vFabric Postgres 服务,然后将它绑定到您的应用程序,Cloud Foundry 会公开有关此服务的以下属性,您的应用程序可以依次使用这些属性:
cloud.services.my-postgres.connection.host
cloud.services.my-postgres.connection.hostname
cloud.services.my-postgres.connection.name
cloud.services.my-postgres.connection.password
cloud.services.my-postgres.connection.port
cloud.services.my-postgres.connection.user
cloud.services.my-postgres.connection.username
cloud.services.my-postgres.plan
cloud.services.my-postgres.type
放了方便起见,如果只有一项 属于给定类型的服务绑定到应用程序,Cloud Foundry 将根据服务类型而非服务名称来创建别名。例如,如果只有一项 MySQL 服务绑定到应用程序,属性将使用格式 cloud.services.mysql.connection.{property}。在这种情况下,Cloud Foundry 将使用以下别名:
postgresql
如果您希望在应用程序中使用这些 Cloud Foundry 属性,请使用 cloud 配置文件中的 Spring 属性占位符;有关 Spring 配置文件的信息,在中进行了简要说明。
例如,假设您将一个名为 spring-mysql 的 MySQL 服务绑定到您的应用程序,但您的应用程序需要 c3p0 连接池而非 Cloud Foundry 提供的连接池。但是,您仍希望为 MySQL 服务使用由 Cloud Foundry 定义的相同连接属性,特别是 username、password 和 JDBC URL。下面的 Spring 应用程序上下文代码段展示了如何实现此目标:
&beans profile="cloud"&
&bean id="c3p0DataSource" class="com.mchange.boPooledDataSource" destroy-method="close"&
&property name="driverClass" value="com.mysql.jdbc.Driver" /&
&property name="jdbcUrl"
value="jdbc:mysql://${cloud.services.spring-mysql.connection.host}:${cloud.services.spring-mysql.connection.port}/${cloud.services.spring-mysql.connection.name}" /&
&property name="user" value="${cloud.services.spring-mysql.connection.username}" /&
&property name="password" value="${cloud.services.spring-mysql.connection.password}" /&
下表列出了 Cloud Foundry 向部署应用程序公开的所有应用程序和服务属性。在属性名称中,{service-name} 引用了绑定服务的实际名称。
相关服务类型
cloud.application.name
应用程序的名称。
cloud.provider.url
托管您的应用程序的云的 URL,例如 <。
cloud.services.{service-name}.connection.db
Cloud Foundry 创建的数据库的名称。
cloud.services.{service-name}.connection.host
运行 MongoDB 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.hostname
运行 MongoDB 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.name
连接到 MongoDB 数据库的用户的名称。
cloud.services.{service-name}.connection.password
连接到 MongoDB 数据库的用户的密码。
cloud.services.{service-name}.connection.port
MongoDB 服务器的侦听端口。
cloud.services.{service-name}.connection.username
连接到 MongoDB 数据库的用户的名称。
cloud.services.{service-name}.plan
服务的支付方案,例如免费。
cloud.services.{service-name}.type
MongoDB 服务器的名称和版本。
cloud.services.{service-name}.connection.name
Cloud Foundry 创建的 MySQL 数据库的名称。
cloud.services.{service-name}.connection.host
运行 MySQL 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.hostname
运行 MySQL 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.port
MySQL 服务器的侦听端口。
cloud.services.{service-name}.connection.user
连接到 MySQL 数据库的用户的名称。
cloud.services.{service-name}.connection.username
连接到 MySQL 数据库的用户的名称。
cloud.services.{service-name}.connection.password
连接到 MySQL 数据库的用户的密码。
cloud.services.{service-name}.plan
服务的支付方案,例如免费。
cloud.services.{service-name}.type
MySQL 服务器的名称和版本。
cloud.services.{service-name}.connection.name
vFabric Postgres
Cloud Foundry 创建的 vFabric Postgres 数据库的名称。
cloud.services.{service-name}.connection.host
vFabric Postgres
运行 vFabric Postgres 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.hostname
vFabric Postgres
运行 vFabric Postgres 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.port
vFabric Postgres
vFabric Postgres 服务器的侦听端口。
cloud.services.{service-name}.connection.user
vFabric Postgres
连接到 vFabric Postgres 数据库的用户的名称。
cloud.services.{service-name}.connection.username
vFabric Postgres
连接到 vFabric Postgres 数据库的用户的名称。
cloud.services.{service-name}.connection.password
vFabric Postgres
连接到 vFabric Postgres 数据库的用户的密码。
cloud.services.{service-name}.plan
vFabric Postgres
服务的支付方案,例如免费。
cloud.services.{service-name}.type
vFabric Postgres
vFabric Postgres 服务器的名称和版本。
cloud.services.{service-name}.connection.url
用于连接到 AMPQ 代理的 URL。URL 包括主机、端口、用户名等。
cloud.services.{service-name}.plan
服务的支付方案,例如免费。
cloud.services.{service-name}.type
RabbitMQ 服务器的名称和版本。
cloud.services.{service-name}.connection.host
运行 Redis 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.hostname
运行 Redis 服务器的主机的名称或 IP 地址。
cloud.services.{service-name}.connection.port
Redis 服务器的侦听端口。
cloud.services.{service-name}.connection.name
连接到 Redis 数据库的用户的名称。
cloud.services.{service-name}.connection.password
连接到 Redis 数据库的用户的密码。
cloud.services.{service-name}.plan
服务的支付方案,例如免费。
cloud.services.{service-name}.type
Redis 服务器的名称和版本。}

我要回帖

更多关于 spring cloud 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信