Spring Cloud是在Spring Boot基础上构建的,用于快速构建分布式系统的通用模式的工具集。
核心功能
- Distributed/versioned configuration 分布式/版本化的配置管理
- Service registration and discovery 服务注册与服务发现
- Routing 路由
- Service-to-service calls 端到端的调用
- Load balancing 负载均衡
- Circuit Breakers 断路器
- Global locks 全局锁
- Leadership election and cluster state 选举与集群状态管理
- Distributed messaging 分布式消息
以图书馆预约为例,用户服务调用图书馆服务进行预约。如用户服务向图书馆服务发起一个请求,查询当前是否有位置,是否还能预约,预约需要带什么东西,等等。
这个时候用户服务就是一个消费者,图书馆服务就是提供者,服务提供者向服务消费者提供服务,以便消费者使用
Spring Boot/Spring Cloud时代后,应用开发基本遵循三板斧:
- 加依赖
- 加注解
- 写配置
编写服务提供者[图书馆服务]
test-provider-library
引入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.0.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>test-provider-library</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-provider-library</name>
<description>Demo project for Spring Boot</description>
<properties>
<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-test</artifactId>
<scope>test</scope>
</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建实体类
package com.example.testproviderlibrary.pojo;
import lombok.*;
import javax.persistence.*;
/**
* 图书馆信息
*
*/
@Getter
@Setter
@Builder
@Entity
// 为类提供一个无参的构造方法。
@NoArgsConstructor
// 为类提供一个全参的构造方法
@AllArgsConstructor
public class LibraryInfo {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
/**
* 图书馆名称
*/
@Column
private String name;
/**
* 图书馆地址
*/
@Column
private String address;
/**
* 总座位数
*/
@Column
private Integer number;
/**
* 可预约数量
*/
@Column
private Integer count;
}
创建dao
package com.example.testproviderlibrary.dao;
import com.example.testproviderlibrary.pojo.LibraryInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface LibraryDao extends JpaRepository<LibraryInfo,Long> {
}
创建Service
package com.example.testproviderlibrary.service;
import com.example.testproviderlibrary.dao.LibraryDao;
import com.example.testproviderlibrary.pojo.LibraryInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
//可以代替@AutoWired注解,需要注意的是在注入时需要用final定义,或者使用@notnull注解
@RequiredArgsConstructor
public class LibraryService {
private final LibraryDao libraryDao;
public Optional<LibraryInfo> findById(Long id){
return this.libraryDao.findById(id);
}
}
创建controller
package com.example.testproviderlibrary.controller;
import com.example.testproviderlibrary.pojo.LibraryInfo;
import com.example.testproviderlibrary.service.LibraryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;
@RestController
@RequestMapping("/library")
public class LibraryController {
@Autowired
private LibraryService libraryService;
@GetMapping("/{id}")
public LibraryInfo findById(@PathVariable Long id){
Optional<LibraryInfo> libraryInfo = this.libraryService.findById(id);
return libraryInfo.get();
}
}
编写启动类
package com.example.testproviderlibrary;
import com.example.testproviderlibrary.dao.LibraryDao;
import com.example.testproviderlibrary.pojo.LibraryInfo;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.stream.Stream;
@SpringBootApplication
public class TestProviderLibraryApplication {
public static void main(String[] args) {
SpringApplication.run(TestProviderLibraryApplication.class, args);
}
/**
* 使用内置数据库h2 初始化数据
*/
@Bean
ApplicationRunner init (LibraryDao repository){
return args ->{
LibraryInfo libraryInfo1=LibraryInfo.builder().id(1L).name("前门图书馆")
.address("东城区前门东大街").number(1000).count(20).build();
LibraryInfo libraryInfo2=LibraryInfo.builder().id(2L).name("青年路图书馆")
.address("丰台区青年路").number(500).count(10).build();
LibraryInfo libraryInfo3=LibraryInfo.builder().id(2L).name("大地图书馆")
.address("昌平区上地八街").number(800).count(2).build();
Stream.of(libraryInfo1,libraryInfo2,libraryInfo3).forEach(repository :: save);
};
}
}
配置类
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
启动测试
http://localhost:8000/library/1
{"id":1,"name":"前门图书馆","address":"东城区前门东大街","number":1000,"count":20}
编写服务消费者[用户服务]
test-consumer-user
<?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.0.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>test-consumer-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-consumer-user</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
编写实体类
package com.example.testconsumeruser.pojo;
import lombok.*;
/**
* 图书馆信息
*
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
// 为类提供一个全参的构造方法
@AllArgsConstructor
public class LibraryInfo {
private Long id;
/**
* 图书馆名称
*/
private String name;
/**
* 图书馆地址
*/
private String address;
/**
* 总座位数
*/
private Integer number;
/**
* 可预约数量
*/
private Integer count;
}
编写启动类
package com.example.testconsumeruser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class TestConsumerUserApplication {
public static void main(String[] args) {
SpringApplication.run(TestConsumerUserApplication.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
编写controller
package com.example.testconsumeruser.controller;
import com.example.testconsumeruser.pojo.LibraryInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/library/{id}")
public LibraryInfo findById(@PathVariable Long id){
// 这里用到了RestTemplate的占位符能力
LibraryInfo forObject = restTemplate.getForObject("http://localhost:8000/library/{id}", LibraryInfo.class, id);
return forObject;
}
}
配置类
server:
port: 8081
启动项目,访问方法消费提供者提供的服务
http://localhost:8081/user/library/1