这篇文章简要的教大家搭建一个入门的微服务. 微服务构建的分布式系统,微服务之间是通过网络进行通信的. 我们使用服务消费者与服务提供者来描述服务之间的调用关系, 本章将从编写一个服务提供者和一个提供消费者;

这里以电影售票系统为例编写服务提供者和服务消费者,电影微服务充当服务消费者,用户微服务就是一个服务提供者;

spring微服务内存不释放 spring 微服务入门_Spring Boot

编写服务提供者

Spring Cloud 是基于Spring Boot 构建的,因此我们首先创建Spring Boot项目

这里介绍使用Spring Initiallizr快速创建Spring Boot 项目:

Spring Initiallizr 有以下几种用法:

  • 通过网页使用
  • 通过Spring Tool Suite使用
  • 通过IntellJ IDEA使用
  • 使用Spring Boot CLI使用

这里我使用第一种方法进行创建.

  1. 访问https://start.spring.io/,会看到如下类似的界面.
  2. 按照需求,选择项目类型和Spring Boot版本,填写GroupId和ArtifactId.
  3. 点击Generate Project按钮,获取zip包,压缩后用IDE打开.

spring微服务内存不释放 spring 微服务入门_spring微服务内存不释放_02

接下来我们开始编写项目:

我这里用的spring boot版本为2.0.4
jdk1.8
maven3.5.0
  1. 服务提供者的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,不过项目中使用的硬编码,调用地址是直接写死的,这样做存在很多问题.

  • 适用场景有局限:如果服务提供者的网络地址发生了变化,将会影响服务消费者. 例如如果用户微服务的网络地址发生了变化,那么电影微服务就需要修改配置并重新发布,这显然不可取.
  • 无法动态伸缩: 再生产环境中,每个微服务一般都会有部署多个实例,从而实现容灾和负载均衡. 在微服务架构的系统中,还需要系统具备自动伸缩的能力,例如动态增减节点等. 硬编码无法适应这种需求.

那么如何解决这些问题呢,接下来的文章我会介绍微服务的注册与发现,敬请期待!