Spring Cloud Finchley版-02-構建分佈式應用

 

服務消費者 & 提供者

本書使用服務提供者與服務消費者來描述微服務之間的調用關係。下表解釋了服務提供者與服務消費者。
表-服務提供者與服務消費者

[td]

名詞
定義
服務提供者 服務的被調用方(即:爲其他服務提供服務的服務)
服務消費者 服務的調用方(即:依賴其他服務的服務)
以電影售票系統爲例。如圖,用戶向電影微服務發起了一個購票的請求。在進行購票的業務操作前,電影微服務需要調用用戶微服務的接口,查詢當前用戶的餘額是多少、是不是符合購票標準等。在這種場景下,用戶微服務就是一個服務提供者,電影微服務則是一個服務消費者。
圍繞該場景,先來編寫一個用戶微服務,然後編寫一個電影微服務。
TIPS
服務消費者和服務提供者描述的只是微服務之間的調用關係,一般成對出現。例如本文,用戶微服務是是電影微服務的服務提供者,電影微服務是用戶微服務的服務消費者。很多初學者和筆者交流時,會描述提供者如何如何……彷彿消費者和提供者是微服務的固有屬性,這是不對的——例如A調用B,B調用C,那麼B相對A就是提供者,B相對C就消費者。

Spring Boot/Spring Cloud應用開發套路

Spring Boot/Spring Cloud時代後,應用開發基本遵循三板斧
  • 加依賴
  • 加註解
  • 寫配置
至於你的業務代碼,該怎麼寫還怎麼寫。
TIPS
對於懶人,可使用Spring Initilizr(IDEA、Spring Tool Suite等IDE上均有集成,也可在http://start.spring.io 使用網頁版)創建應用,它會給你生成項目的依賴以及項目的骨架。後續,筆者會以番外的形式更新相關教程。

編寫服務提供者【用戶微服務】

1 創建一個Maven項目,依賴如下:
  • <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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  •   <modelVersion>4.0.0</modelVersion>
  •   <groupId>com.itmuch.cloud</groupId>
  •   <artifactId>microservice-simple-provider-user</artifactId>
  •   <version>0.0.1-SNAPSHOT</version>
  •   <packaging>jar</packaging>
  •   <!-- 引入spring boot的依賴 -->
  •   <parent>
  •     <groupId>org.springframework.boot</groupId>
  •     <artifactId>spring-boot-starter-parent</artifactId>
  •     <version>2.0.7.RELEASE</version>
  •   </parent>
  •   <properties>
  •     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  •     <java.version>1.8</java.version>
  •   </properties>
  •   <dependencies>
  •     <dependency>
  •       <groupId>org.springframework.boot</groupId>
  •       <artifactId>spring-boot-starter-web</artifactId>
  •     </dependency>
  •     <dependency>
  •       <groupId>org.springframework.boot</groupId>
  •       <artifactId>spring-boot-starter-data-jpa</artifactId>
  •     </dependency>
  •     <!-- 引入H2數據庫,一種內嵌的數據庫,語法類似MySQL -->
  •     <dependency>
  •       <groupId>com.h2database</groupId>
  •       <artifactId>h2</artifactId>
  •     </dependency>
  •     <!-- 引入Lombok -->
  •     <dependency>
  •       <groupId>org.projectlombok</groupId>
  •       <artifactId>lombok</artifactId>
  •       <optional>true</optional>
  •     </dependency>
  •   </dependencies>
  •   <!-- 引入spring cloud的依賴,不能少,主要用來管理Spring Cloud生態各組件的版本 -->
  •   <dependencyManagement>
  •     <dependencies>
  •       <dependency>
  •         <groupId>org.springframework.cloud</groupId>
  •         <artifactId>spring-cloud-dependencies</artifactId>
  •         <version>Finchley.SR2</version>
  •         <type>pom</type>
  •         <scope>import</scope>
  •       </dependency>
  •     </dependencies>
  •   </dependencyManagement>
  •   <!-- 添加spring-boot的maven插件,不能少,打jar包時得用 -->
  •   <build>
  •     <plugins>
  •       <plugin>
  •         <groupId>org.springframework.boot</groupId>
  •         <artifactId>spring-boot-maven-plugin</artifactId>
  •       </plugin>
  •     </plugins>
  •   </build>
  • </project>
其中, spring-boot-starter-web 提供了Spring MVC的支持; spring-boot-starter-data-jpa 提供了Spring Data JPA的支持; h2 是一種內嵌的數據庫,語法和MySQL類似(筆者實在沒有動力爲了簡單的演示再寫一大堆內容去演示怎麼安裝MySQL數據庫); lombok 則是一款開發利器,可以幫助你簡化掉N多冗餘代碼。
WARNING
TIPS
2 創建實體類:
  • @Entity
  • @Data
  • @NoArgsConstructor
  • @AllArgsConstructor
  • public class User {
  •   @Id
  •   @GeneratedValue(strategy = GenerationType.AUTO)
  •   private Long id;
  •   @Column
  •   private String username;
  •   @Column
  •   private String name;
  •   @Column
  •   private Integer age;
  •   @Column
  •   private BigDecimal balance;
  • }
3 創建DAO:
  • @Repository
  • public interface UserRepository extends JpaRepository<User, Long> {
  • }
4 創建Controller:
  • @RequestMapping("/users")
  • @RestController
  • public class UserController {
  •   @Autowired
  •   private UserRepository userRepository;
  •   @GetMapping("/{id}")
  •   public Optional<User> findById(@PathVariable Long id) {
  •     return this.userRepository.findById(id);
  •   }
  • }
其中, @GetMapping,是Spring 4.3提供的新註解。它是一個組合註解,等價於 @RequestMapping(method=RequestMethod.GET),用於簡化開發。同理還有 @PostMapping、 @PutMapping、 @DeleteMapping、 @PatchMapping等。
5 編寫啓動類:
  • @SpringBootApplication
  • public class ProviderUserApplication {
  •   public static void main(String[] args) {
  •     SpringApplication.run(ProviderUserApplication.class, args);
  •   }
  •   /**
  •    * 初始化用戶信息
  •    * 注:Spring Boot2不能像1.x一樣,用spring.datasource.schema/data指定初始化SQL腳本,否則與actuator不能共存
  •    * 原因詳見:
  •    *
  •    * @param repository repo
  •    * @return runner
  •    */
  •   @Bean
  •   ApplicationRunner init(UserRepository repository) {
  •     return args -> {
  •       User user1 = new User(1L, "account1", "張三", 20, new BigDecimal(100.00));
  •       User user2 = new User(2L, "account2", "李四", 28, new BigDecimal(180.00));
  •       User user3 = new User(3L, "account3", "王五", 32, new BigDecimal(280.00));
  •       Stream.of(user1, user2, user3)
  •         .forEach(repository::save);
  •     };
  •   }
  • }
@SpringBootApplication是一個組合註解,它整合了 @Configuration、 @EnableAutoConfiguration和 @ComponentScan註解,並開啓了Spring Boot程序的組件掃描和自動配置功能。在開發Spring Boot程序的過程中,常常會組合使用 @Configuration、 @EnableAutoConfiguration和 @ComponentScan等註解,所以Spring Boot提供了 @SpringBootApplication,來簡化開發。
在啓動時,我們使用了 ApplicationRunnerinit(UserRepositoryrepository) 初始化了三條數據,分別是張三、李四、王五。 @Bean 則是一個方法註解,作用是實例化一個Bean並使用該方法的名稱命名。類似於XML配置方式的 <beanid="init"class="...ApplicationRunner"/> 。
6 編寫配置文件 application.yml :
  • server:
  •   # 指定Tomcat端口
  •   port: 8000
  • spring:
  •   jpa:
  •     # 讓hibernate打印執行的SQL
  •     show-sql: true
  • logging:
  •   level:
  •     root: INFO
  •     # 配置日誌級別,讓hibernate打印出執行的SQL參數
  •     org.hibernate: INFO
  •     org.hibernate.type.descriptor.sql.BasicBinder: TRACE
  •     org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
傳統Web應用開發中,常使用properties格式文件作爲配置文件。Spring Boot以及Spring Cloud支持使用properties或者yml格式的文件作爲配置文件。
yml文件格式是YAML(Yet Another Markup Language)編寫的文件格式,YAML和properties格式的文件可互相轉換,例如本節中的application.yml,就等價於如下的properties文件:
  • server.port=8000
  • spring.jpa.show-sql=true
  • logging.level.root=INFO
  • logging.level.org.hibernate=INFO
  • logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
  • logging.level.org.hibernate.type.descriptor.sql.BasicExtractor=TRACE
從中不難看出,YAML比properties結構清晰;可讀性、可維護性也更強,並且語法非常簡潔。因此,本書使用YAML格式作爲配置文件。但,yml有嚴格的縮進,並且key與value之間使用 : 分隔,冒號後的空格不能少,請大家注意

測試

訪問 http://localhost:8000/users/1 ,可獲得結果:
  • {"id":1,"username":"account1","name":"張三","age":20,"balance":100.00}

編寫服務消費者【電影微服務】

我們已經編寫了一個服務提供者(用戶微服務),本節來編寫一個服務消費者(電影微服務)。該服務非常簡單,它使用RestTemplate調用用戶微服務的API,從而查詢指定id的用戶信息。
1 創建一個Maven項目,ArtifactId是 microservice-simple-consumer-movie 。
2 加依賴:
  • <dependency>
  •   <groupId>org.springframework.boot</groupId>
  •   <artifactId>spring-boot-starter-web</artifactId>
  • </dependency>
3 創建實體類:
  • @Data
  • @AllArgsConstructor
  • @NoArgsConstructor
  • public class User {
  •   private Long id;
  •   private String username;
  •   private String name;
  •   private Integer age;
  •   private BigDecimal balance;
  • }
4 創建啓動類:
  • @SpringBootApplication
  • public class ConsumerMovieApplication {
  •   @Bean
  •   public RestTemplate restTemplate() {
  •     return new RestTemplate();
  •   }
  •   public static void main(String[] args) {
  •     SpringApplication.run(ConsumerMovieApplication.class, args);
  •   }
  • }
5 創建Controller:
  • @RequestMapping("/movies")
  • @RestController
  • public class MovieController {
  •   @Autowired
  •   private RestTemplate restTemplate;
  •   @GetMapping("/users/{id}")
  •   public User findById(@PathVariable Long id) {
  •     // 這裏用到了RestTemplate的佔位符能力
  •     User user = this.restTemplate.getForObject("http://localhost:8000/users/{id}", User.class, id);
  •     // ...電影微服務的業務...
  •     return user;
  •   }
  • }
由代碼可知,Controller使用RestTemplate調用用戶微服務的RESTful API。
6 編寫配置文件 application.yml :
  • server:
  •   port: 8010
拓展閱讀
本文使用RestTemplate實現了基於HTTP的遠程調用,事實上,Spring 5開始,WebFlux提供了Reactive的Web Client: WebClinet ,使用方式和RestTemplate基本類似,但性能更強,吞吐更好。有興趣的可前往https://docs.spring.io/spring/do ... flux-client-builder 瞭解。在這裏,筆者對WebClient做了一些簡單的封裝,也可關注:https://github.com/itmuch/thor-t ... /WebClientUtil.java

測試

訪問: http://localhost:8010/movies/users/1 ,結果如下:
  • {"id":1,"username":"account1","name":"張三","age":20,"balance":100.00}

存在的問題

至此,我們已經實現了這個最簡單的分佈式應用,應用之間通過HTTP通信。代碼非常簡單,但這些簡單的代碼裏,存在着若干問題:
1 應用沒有監控,沒有畫板,一切指標都沒有。在這個Growth Hack逐漸成爲主流的時代,不弄個Dashboard把系統壓力、QPS、CPU、內存、日活啥的可視化,你好意思出來混嗎……
2 地址硬編碼問題——電影微服務中將用戶微服務的地址寫死,如果用戶微服務地址發生變化,難道要重新上線電影微服務嗎?
你可能會質疑:用戶微服務地址爲什麼會變,讓它保持不變就行了啊,這不是問題。這裏舉兩個例子:
例1:如果你用Docker,那麼地址幾乎每次啓動都會變……
例2:你之前用的是TXYun,後來你想把用戶微服務遷移到Aliyun。這個時候IP就會發生變化。我相信你不會樂意找到哪些服務調用了用戶微服務的接口,然後所有調用用戶微服務的服務統一修改地址……
3 負載均衡如何考慮?難道得在電影微服務和用戶微服務之間加個NGINX做負載均衡嗎?聽起來是可行的,但如果有10000+服務(這並不誇張,我司的微服務數目是這個數字乘以N,N >= m,哈哈哈)那這個NGINX的配置得有多複雜……
4 服務之間沒有容錯機制,相信對技術有激情的你已經不止一次聽過容錯、降級、fallback、回退之類的詞彙。
5 如果應用發生故障,你怎麼迅速找到問題所在?
6 用戶認證和授權呢?被狗吃了嗎?
如上詞彙,你可能看得懂,你也可能看不懂。沒有關係,請繼續閱讀,筆者將會用通俗的語言去描述,在你看完本系列後,你會知道,原來那些所謂的高大上的理論、術語、技術,原來也就是這麼回事兒。

配套代碼

GitHub:
  •  
    https://github.com/eacdy/spring-cloud-study/tree/master/2018-Finchley/microservice-simple-provider-user
  •  
    https://github.com/eacdy/spring-cloud-study/tree/master/2018-Finchley/microservice-simple-consumer-movie
Gitee:
  •  
    https://gitee.com/itmuch/spring-cloud-study/tree/master/2018-Finchley/microservice-simple-provider-user
  •  
    https://gitee.com/itmuch/spring-cloud-study/tree/master/2018-Finchley/microservice-simple-consumer-movie

更多免費技術資料可關注:annalin1203

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章