[TOC]

通过上一篇文章理解了微服务后我们通过一个简单的电影购票场景来实现微服务。

如图:
电影购票系统
这个场景当中,用户微服务是一个服务提供者,电影微服务是一个服务消费者,之前我们也说到,每个微服务从开发,测试,构建,部署,都应当独立运行,即每个微服务是单独的子项目。下面来实现这个场景。

一、编写服务提供者

新建一个Spring Boot (版本1.5.9.RELEASE) 项目,不知道如何在IDEA中新建的可以看这篇>>传送门::Spring Boot 入门知识

添加依赖

项目使用H2作为数据库,使用jpa作为持久层框架。Spring Boot环境中H2数据库的基本配置可参考这篇>>传送门:Spring Boot环境下的 H2数据库基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<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>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<!--引入SpringCloud 依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

配置

application.yml 文件配置如下 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8000
spring:
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl_auto: none
h2:
console:
path: /h2-console #h2 web控制台路径
enabled: true #开启 Web Console
settings:
web-allow-others: true #允许远程访问 Web Console
datasource:
platform: h2 #指定数据源类型
schema: classpath:schema.sql #指定数据库的数据脚本
data: classpath:data.sql #指定数据库的数据脚本

spring.h2.console.path 指定了h2控制台的路径,可以通过localhost:8000/h2-console 去访问H2的控制台。
spring.datasource.schema 与 datasource.data 会在每次启动项目时都会被执行

schema.sql

1
2
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));

data.sql

1
2
3
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);

pojo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Entity
@JsonIgnoreProperties(value={"hibernateLazyInitializer","handler"})
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;

//...get
//...set
}

报错

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer
如果在运行时报以上错误,则需要添加注释:
@JsonIgnoreProperties(value={“hibernateLazyInitializer”,”handler”})

这是因为 hibernate会给每个被管理的对象加上hibernateLazyInitializer属性,jsonplugin通过java的反射机制将pojo转换成json,会把hibernateLazyInitializer也拿出来操作,但是hibernateLazyInitializer无法由反射得到,就会抛异常了。

Dao

1
2
3
@Repository
public interface UserRepository extends JpaRepository<User,Long>{
}

Controller

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;

@GetMapping("/{id}")
public User findById(@PathVariable Long id){
User findOne = this.userRepository.getOne(id);
return findOne;
}
}

@GetMapping() 等同于 @RequestMapping(method = {RequestMethod.GET})

测试

运行项目,访问测试
测试

二、编写服务消费者

消费者作为服务调用方,这里只使用最简单的方式来实现。

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>
</dependencies>
<!--引入SpringCloud 依赖-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Edgware.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

配置

application.yml

1
2
3
4
server:
port: 8011
user:
userServiceUrl: http://localhost:8000/

user.userServiceUrl :把调用地址写入配置文件

pojo

这里与上面的服务提供者相同

RestTemplate

实例化RestTemplate
在启动类中添加以下方法:

1
2
3
4
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@Value("${user.userServiceUrl}")
private String userServiceUrl;

@GetMapping("/user/{id}")
public User findById(@PathVariable Long id){
return this.restTemplate.getForObject(this.userServiceUrl + id,User.class);
}
}

这里使用 restTemplate 来调用服务
@Value(“${user.userServiceUrl}”) 从配置文件中取user.userServiceUrl值

测试

启动项目进行测试
消费者测试

三、总结

至此,一个简单的微服务完成!是不是觉得与我们平时写接口是差不多的,平时我们是在整个系统内部,各个功能模块之间进行接口调用,微服务则是把这些模块单独出来成为一个子系统,每个子系统提供接口给其它系统调用。
在整个电影购票系统中
使用单一职责原则:两个微服务只关注整个系统中单独,有界限的一部分。
满足服务自治原则:每个微服务具备独立的业务能力,依赖与运行环境。
使用了轻量级通信机制:消费者中使用了Rest 进行服务调用
微服务粒度:两个微服务都有明确的功能划分。

当然微服务不只是这么简单,还应该包括安全性,高可用性等,还需要集成其它的组件,后面会边学习边作记录。