这篇文章简要的教大家搭建一个入门的微服务. 微服务构建的分布式系统,微服务之间是通过网络进行通信的. 我们使用服务消费者与服务提供者来描述服务之间的调用关系, 本章将从编写一个服务提供者和一个提供消费者;
这里以电影售票系统为例编写服务提供者和服务消费者,电影微服务充当服务消费者,用户微服务就是一个服务提供者;
编写服务提供者
Spring Cloud 是基于Spring Boot 构建的,因此我们首先创建Spring Boot项目
这里介绍使用Spring Initiallizr快速创建Spring Boot 项目:
Spring Initiallizr 有以下几种用法:
- 通过网页使用
- 通过Spring Tool Suite使用
- 通过IntellJ IDEA使用
- 使用Spring Boot CLI使用
这里我使用第一种方法进行创建.
- 访问https://start.spring.io/,会看到如下类似的界面.
- 按照需求,选择项目类型和Spring Boot版本,填写GroupId和ArtifactId.
- 点击Generate Project按钮,获取zip包,压缩后用IDE打开.
接下来我们开始编写项目:
我这里用的spring boot版本为2.0.4
jdk1.8
maven3.5.0
- 服务提供者的pom.xml文件如下:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ccgogoing.cloud</groupId>
<artifactId>microservice-simple-provider-user</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>microservice-simple-provider-user</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</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>
其中,Spring-boot-starter-web提供了SpringMVC支持;spring-boot-starter-data-jpa提供了Spring Data JPA支持;
2.准备好建表语句,在项目的resource目录也就是classpath 下建立schema.sql,并添加如下内容:
drop table user if EXISTS ;
CREATE TABLE user (id bigint generated by default as IDENTITY ,username varchar(40),name VARCHAR (20),age int(3),balance decimal(10,2),PRIMARY key (id));
3.准备几条模拟数据,在项目的resource目录下建立文件data.sql并添加如下内容:
insert into user (id,username,name,age,balance) values (1,'account1','张三',20,100.00);
insert into user (id,username,name,age,balance) values (2,'account2','李四',28,180.00);
insert into user (id,username,name,age,balance) values (3,'account3','王五',32,280.00);
4.创建用户实体类:
package com.ccgogoing.cloud.microservicesimpleprovideruser.entity;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* 描述:
*
* @outhor chong
* @create 2018-08-01 23:31
*/
@Entity
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;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
5.创建DAO:
package com.ccgogoing.cloud.microservicesimpleprovideruser.dao;
import com.ccgogoing.cloud.microservicesimpleprovideruser.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 描述:
*
* @outhor chong
* @create 2018-08-01 23:34
*/
@Repository
public interface UserRepository extends JpaRepository<User,Long>{
}
6.创建Controller:
package com.ccgogoing.cloud.microservicesimpleprovideruser.controller;
import com.ccgogoing.cloud.microservicesimpleprovideruser.dao.UserRepository;
import com.ccgogoing.cloud.microservicesimpleprovideruser.entity.User;
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.RestController;
/**
* 描述:
*
* @outhor chong
* @create 2018-08-01 23:36
*/
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/{id}")
public User findById(@PathVariable Long id) {
User findOne = this.userRepository.findById(id).get();
return findOne;
}
}
7.编写启动类,在类上的@SpringBootApplication声明这是一个Spring Boot项目.
package com.ccgogoing.cloud.microservicesimpleprovideruser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MicroserviceSimpleProviderUserApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceSimpleProviderUserApplication.class, args);
}
}
@SpringBootApplication是一个组合注解,它整合了@Configuration、@EnableAutoConfiguration和@ComponentScan 注解,并开启了Spring Boot程序的组件扫描和自动配置功能.
8.编写配置文件,这里我们将application.properties后缀改为.yml
server:
port: 8000
spring:
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource:
platform: h2
schema: classpath:schema.sql
data: classpath:data.sql
managmanagement:
health:
defaults:
enabled: false
info:
app:
name: @project.artifactId@
encoding: @project.build.sourceEncoding@
java:
source: @java.version@
target: @java.version@
logging:
level:
root: info
org.hibernate: info
org.hibernate.type.descriptor.sql.BasicBinder: trace
org.hibernate.type.descriptor.sql.BasicExtractor: trace
从中不难看出YAML文件比properties结构清晰;可读性、可维护性也更强,并且语法非常简洁。因此推荐大家尽可能使用YAML格式作为配置文件.
测试
访问: http://localhost:8000/1 ,获得结果:
{
"id": 1,
"username": "account1",
"name": "张三",
"age": 20,
"balance": 100.00
}
说明可通过ID查询用户信息
编写服务消费者
上面我们编写好了服务提供者(用户微服务),接下来我们编写一个服务消费者(电影微服务),该服务非常简单,使用RestTemplate 调用用户为服务的API,查询指定用户信息.
1.创建一个Maven项目,ArtifactId是microservice-simple-consumer-movie
2.pom.xml文件如下:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ccgogoing.cloud</groupId>
<artifactId>microservice-simple-consumer-movie</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>microservice-simple-consumer-movie</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<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-actuator</artifactId>
</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>
3.创建用户实体类,该类是一个POJO.
package com.ccgogoing.cloud.microservicesimpleconsumermovie.pojo;
import java.math.BigDecimal;
/**
* 描述:
*
* @outhor chong
* @create 2018-08-02 20:02
*/
public class User {
private Long id;
private String username;
private String name;
private Integer age;
private BigDecimal balance;
/**这里省略getters and setters*/
4.创建启动类,代码如下.
package com.ccgogoing.cloud.microservicesimpleconsumermovie;
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 ConsumerMovieApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieApplication.class, args);
}
}
@Bean是一个方法注解,作用是实例化一个Bean并使用该方法的名称命名.
5.创建Controller,在其中使用RestTemplate请求用户微服务API.
package com.ccgogoing.cloud.microservicesimpleconsumermovie.controller;
import com.ccgogoing.cloud.microservicesimpleconsumermovie.pojo.User;
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.RestController;
import org.springframework.web.client.RestTemplate;
/**
* 描述:
*
* @outhor chong
* @create 2018-08-02 20:05
*/
@RestController
public class MovieController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://localhost:8000/" + id,User.class);
}
}
6.编写配置文件application.yml:
server:
port: 8010
这样,一个电影微服务就完成了,so easy吧!
测试
访问:http://localhost:8010/user/1,结果如下:
{
"id": 1,
"username": "account1",
"name": "张三",
"age": 20,
"balance": 100.00
}
为项目整合Spring Boot Actuator
Spring Boot Actuator 提供了很多监控端点.可使用 http://{ip}:{port}/{endpoint} 的形式访问这些端点,从而了解应用程序的运行状况.
Spring Boot整合Actuator 十分简单,只需要给项目添加一下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
这样就整合好了, 是不是很简单.
重启服务后进行测试:
1.访问: http://localhost:8000/actuator/health,可得到类似下面结果
{
"status": "UP"
}
其中UP表示运行正常.
2.访问: http://localhost:8000/actuator/info,可得到如下内容:
{}
有结果可知,info端点并没有任何数据给我们.可使用info.* 属性来自定义info端点公开的数据,例如:
info:
app:
name: @project.artifactId@
encoding: @project.build.sourceEncoding@
java:
source: @java.version@
target: @java.version@
这样,重启后,再次访问就会看到如下类似内容:
{
"app": {
"name": "microservice-simple-provider-user",
"encoding": "UTF-8",
"java": {
"source": "1.8.0_151",
"target": "1.8.0_151"
}
}
}
由结果而知,info端点反悔了项目名称,编码,Java版本等信息.
本章介绍了使用Spring Boot搭建服务提供者和服务消费者, 实现了使用RestTemplate调用用户微服务中的RestFul API,不过项目中使用的硬编码,调用地址是直接写死的,这样做存在很多问题.
- 适用场景有局限:如果服务提供者的网络地址发生了变化,将会影响服务消费者. 例如如果用户微服务的网络地址发生了变化,那么电影微服务就需要修改配置并重新发布,这显然不可取.
- 无法动态伸缩: 再生产环境中,每个微服务一般都会有部署多个实例,从而实现容灾和负载均衡. 在微服务架构的系统中,还需要系统具备自动伸缩的能力,例如动态增减节点等. 硬编码无法适应这种需求.
那么如何解决这些问题呢,接下来的文章我会介绍微服务的注册与发现,敬请期待!