Spring Cloud 簡介

工程 | Ramnivas Laddad | 2014 年 6 月 3 日 | ...

開發、部署和運作雲端應用程式應該要像(甚至比)本地應用程式一樣簡單。這應該是任何雲端平台、函式庫或工具背後的主導原則。Spring Cloud—一個開放原始碼函式庫—讓開發雲端的 JVM 應用程式變得容易。有了它,應用程式可以輕鬆地連接到服務,並在多個雲端(例如 Cloud Foundry 和 Heroku)中發現有關雲端環境的資訊。此外,您可以將其擴展到其他雲端平台和新服務。

在本部落格(系列文章的第一篇)中,我將介紹 Spring Cloud,並從應用程式開發人員的角度展示其用法。我們將開發一個簡單的應用程式,並將其部署到 Cloud FoundryHeroku。在後續的部落格中,我將探討其擴展性。

Spring Cloud 簡介

在雲端中執行應用程式的眾多優勢之一是各種服務的易於取得。您只需點擊一個按鈕或一個 shell 命令,即可建立和綁定服務,而無需管理硬體、安裝、操作、備份等。

應用程式如何存取這些服務?例如,如果您有一個關聯式資料庫綁定到您的應用程式,您將需要根據該服務建立一個 DataSource 物件。這就是 Spring Cloud 的用武之地。它消除了存取和配置服務連接器所需的所有工作,讓您可以專注於使用這些服務。它還公開了應用程式實例資訊(主機位址、埠號、名稱等)。

Spring Cloud 以獨立於雲端的方式透過雲端連接器(Cloud Connector)的概念來完成所有這些工作。雖然它提供了 Cloud Foundry 和 Heroku 的實作,但您(或雲端供應商)可以透過實作介面並利用函式庫的其餘部分將其擴展到其他雲端。然後,只需將包含擴展的函式庫新增到應用程式的類別路徑中即可;無需 fork 和建置 Spring Cloud。

Spring Cloud 也意識到它不可能滿足每個雲端上的每項服務。因此,在開箱即用地支援許多常見服務的同時,它允許您(或服務供應商)將其功能擴展到其他服務。正如擴展到其他雲端一樣,您只需將包含服務擴展的 jar 檔案新增到應用程式的類別路徑中即可。

最後,它針對 Spring 應用程式(在一個單獨的模組中)提供了特殊支援,包括 Spring Boot 應用程式,形式為 Java 和 XML 配置支援,並以易於使用的方式公開應用程式和服務屬性。這是 Spring Cloud 中唯一依賴 Spring 的模組。其他框架供應商可以類似的方式為其框架貢獻特定的支援。

讓我們看看 Spring Cloud 的實際運作情況。

Spring Cloud 實戰

我們將從一個基於 Spring Boot 的簡單應用程式(原始碼)開始(傳統的 Spring MVC 應用程式也可以正常運作,儘管會犧牲更多的設定程式碼)。該應用程式包含一個控制器,其中注入了代表綁定服務的 bean,以及一個首頁,用於列印有關綁定到應用程式的服務的資訊。

@Controller
public class HomeController {
    @Autowired(required = false) DataSource dataSource;
    @Autowired(required = false) RedisConnectionFactory redisConnectionFactory;
    @Autowired(required = false) MongoDbFactory mongoDbFactory;
    @Autowired(required = false) ConnectionFactory rabbitConnectionFactory;
    
    @Autowired ApplicationInstanceInfo instanceInfo;

    @RequestMapping("/")
    public String home(Model model) {
        Map<Class<?>, String> services = new LinkedHashMap<Class<?>, String>();
        services.put(dataSource.getClass(), toString(dataSource));
        services.put(mongoDbFactory.getClass(), toString(mongoDbFactory));
        services.put(redisConnectionFactory.getClass(), toString(redisConnectionFactory));
        services.put(rabbitConnectionFactory.getClass(), toString(rabbitConnectionFactory));
        model.addAttribute("services", services.entrySet());
        
        model.addAttribute("instanceInfo", instanceInfo);
        
        return "home";
    }
 
    // ... various toString() methods to create a string representation for each service
 
}

HomeController 具有四個注入的依賴項,分別代表可能綁定到應用程式的服務,以及另一個用於 ApplicationInstanceInfo 的依賴項。 "/" 路由將代表每個服務的字串及其類別新增到模型中,以及實例資訊。相關視圖 呈現所有這些資訊。

對於配置,我們新增 CloudConfig 如下所示

@Configuration
@ServiceScan
@Profile("cloud")
public class CloudConfig extends AbstractCloudConfig {
    @Bean
    public ApplicationInstanceInfo applicationInfo() {
        return cloud().getApplicationInstanceInfo();
    }
}

該類別擴展了 AbstractCloudConfig,這是將 Java 配置方法與 Spring Cloud 一起使用的方式。我們設定 @Profile(“cloud”) 以確保此配置僅在雲端環境中載入。 @ServiceScan 註解掃描所有綁定服務,並為每個服務建立一個 bean(然後將其自動注入到 HomeController 中)。如果您想知道 @ComponentScan@ServiceScan 之間的相似之處,您是對的。前者掃描可以實例化為 bean 的候選類別,而後者掃描綁定服務。我們還建立一個與應用程式實例資訊對應的 bean。

將應用程式部署到 Cloud Foundry

我們包含以下 manifest.yml,它綁定了我們為了說明目的所需的所有四個服務(您需要使用 cf create-service 命令建立這些服務)

---
applications:
- name: hello-spring-cloud
  memory: 512M
  instances: 1
  host: hello-spring-cloud-${random-word}
  domain: cfapps.io
  path: target/hello-spring-cloud-0.0.1-SNAPSHOT.jar
  services:
	- postgres-service
	- amqp-service
	- mongodb-service
	- redis-service
  env:
    SPRING_PROFILES_DEFAULT: cloud

現在我們只需要建置並推送

$ mvn package
$ cf push
``` 
Now when we visit the page, we see information about all four services:

![Application deployed on Cloud Foundry](https://raw.githubusercontent.com/cloudfoundry-samples/hello-spring-cloud/gh-pages/img/hello-spring-cloud-cf.png)

In a real app, you probably would inject these services into service beans and do something more useful that printing their connection information! Please head over to http://projects.spring.io/spring-cloud to see a list of sample apps that do more interesting things with Spring Cloud. Speaking of https://spring.dev.org.tw, it too uses Spring Cloud underneath.

# Deploying it to Heroku

We can deploy the same application to Heroku. We need to add a couple of files (neither are specific to Spring Cloud): `system.properties` to make Heroku use Java 7 and `Procfile` to make it execute the right command to start the application and enable the `cloud` profile. We push the application to Heroku as follows:

```sh 
$ heroku apps:create
$ heroku addons:add mongolab
$ heroku addons:add rediscloud
$ heroku addons:add cloudamqp
$ heroku config:set SPRING_CLOUD_APP_NAME=hello-spring-cloud
$ git push heroku master
```

Here we create add-ons (similar to Cloud Foundry services) for a MongoDb, Redis, and AMQP service provider. Heroku automatically provisions a Postgres service, therefore we don’t need to explicitly add it. Heroku app’s environment, unlike Cloud Foundry, doesn’t expose the app name, so we use `heroku config:set` to explicitly set it (if not, Spring Cloud will set it to `<unknown>`). There are a few other differences in how Spring Cloud adapts to differences between these two clouds; we will cover those in a later blog.
 
That’s all we need to do. When we visit our application, it shows all services info much the same way it did on Cloud Foundry.

![Application deployed on Heroku](https://raw.githubusercontent.com/cloudfoundry-samples/hello-spring-cloud/gh-pages/img/hello-spring-cloud-heroku.png)


# Taking some control
The use of `@ServiceScan` made it easy to grab all services and start using them. But in practice, you often need more control over creating a service connector such as setting their pooling parameters. If that is the case, you can use Spring Cloud’s Java Config or XML config support. Let’s change the `CloudConfig` class as follows:

```java
@Configuration
@Profile("cloud")
public class CloudConfig extends AbstractCloudConfig {
    @Bean
    public ConnectionFactory rabbitConnectionFactory() {
    	return connectionFactory().rabbitConnectionFactory();
    }
   
    @Bean
    public DataSource dataSource() {
    	return connectionFactory().dataSource();
    }
 
    @Bean
    public MongoDbFactory mongoDb() {
    	return connectionFactory().mongoDbFactory();
    }
 
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
    	return connectionFactory().redisConnectionFactory();
    }

    @Bean
    public ApplicationInstanceInfo applicationInfo() {
        return cloud().getApplicationInstanceInfo();
    }
}
```

Compared to the first version, we removed the `@ServiceScan` annotation. Instead, we use the API exposed by `AbstractCloudConfig` to create beans for each of the services. For now, the beans created this way are identical to that created by `@ServiceScan`, but we now have a possibility of configuring it further. For example, if we wanted to bind the `DataSource` bean to a specific service (presumably among many bound to the app) and initialize it with a specific pool configuration, we can make the following change:
 
```java
@Bean
public DataSource dataSource() {
    PoolConfig poolConfig = new PoolConfig(20, 200);
    ConnectionConfig connectionConfig =
        new ConnectionConfig("sessionVariables=sql_mode='ANSI';characterEncoding=UTF-8");
    DataSourceConfig serviceConfig = 
        new DataSourceConfig(poolConfig, connectionConfig);
     return connectionFactory().dataSource("my-service", serviceConfig);
}
``` 
The `DataSource` created this way will have max pool size of 20 and max wait time of 200 milliseconds along with a specific connection property string.

# Summary
Spring Cloud abstracts connecting to cloud services and makes it possible to have the same application deployed to multiple clouds with little extra effort. In this blog we merely scratched the surface of what Spring Cloud offers. In the next blog, we will explore more about the Java and XML config as well as how you can use its core API in non-spring apps. In the blogs that follow we will dive deeper into the extensibility angle of Spring Cloud.
 

取得 Spring 電子報

隨時掌握 Spring 電子報的最新資訊

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

瞭解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

瞭解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部