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.1.6.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 编写实体类
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层
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;@Repositorypublic interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {}
3.4 编写service层
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);}
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@Transactionalpublic 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层
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文件
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 配置启动类
package com.sunxiaping.product;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.autoconfigure.domain.EntityScan;@SpringBootApplicationpublic 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 编写实体类
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
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 配置启动类
package com.sunxiaping.order;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic 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
package com.sunxiaping.order.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;@Configurationpublic class SpringConfig { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); }}
4.7 编写下单方法
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 分析模拟微服务中存在的问题
服务调用者将微服务的请求路径硬编码到Java代码中。
不能对微服务负载均衡的调用,因为请求路径的URL硬编码了。
服务多起来,对前端调用不友好,需要加入API网关。
微服务多起来的话,如果每次都需要重新修改配置文件,很麻烦,需要配置的统一管理。
链路追踪。
系统容错。
……
