我们从多个角度看什么是Spring Cloud:
打开 Spring 官网 http://spring.io 首页的中部,可以看到 Spring Cloud 的简介。
【原文】Building distributed systems doesn’t need to be complex and error-prone(易错). Spring Cloud offers a simple and accessible(易接受的) programming model to the most common distributed system patterns(模式), helping developers build resilient(有弹性的), reliable(可靠的), and coordinated(协调的) applications. Spring Cloud is built on top of Spring Boot, making it easy for developers to get started and become productive quickly.
【翻译】构建分布式系统不需要复杂和容易出错。Spring Cloud 为最常见的分布式系统模式提供了一种简单且易于接受的编程模型,帮助开发人员构建有弹性的、可靠的、协调的应用程序。Spring Cloud 构建于 Spring Boot 之上,使得开发者很容易入手并快速应用于生产中。
Spring Cloud 是一系列框架的有序集合
。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。Spring Cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者提供了一套简单易懂、易部署和易维护的分布式系统开发工具包。
Spring Cloud 是什么?阿里高级框架师、Dubbo 项目的负责人刘军说,Spring Cloud 是微服务系统架构的一站式解决方案。
Spring Cloud 与 Spring Boot 是什么关系呢?Spring Boot 为 Spring Cloud 提供了代码实现环境,使用 Spring Boot将其它组件有机融合到了 Spring Cloud 的体系架构中了。所以说,Spring Cloud 是基于 Spring Boot 的、微服务系统架构的一站式解决方案
。
Spring Cloud 的版本号并不是我们通常见的数字版本号,而是一些很奇怪的单词。这些单词均为英国伦敦地铁站的站名
。同时根据字母表的顺序来对应版本时间顺序,比如:最早的 Release 版本 Angel(天使),第二个 Release 版本 Brixton(英国地名),然后是 Camden、Dalston、Edgware,目前使用较多的是 Finchley(英国地名)版本,而最新版本为 Hoxton(英国地名),后续的快速入门篇,我们使用的都是目前最新版本 Hoxton。
除了版本号外还有一些其他的版本标记:
快照版
,可以使用,但其仍处理连续不断的开发改进中,不建议使用。里程碑版
。其也会标注上 PRE,preview,预览版,内测版,
不建议使用。发行候选版
,主要是用于修复 BUG,一般该版本中不会再添加大的功能修改了。正式发行前的版本。服务发行版,正式发行版
。一般还会被标注上 GA,General Available某一版本的 Spring Cloud 要求必须要运行在某一特定 Spring Boot 版本下。它们的对应关系在 Spring Cloud 官网可以看到版本对应说明。
右边表示的是SpringBoot最低版本
Dubbo 与 Spring Cloud 在不同节点上的技术实现、知识点对比:
路由网关
反向代理Nginx
:静态代理、动静分离、负载均衡、虚拟主机Spring Cloud Zuul
:服务路由、请求过滤、令牌桶限流、多维请求限流、灰度发布、Zuul的高可用、负载均衡、服务降级Gateway
:暂时没研究消费者集群
服务容错/熔断、降级
Dubbo自身实现
:集群容错Cluster、Mock机制、声明式缓存Hystrix
: 执行隔离、fallbackMethod降级、fallbackFactory降级、Dashboard仪表盘、服务降级报警机制服务路由
Dubbo自身实现的
:Router负载均衡
Dubbo自身实现的
:LoadBalanceRibbon
:IRule服务注册中心
Zookeeper
(CP强一致性):Paxos算法、ZAB算法、Leader选举、Znode、Wather、ZkClient、CuratorRedis等其他中间间也可以做注册中心
Eureka
(AP高可用):服务发现Discovery、自我保护机制、Eureka集群提供者集群
默认用Zookeeper作为配置中心
Spring Cloud Config
:配置文件的自动更新、消息总线系统Spring Cloud Bus
缓存数据层
DBMS
Spring Cloud 与 Dubbo 均为微服务框架,开发团队在进行技术选型时,总会将它们进行对比,考虑应该选择哪一个。可以从以下几方面考虑:
架构完整度
社区活跃度
通讯协议
Dubbo 通讯使用的是 RPC
,属于传输层,通信效率更高Spring Cloud 是 HTTP REST
,属于应用层,比Dubbo通信效率低,但是现在也不是问题了,因为有Spring Cloud gRPC
(谷歌的)技术改造与微服务开发
加下来我们会创建一个服务提供者工程和一个服务消费者工程,作为后续快速入门篇知识讲解演示的基础工程。
本例实现了消费者对提供者的调用,但并未使用到Spring Cloud,但其为后续Spring Cloud的运行测试环境。使用 MySQL 数据库,使用 Spring Data JPA 作为持久层技术。
(1) 创建工程
创建一个 Spring Initializr 工程,并命名为 01-provider-8081。导入 Lombok、Web、JPA 及 MySQL 驱动依赖。
(2) 导入 Druid 依赖
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
(3) 定义实体类
@Data
@Entity // springdatajpa默认用的hibernate,可以自动建表
@JsonIgnoreProperties({
"hibernateLazyInitializer", "handler", "fieldHandler"})
// 客户端与服务器之间的数据交互,是由SpringMVC的HttpMessageConverter处理的
// 其中一个实现是处理 Jackson数据 -> 完成Java对象与JSON数据间的转换工作
// JPA的默认实现是Hibernate,而Hibernate默认对于对象的查询是基于延迟加载的
// Depart depart = service.findById(5); 这里的depart实际是一个javasist动态代理对象
// String name = depart.getName();
// 对于HttpMessageConverter,它拿到对象后会立即转成json,这个时候对象中有些数据是空的会报错
// 基于这种情况我们需要忽略掉一些属性就不会报错了
public class Depart {
@Id // 表示当前属性为自动建的表的主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自动递增
private Integer id;
private String name;
}
(4) 定义 Repository 接口
// 第一个泛型是,当前Repository所操作的对象的类型
// 第二个泛型是,当前Repository所操作的对象的id类型
public interface DepartRepository extends JpaRepository<Depart, Integer> {
}
(5) 定义 Service 接口
public interface DepartService {
boolean saveDepart(Depart depart);
boolean removeDepartById(Integer id);
boolean modifyDepart(Depart depart);
Depart getDepartById(int id);
List<Depart> listAllDeparts();
}
(6) 定义 Service 实现类
@Service
public class DepartServiceImpl implements DepartService {
@Autowired
private DepartRepository repository;
//A、添加数据
// 插入
@Override
public boolean saveDepart(Depart depart) {
// 对于save()的参数,根据其id的不同,有以下三种情况:
// depart的id为null:save()执行的是插入操作
// depart的id不为null,且DB中该id存在:save()执行的是修改操作
// depart的id不为null,但DB中该id不存在:save()执行的是插入操作,
// 但其播入后的记录id值并不是这里指定的id,而是其根据指定的id生成策略所生成的id
Depart obj = repository.save(depart);
return obj != null ? true : false;
}
//B、 删除数据
@Override
public boolean removeDepartById(Integer id) {
if(repository.existsById(id)) {
// 在DB中指定的id若不存在,该方法会抛出异常
repository.deleteById(id);
return true;
}
return false;
}
//C、 修改数据
@Override
public boolean modifyDepart(Depart depart) {
Depart obj = repository.save(depart);
return obj != null ? true : false;
}
//D、根据 id 查询
@Override
public Depart getDepartById(int id) {
if(repository.existsById(id)) {
// 在DB中指定的id若不存在,该方法会抛出异常
return repository.getOne(id);
}
Depart depart = new Depart();
depart.setName("no this depart");
return depart;
}
//E、 查询所有
@Override
public List<Depart> listAllDeparts() {
return repository.findAll();
}
}
(7) 定义处理器
@RestController
@RequestMapping("/provider/depart")
public class DepartController {
@Autowired
private DepartService service;
@PostMapping("/save")
public boolean saveHandler(@RequestBody Depart depart) {
return service.saveDepart(depart);
}
@DeleteMapping("/del/{id}")
public boolean delHandler(@PathVariable("id") Integer id) {
return service.removeDepartById(id);
}
@PutMapping("/update")
public boolean updateHandler(@RequestBody Depart depart) {
return service.modifyDepart(depart);
}
@GetMapping("/get/{id}")
public Depart getHandler(@PathVariable("id") Integer id) {
return service.getDepartById(id);
}
@GetMapping("/list")
public List<Depart> listHandler() {
return service.listAllDeparts();
}
}
(8) 修改配置文件
server:
port: 8081
spring:
# 配置spring data jpa
jpa:
# 指定是否在spring容器启动时创建表,默认false
generate-ddl: true
# 指定在控制台是否显示SQL语句,默认false
show-sql: true
# 指定应用重启后不重新更新表内容
hibernate:
ddl-auto: none
# 配置数据源
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///test?useUnicode=true&characterEncoding=utf8
username: root
password: 111
# 配置日志
logging:
# 控制日志在控制台的输出
pattern:
console: level-%level %msg%n
# 控制日志的显示级别
level:
# 控制Spring Boot启动时显示的日志级别
root: info
# 控制Hibernate运行时的日志级别
org.hibernate: info
# 在show-sql为true时显示SQL中的动态参数值
org.hibernate.type.descriptor.sql.BasicBinder: trace
# 在show-sql为true时显示查询结果
org.hibernate.hql.internal.ast.exec.BasicExecutor: trace
# 控制自己代码运行时显示的日志级别
com.abc: debug
(9) 启动类更名
@SpringBootApplication
public class ApplicationProvider8081 {
//方便和消费者区分
public static void main(String[] args) {
SpringApplication.run(ApplicationProvider8081.class, args);
}
}
(1) 创建工程
创建一个 Spring Initializr 工程,并命名为 01-consumer-8080,导入 Lombok 与 Web 依赖。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
(2) 定义实体类
//消费者端,不需要持久层相关注解了
@Data
public class Depart {
private Integer id;
private String name;
}
(3) 定义 JavaConfig 配置类
@Configuration
public class DepartCodeConfigure {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
(4) 定义处理器类
@RestController
@RequestMapping("/consumer/depart")
public class SomeController {
@Autowired
private RestTemplate restTemplate;
private static final String SERVICE_PROVIDER = "http://localhost:8081";
//A、添加数据
@PostMapping("/save")
public boolean saveHandler(@RequestBody Depart depart) {
String url = SERVICE_PROVIDER + "/provider/depart/save";
return restTemplate.postForObject(url, depart, Boolean.class);
}
//B、 删除与修改数据
@DeleteMapping("/del/{id}")
public void deleteHandler(@PathVariable("id") int id) {
String url = SERVICE_PROVIDER + "/provider/depart/del/" + id;
//RestTemplate的delete方法是没有返回值的
restTemplate.delete(url);
}
@PutMapping("/update")
public void updateHandler(@RequestBody Depart depart) {
String url = SERVICE_PROVIDER + "/provider/depart/update";
//RestTemplate的put也是没有返回值的
restTemplate.put(url, depart);
}
//C、 两个查询
@GetMapping("/get/{id}")
public Depart getByIdHandler(@PathVariable("id") int id) {
String url = SERVICE_PROVIDER + "/provider/depart/get/" + id;
return restTemplate.getForObject(url, Depart.class);
}
@GetMapping("/list")
public List<Depart> listHandler() {
String url = SERVICE_PROVIDER + "/provider/depart/list";
return restTemplate.getForObject(url, List.class);
}
}
(5) 启动类更名
@SpringBootApplication
public class ApplicationConsumer8080 {
public static void main(String[] args) {
SpringApplication.run(ApplicationConsumer8080.class, args);
}
}