说明:SpringCloud系列笔者自学系列,学习来源是周立的博客 http://www.itmuch.com/ 。而此处转载其博客只是为了方便自己以后的学习。
本篇来源 http://www.itmuch.com/spring-cloud/finchley-2/
上一节说过,Spring Cloud是一个快速构建分布式应用的工具集。本节,我们就来编写一个简单的分布式应用,并探讨这个分布式应用有哪些问题。
服务消费者 & 提供者
本书使用服务提供者与服务消费者来描述微服务之间的调用关系。下表解释了服务提供者与服务消费者。
表-服务提供者与服务消费者
服务提供者 | 服务的被调用方(即:为其他服务提供服务的服务) |
服务消费者 | 服务的调用方(即:依赖其他服务的服务) |
以电影售票系统为例。如图,用户向电影微服务发起了一个购票的请求。在进行购票的业务操作前,电影微服务需要调用用户微服务的接口,查询当前用户的余额是多少、是不是符合购票标准等。在这种场景下,用户微服务就是一个服务提供者,电影微服务则是一个服务消费者。
围绕该场景,先来编写一个用户微服务,然后编写一个电影微服务。
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 使用网页版)创建应用,它会给你生成项目的依赖以及项目的骨架。后续,笔者会以番外的形式更新相关教程。
编写服务提供者【用户微服务】
-
创建一个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
- Lombok之前,必须为你的IDE安装Lombok插件!可参考:http://www.cnblogs.com/shindo/p/7550790.html
TIPS
- Lombok快速上手:https://blog.csdn.net/motui/article/details/79012846
- Lombok官方网站:https://projectlombok.org/
-
创建实体类:
public class User { (strategy = GenerationType.AUTO) private Long id; private String username; private String name; private Integer age; private BigDecimal balance; }
-
创建DAO:
public interface UserRepository extends JpaRepository<User, Long> { }
-
创建Controller:
"/users") ( public class UserController { private UserRepository userRepository; "/{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
等。 -
编写启动类:
public class ProviderUserApplication { public static void main(String[] args) { SpringApplication.run(ProviderUserApplication.class, args); } /** * 初始化用户信息 * 注:Spring Boot2不能像1.x一样,用spring.datasource.schema/data指定初始化SQL脚本,否则与actuator不能共存 * 原因详见: * https://github.com/spring-projects/spring-boot/issues/13042 * https://github.com/spring-projects/spring-boot/issues/13539 * * @param repository repo * @return runner */ 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
,来简化开发。在启动时,我们使用了
ApplicationRunner init(UserRepository repository)
初始化了三条数据,分别是张三、李四、王五。@Bean
则是一个方法注解,作用是实例化一个Bean并使用该方法的名称命名。类似于XML配置方式的<bean id="init" class="...ApplicationRunner"/>
。 -
编写配置文件
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的用户信息。
-
创建一个Maven项目,ArtifactId是
microservice-simple-consumer-movie
。 -
加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
-
创建实体类:
public class User { private Long id; private String username; private String name; private Integer age; private BigDecimal balance; }
-
创建启动类:
public class ConsumerMovieApplication { public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerMovieApplication.class, args); } }
-
创建Controller:
"/movies") ( public class MovieController { private RestTemplate restTemplate; "/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。
-
编写配置文件
application.yml
:server: port: 8010
拓展阅读
本文使用RestTemplate实现了基于HTTP的远程调用,事实上,Spring 5开始,WebFlux提供了Reactive的Web Client:WebClinet
,使用方式和RestTemplate基本类似,但性能更强,吞吐更好。有兴趣的可前往https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-builder 了解。在这里,笔者对WebClient做了一些简单的封装,也可关注:https://github.com/itmuch/thor-test/blob/master/src/main/java/com/itmuch/thor/httpclient/WebClientUtil.java
测试
访问:http://localhost:8010/movies/users/1
,结果如下:
{"id":1,"username":"account1","name":"张三","age":20,"balance":100.00}
|
存在的问题
至此,我们已经实现了这个最简单的分布式应用,应用之间通过HTTP通信。代码非常简单,但这些简单的代码里,存在着若干问题:
-
应用没有监控,没有画板,一切指标都没有。在这个Growth Hack逐渐成为主流的时代,不弄个Dashboard把系统压力、QPS、CPU、内存、日活啥的可视化,你好意思出来混吗……
-
地址硬编码问题——电影微服务中将用户微服务的地址写死,如果用户微服务地址发生变化,难道要重新上线电影微服务吗?
你可能会质疑:用户微服务地址为什么会变,让它保持不变就行了啊,这不是问题。这里举两个例子:
例1:如果你用Docker,那么地址几乎每次启动都会变……
例2:你之前用的是TXYun,后来你想把用户微服务迁移到Aliyun。这个时候IP就会发生变化。我相信你不会乐意找到哪些服务调用了用户微服务的接口,然后所有调用用户微服务的服务统一修改地址……
-
负载均衡如何考虑?难道得在电影微服务和用户微服务之间加个NGINX做负载均衡吗?听起来是可行的,但如果有10000+服务(这并不夸张,我司的微服务数目是这个数字乘以N,N >= m,哈哈哈)那这个NGINX的配置得有多复杂……
-
服务之间没有容错机制,相信对技术有激情的你已经不止一次听过容错、降级、fallback、回退之类的词汇。
-
如果应用发生故障,你怎么迅速找到问题所在?
-
用户认证和授权呢?被狗吃了吗?
如上词汇,你可能看得懂,你也可能看不懂。没有关系,请继续阅读,笔者将会用通俗的语言去描述,在你看完本系列后,你会知道,原来那些所谓的高大上的理论、术语、技术,原来也就是这么回事儿。
配套代码
-
GitHub:
-
Gitee:
相关文章
- Config Server——使用Spring Cloud Bus自动刷新配置
- Docker容器双向联通与高可用的Eureka Server
- Spring Cloud第二篇 创建一个Eureka Server
- Spring Cloud中,如何解决Feign/Ribbon第一次请求失败的问题?
- Spring Cloud中,Eureka常见问题总结
相关推荐
这些资源文件是开源书籍《跟我学Spring Cloud》的配套代码,用于帮助读者通过实例更好地理解和掌握Spring Cloud的使用方法和原理。 其中,Dockerfile文件位于2016-Brixton的microservice-api-gateway项目中,它描述...
《跟我学Spring》是一份全面且深入的Spring学习资源,旨在帮助初学者和有经验的开发者从零开始掌握Spring框架,逐步进阶为Spring高手。这份资料不仅包含了Spring的基本概念和核心特性,还通过实战项目让你亲身体验...
Spring Cloud 是一组工具,用于构建分布式系统,如服务发现、配置管理、断路器模式、负载均衡等。它是微服务架构的重要组成部分。 在 Spring 6.1.4 版本中,可能包含了一些新特性、增强的功能和优化。例如,可能...
通过对这些代码的分析和实践,开发者可以深入了解Spring框架在实际项目中的应用,学习如何提高代码质量,优化性能,并跟上Java和Spring社区的发展步伐。同时,这些示例也可以作为进一步研究和学习的基础,帮助开发者...
- Spring Boot与Spring Cloud:快速开发与分布式微服务架构。 这些知识点只是Java面试准备的一部分,每个主题下都有更深入的内容等待探索。通过不断学习和实践,才能更好地应对Java面试中的各种挑战。希望这1000集...
28. 微服务:研究Spring Boot和Spring Cloud,掌握微服务架构的设计和实现。 29. 代码重构:适时进行代码重构,提高代码质量,保持代码的可维护性。 30. 安全性:关注Java安全,学习如何防止SQL注入、XSS攻击等,...