因为第一章都是一些概念性的东西,包括系统架构的演变、微服务架构的介绍(服务调度、服务治理、服务容错、链路追踪等等),大家感兴趣的可以阅读原文,我们这里直接从第二章微服务环境搭建开始。
本次使用的电商项目中的商品、订单、用户为案例进行讲解。
一、技术选型
JDK
:1.8
maven
:3.8.6
数据库
:MySQL 8.0.31
持久层
:SpringData Jpa
其他
:Spring Cloud Alibaba 2021.0.4.0,截止到目前最新版本
开发工具
:IntelliJ idea 2022.2
# 二、模块设计
springcloud-alibaba
:父工程
shop-common
:公共模块【实体类】
shop-user
:用户微服务【端口:807x】
shop-product
:商品微服务【端口:808x】
`shop-order:订单微服务【端口:809x】
2.1、创建父工程
打开idea,创建maven工程,选择【New Project】,输入Name
、GroupId
、ArtifactId
,选择存储目录,JDK选择本机安装的1.8版本。点击CREATE
完成项目创建。
springcloud-alibaba
只是作为父工程,我们不会写任何代码,所以直接把src
文件夹整体删掉。
然后在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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.11</version>
<relativePath/>
</parent>
<groupId>net.xiangcaowuyu</groupId>
<artifactId>springcloud-alibaba</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Cloud依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
注意一下是Spring Cloud Alibaba与Spring Cloud及Spring Boot的版本对应关系,一定要选择对应的版本。
Spring Cloud Alibaba Version | Spring Cloud Version | Spring Boot Version |
---|---|---|
2021.0.4.0* | Spring Cloud 2021.0.4 | 2.6.11 |
2021.0.1.0 | Spring Cloud 2021.0.1 | 2.6.3 |
2021.1 | Spring Cloud 2020.0.1 | 2.4.2 |
2.2、创建shop-common模块
2.2.1、创建模块
在工程上右键,选择NEW→Module
名称输入shop-common
创建模块
2.2.2、添加依赖
因为我们使用JPA,因此需要引入JPA
的依赖,为了减少代码量,同时使用了lombok
及fastjson
序列化,所以shop-common
添加依赖以下依赖
<dependencies>
<!-- jpa依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.19</version>
</dependency>
<!-- MySQL依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
</dependencies>
2.2.3、创建包
目前shop-common
模块中,src
文件夹还是空的,为了规范,我们把shop-common
的包定义为net.xiangcaowuyu.shop.common
2.2.4、创建实体
我们统一把实体放到entity
下,创建三个实体:用户(User)、商品(Product)、订单(Order)
package net.xiangcaowuyu.shop.common.entity;
import lombok.Data;
import javax.persistence.*;
/**
* 用户
*/
@Data
@Entity
@Table(name = "shop_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer ID;
/**
* 用户名
*/
private String name;
/**
* 密码
*/
private String password;
/**
* 手机号码
*/
private String telephone;
}
package net.xiangcaowuyu.shop.common.entity;
import lombok.Data;
import javax.persistence.*;
/**
* 商品
*/
@Data
@Entity
@Table(name = "shop_product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer ID;
/**
* 商品名
*/
private String name;
/**
* 价格
*/
private Double price;
/**
* 库存
*/
private Integer stock;
}
package net.xiangcaowuyu.shop.common.entity;
import lombok.Data;
import javax.persistence.*;
/**
* 订单
*/
@Data
@Entity
@Table(name = "shop_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer ID;
/**
* 用户ID
*/
private Integer uid;
/**
* 商品ID
*/
private Integer pid;
}
2.3、创建用户微服务
2.3.1、创建模块
参考2.2.1创建用户模块shop-user
2.3.2、添加依赖
添加以下依赖即可。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.xiangcaowuyu</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2.3.3、创建包
目前shop-user
模块中,src
文件夹还是空的,为了规范,我们把shop-user
的包定义为net.xiangcaowuyu.shop.user
2.3.4、编写主类
shop-user
作为一个微服务,必须是可独立运行的,因此必须创建一个主类UserApplication.java
。
package net.xiangcaowuyu.shop.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
2.3.5、创建配置文件
在resources
文件夹添加application.yaml
配置文件
server:
port: 8071
spring:
application:
name: service-user
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.236.2/shop?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
username: root
password: root
jpa:
hibernate:
#指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
ddl-auto: update
naming:
#指定jpa的自动表生成策略,驼峰自动映射为下划线格式
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
# 默认false,在日志里显示执行的sql语句
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
2.3.6、验证
设置完成后,shop-user
整个结构如下
启用UserApplication
,此时控制台输入一下内容,说明启动成功
2.4、创建商品微服务
2.4.1、创建模块
参考2.2.1创建商品模块shop-product
2.4.2、添加依赖
添加以下依赖即可。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.xiangcaowuyu</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2.4.3、创建包
目前shop-product
模块中,src
文件夹还是空的,为了规范,我们把shop-product
的包定义为net.xiangcaowuyu.shop.product
2.4.4、编写主类
shop-product
作为一个微服务,必须是可独立运行的,因此必须创建一个主类ProductApplication.java
。
package net.xiangcaowuyu.shop.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
2.4.5、创建配置文件
在resources
文件夹添加application.yaml
配置文件
server:
port: 8081
spring:
application:
name: service-product
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.236.2/shop?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
username: root
password: root
jpa:
hibernate:
#指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
ddl-auto: update
naming:
#指定jpa的自动表生成策略,驼峰自动映射为下划线格式
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
# 默认false,在日志里显示执行的sql语句
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
2.4.6、验证
设置完成后,shop-product
整个结构如下
启用ProductApplication
,此时控制台输入一下内容,说明启动成功
2.5、创建订单微服务
2.5.1、创建模块
参考2.2.1创建商品模块shop-order
2.5.2、添加依赖
添加以下依赖即可。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.xiangcaowuyu</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2.5.3、创建包
目前shop-order
模块中,src
文件夹还是空的,为了规范,我们把shop-product
的包定义为net.xiangcaowuyu.shop.order
2.5.4、编写主类
shop-order
作为一个微服务,必须是可独立运行的,因此必须创建一个主类OrderApplication.java
。
package net.xiangcaowuyu.shop.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
2.5.5、创建配置文件
在resources
文件夹添加application.yaml
配置文件
server:
port: 8091
spring:
application:
name: service-order
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.236.2/shop?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
username: root
password: root
jpa:
hibernate:
#指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
ddl-auto: update
naming:
#指定jpa的自动表生成策略,驼峰自动映射为下划线格式
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
# 默认false,在日志里显示执行的sql语句
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
2.5.6、验证
设置完成后,shop-order
整个结构如下
启用OrderApplication
,此时控制台输入一下内容,说明启动成功
三、原始服务调用
3.1、创建测试数据
为了方便后续演示,我们现在用户及商品表中创建几条测试数据。
INSERT INTO shop_user (id, name, password, telephone) VALUES (1, '张三', '123456', '13333333333');
INSERT INTO shop_user (id, name, password, telephone) VALUES (2, '李四', '123456', '14444444444');
INSERT INTO shop_product (id, name, price, stock) VALUES (1, '小米', 1000, 5000);
INSERT INTO shop_product (id, name, price, stock) VALUES (2, '华为', 2000, 5000);
INSERT INTO shop_product (id, name, price, stock) VALUES (3, '苹果', 3000, 5000);
INSERT INTO shop_product (id, name, price, stock) VALUES (4, '一加', 4000, 5000);
3.2、商品提供查询服务
3.2.1、数据访问层
创建商品数据访问层一个空的接口,JPA默认会提供查询方法
package net.xiangcaowuyu.shop.product.dao;
import net.xiangcaowuyu.shop.common.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* 商品数据访问层
*/
@Repository
public interface ProductDao extends JpaRepository<Product, Integer> {
}
3.2.2、服务层
创建ProductService及接口实现类
package net.xiangcaowuyu.shop.product.service;
import net.xiangcaowuyu.shop.common.entity.Product;
public interface ProductService {
/**
* 根据ID查询商品
*
* @param ID 商品ID
* @return 商品
*/
Product findByID(Integer ID);
}
package net.xiangcaowuyu.shop.product.service.impl;
import net.xiangcaowuyu.shop.common.entity.Product;
import net.xiangcaowuyu.shop.product.dao.ProductDao;
import net.xiangcaowuyu.shop.product.service.ProductService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ProductServiceImpl implements ProductService {
@Resource
private ProductDao productDao;
/**
* 根据ID查询商品
*
* @param ID 商品ID
* @return 商品
*/
@Override
public Product findByID(Integer ID) {
return productDao.findById(ID).get();
}
}
3.2.3、创建Rest接口
package net.xiangcaowuyu.shop.product.controller;
import lombok.extern.slf4j.Slf4j;
import net.xiangcaowuyu.shop.common.entity.Product;
import net.xiangcaowuyu.shop.product.service.ProductService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class ProductController {
@Resource
private ProductService productService;
@GetMapping("/product/{id}")
public Product product(@PathVariable("id") Integer ID) {
return productService.findByID(ID);
}
}
运行程序,现在浏览器测试一下
3.3、订单查询商品
3.3.1、创建配置文件
我们这里通过RestTemplate
访问商品,因此增加一个配置文件。
package net.xiangcaowuyu.shop.order.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class OrderConfiguration {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
3.3.1、创建Rest接口
这里简单模拟一下订单查询商品信息,只简单创建一个订单的controller
package net.xiangcaowuyu.shop.order.controller;
import lombok.extern.slf4j.Slf4j;
import net.xiangcaowuyu.shop.common.entity.Product;
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;
import javax.annotation.Resource;
@RestController
@RequestMapping("order")
@Slf4j
public class OrderController {
@Resource
private RestTemplate restTemplate;
@GetMapping("product/{id}")
public Product order(@PathVariable("id") Integer productID){
return restTemplate.getForObject("http://localhost:8081/product/1", Product.class);
}
}
简单测试一下订单接口
接口也是能够正常访问的。
四、传统服务调用的一些弊端
通过上面的代码,我们虽然实现了接口的调用,但是我们把服务提供者的网络地址(ip、端口)等硬编码到了代码中,这种做法存在很多问题:
- 一旦服务提供者地址发生变化,就需要手工修改代码(当然可以做成配置或者放到配置文件中)
- 一旦是多个服务提供者,无法实现负载均衡功能
- 一旦服务变得越来越多,人工维护调用关系变得非常困难
为了解决上述问题,我们就引出了微服务架构中的服务治理,也就是通过注册中心等方式,实现服务的自动注册与发现。
评论 (0)