模拟微服务环境
1 数据库表
- 用户表:
CREATE TABLE `tb_user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`username` varchar(40) DEFAULT NULL COMMENT '用户名',
`password` varchar(40) DEFAULT NULL COMMENT '密码',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`balance` decimal(10,2) DEFAULT NULL COMMENT '余额',
`address` varchar(80) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- 商品表:
CREATE TABLE `tb_product` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_name` varchar(40) DEFAULT NULL COMMENT '名称',
`status` int(2) DEFAULT NULL COMMENT '状态',
`price` decimal(10,2) DEFAULT NULL COMMENT '单价',
`product_desc` varchar(255) DEFAULT NULL COMMENT '描述',
`caption` varchar(255) DEFAULT NULL COMMENT '标题',
`inventory` int(11) DEFAULT NULL COMMENT '库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `tb_product` VALUES (1, 'iPhone', 1, 5000.10, '苹果手机就是香', '苹果哇', 50);
- 订单表:
CREATE TABLE `tb_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`product_id` int(11) DEFAULT NULL COMMENT '商品id',
`number` int(11) DEFAULT NULL COMMENT '数量',
`price` decimal(10,2) DEFAULT NULL COMMENT '单价',
`amount` decimal(10,2) DEFAULT NULL COMMENT '总额',
`product_name` varchar(40) DEFAULT NULL COMMENT '商品名',
`username` varchar(40) DEFAULT NULL COMMENT '用户名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2 搭建环境
2.1 创建父工程spring_cloud_demo
- 在IDEA中创建父工程spring_cloud_demo,并引入坐标:
<?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>
<packaging>pom</packaging>
<modules>
<module>product_service</module>
<module>spring_cloud_common</module>
<module>order_service</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>org.sunxiaping</groupId>
<artifactId>spring_cloud_demo</artifactId>
<version>1.0</version>
<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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>http://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
2.2 创建微服务工程模块
- 创建商品微服务模块spring_cloud_product。
- 创建订单微服务模块spring_cloud_order。
3 搭建商品微服务
3.1 在pom.xml中导入相关jar包的坐标
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.sunxiaping</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.2.RELEASE</version>
<configuration>
<fork>true</fork>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.2 编写实体类
- Product.java
package com.sunxiaping.product.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tb_product")
public class Product implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "product_name")
private String productName;
@Column(name = "status")
private Integer status;
@Column(name = "price")
private BigDecimal price;
@Column(name = "product_desc")
private String productDesc;
@Column(name = "caption")
private String caption;
@Column(name = "inventory")
private String inventory;
}
3.3 编写dao层
- ProductDao.java
package com.sunxiaping.product.dao;
import com.sunxiaping.product.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
}
3.4 编写service层
- ProductService.java
package com.sunxiaping.product.service;
import com.sunxiaping.product.domain.Product;
public interface ProductService {
/**
* 根据id查询
*
* @param id
* @return
*/
Product findById(Long id);
/**
* 保存
*
* @param product
*/
void save(Product product);
/**
* 更新
*
* @param product
*/
void update(Product product);
/**
* 删除
*
* @param id
*/
void delete(Long id);
}
- ProductServiceImpl.java
package com.sunxiaping.product.service.impl;
import com.sunxiaping.product.dao.ProductRepository;
import com.sunxiaping.product.domain.Product;
import com.sunxiaping.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
@Transactional
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductRepository productRepository;
@Override
public Product findById(Long id) {
return productRepository.findById(id).orElse(new Product());
}
@Override
public void save(Product product) {
productRepository.save(product);
}
@Override
public void update(Product product) {
productRepository.save(product);
}
@Override
public void delete(Long id) {
productRepository.deleteById(id);
}
}
3.5 编写controller层
- ProductController.java
package com.sunxiaping.product.controller;
import com.sunxiaping.product.domain.Product;
import com.sunxiaping.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(value = "/product")
public class ProductController {
@Autowired
private ProductService productService;
@PostMapping(value = "/save")
public String save(@RequestBody Product product) {
productService.save(product);
return "新增成功";
}
@GetMapping(value = "/findById/{id}")
public Product findById(@PathVariable(value = "id") Long id) {
return productService.findById(id);
}
}
3.6 配置yml文件
- application.yml
server:
port: 9001 # 微服务的端口号
spring:
application:
name: service-product # 微服务的名称
datasource:
url: jdbc:mysql://192.168.237.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
3.7 配置启动类
- ProductApplication.java
package com.sunxiaping.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
4 搭建订单微服务
4.1 在pom.xml中导入相关jar包的坐标
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>org.sunxiaping</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
4.2 编写实体类
- Product.java
package com.sunxiaping.order.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tb_product")
public class Product implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "product_name")
private String productName;
@Column(name = "status")
private Integer status;
@Column(name = "price")
private BigDecimal price;
@Column(name = "product_desc")
private String productDesc;
@Column(name = "caption")
private String caption;
@Column(name = "inventory")
private String inventory;
}
4.3 配置yml
- application.yml
server:
port: 9002 # 微服务的端口号
spring:
application:
name: service-order # 微服务的名称
datasource:
url: jdbc:mysql://192.168.237.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
jpa:
generate-ddl: true
show-sql: true
open-in-view: true
database: mysql
4.4 配置启动类
- OrderApplication.java
package com.sunxiaping.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
4.5 服务调用
4.5.1 概述
- 在用户下单的时候需要调用商品微服务获取商品数据,那么应该需要怎么做呢?商品微服务提供了供人调用的HTTP接口,所以下订单的时候使用HTTP请求的相关工具类完成,如常见的HttpClient、OkHttp,当然也可以使用Spring提供的RestTemplate。
4.5.2 RestTemplate介绍
-
Spring框架提供的RestTemplate类可用于在应用中调用RESTful服务,它简化了和HTTP服务的通信方式,统一了RESTful的标准,封装了HTTP的链接,我们只需要传入URL和返回值类型即可。相较于之前常用的HttpClient、OkHttp等,RestTemplate是一种更加优雅的调用RESTful服务的方式。
-
在Spring应用程序中访问第三方REST服务和Spring的RestTemplate类有关。RestTemplate类的设计原则和许多其他Spring模块类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。
-
RestTemplate默认依赖JDK提供HTTP连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为如Apache HttpComponents、Netty或OkHttp等其他HTTP库。
-
考虑到RestTemplate类是为调用REST服务而设计的,因此他的主要方法和REST的基础紧密相连就不足为奇,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()等方法。
4.6 向Spring容器中注册RestTemplate
- SpringConfig.java
package com.sunxiaping.order.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class SpringConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
4.7 编写下单方法
- OrderController.java
package com.sunxiaping.order.controller;
import com.sunxiaping.product.domain.Product;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping(value = "/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
/**
* 通过订单系统,调用商品微服务根据id查询商品信息
*
* @param id
* @return
*/
@GetMapping(value = "/buy/{id}")
public Product buy(@PathVariable(value = "id") Long id) {
Product product = restTemplate.getForObject("http://localhost:9001/product/findById/" + id, Product.class);
return product;
}
}
5 分析模拟微服务中存在的问题
- 1️⃣服务调用者将微服务的请求路径硬编码到Java代码中。
- 2️⃣不能对微服务负载均衡的调用,因为请求路径的URL硬编码了。
- 3️⃣服务多起来,对前端调用不友好,需要加入API网关。
- 4️⃣微服务多起来的话,如果每次都需要重新修改配置文件,很麻烦,需要配置的统一管理。
- 5️⃣链路追踪。
- 6️⃣系统容错。
- 7️⃣……