四、微服务项目搭建
4.1、技术选型
- 持久层:SpingData Jpa
- 数据库: MySQL5.7
- 技术栈:SpringCloud Alibaba 技术栈
4.2、模块设计
我们搭建一个微服务的项目,但是只有简单的代码,没有任何业务逻辑。
- shop-parent 父工程
- shop-product-api:商品微服务api ,用于存放商品实体。
- shop-product-server:商品微服务,他的1端口是808x。
- shop-order-api 订单微服务api,用于存放订单实体。
- shop-order-server 订单微服务,他的端口是808x。
4.3、微服务的调用
在微服务架构中,最常见的场景就是微服务之间的相互调用。我们以电商系统中常见的用户下单为例来演示微服务的调用:客户向订单微服务发起一个下单的请求,在进行保存订单之前需要调用商品微服务查询商品的信息。
我们一般把服务的主动调用方称为服务消费者,把服务的被调用方称为服务提供者。
![[00_System/Attachments/6d362b19c3de99f86752efbc7163de75_MD5.png]]
4.4、创建父工程
创建一个maven工程,然后在pom.xml文件中添加下面内容
<?xml version="1.0" encoding="UTF-8"?>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/>
<groupId>cn.linstudy</groupId>
<artifactId>Shop-parent</artifactId>
pom
<version>1.0.0</version>
<modules>
<module>Shop-order-api</module>
<module>Shop-order-server</module>
<module>Shop-product-api</module>
<module>Shop-product-server</module>
</modules>
<java.version>11</java.version>
UTF-8
UTF-8
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</dependencyManagement>
复制代码
4.5、创建商品服务
4.5.1、书写Shop-product-api的依赖
创建Shop-product-api项目,然后在pom.xml文件中添加下面内容
<?xml version="1.0" encoding="UTF-8"?>
<artifactId>Shop-parent</artifactId>
<groupId>cn.linstudy</groupId>
<version>1.0.0</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>Shop-product-api</artifactId>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
复制代码
4.5.2、创建实体
//商品
@Entity(name = "t_shop_product")
@Data
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long pid;//主键
private String pname;//商品名称
private Double pprice;//商品价格
private Integer stock;//库存
}
复制代码
4.5.3、书写Shop-product-server的依赖
<?xml version="1.0" encoding="UTF-8"?>
<artifactId>Shop-parent</artifactId>
<groupId>cn.linstudy</groupId>
<version>1.0.0</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>Shop-order-server</artifactId>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>cn.linstudy</groupId>
<artifactId>Shop-order-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>cn.linstudy</groupId>
<artifactId>Shop-product-api</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
复制代码
4.5.4、编写application.yml
server:
port: 8081
spring:
application:
name: product-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///shop-product?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: admin
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
复制代码
4.5.5、创建数据库
由于我们使用的是JPA,所以我们需要创建数据库,但是不需要创建表,因为JPA会在对应的数据库中自动创建表。
4.5.6、创建DAO接口
// 第一个参数是实体类,第二个参数是实体类对象的主键的类型
public interface ProductDao extends JpaRepository<Product,Long> {
}
复制代码
4.5.7、创建Service接口及其实现类
public interface ProductService {
Product findById(Long productId);
}
复制代码
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
ProductDao productDao;
@Override
public Product findById(Long productId) {
return productDao.findById(productId).get();
}
}
复制代码
4.5.8、书写Controller
@RestController
@Slf4j
public class ProductController {
@Autowired
private ProductService productService;
//商品信息查询
@RequestMapping("/product")
public Product findByPid(@RequestParam("pid") Long pid) {
Product product = productService.findByPid(pid);
return product;
}
}
复制代码
4.5.9、加入测试数据
我们在启动项目的时候,可以发现表已经默认帮我们自动创建好了,我们需要导入测试数据。
INSERT INTO t_shop_product VALUE(NULL,'小米','1000','5000');
INSERT INTO t_shop_product VALUE(NULL,'华为','2000','5000');
INSERT INTO t_shop_product VALUE(NULL,'苹果','3000','5000');
INSERT INTO t_shop_product VALUE(NULL,'OPPO','4000','5000');
复制代码
4.5.10、测试
![[00_System/Attachments/3fea6979b43dd61074bcbad4f01800f7_MD5.png]]
4.6、创建订单微服务
4.6.1、书写Shop-order-api的依赖
<?xml version="1.0" encoding="UTF-8"?>
<artifactId>Shop-parent</artifactId>
<groupId>cn.linstudy</groupId>
<version>1.0.0</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>Shop-order-api</artifactId>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
</dependencies>
复制代码
4.6.2、创建订单实体
//订单
@Entity(name = "t_shop_order")
@Data
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
public class Order implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long oid;//订单id
//用户
private Long uid;//用户id
private String username;//用户名
//商品
private Long pid;//商品id
private String pname;//商品名称
private Double pprice;//商品单价
//数量
private Integer number;//购买数量
}
复制代码
4.6.3、创建shop-order-server项目
在pom.xml中添加依赖。
<?xml version="1.0" encoding="UTF-8"?>
<artifactId>Shop-parent</artifactId>
<groupId>cn.linstudy</groupId>
<version>1.0.0</version>
<modelVersion>4.0.0</modelVersion>
<artifactId>Shop-order-server</artifactId>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>cn.linstudy</groupId>
<artifactId>Shop-order-api</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>cn.linstudy</groupId>
<artifactId>Shop-product-api</artifactId>
<version>1.0.0</version>
</dependency>
复制代码
4.6.4、编写application.yml
server:
port: 8082
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///shop-product?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: 1101121833
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
复制代码
4.6.5、编写Service及其实现类
我们写的Service接口及其实现类不具备任何的业务逻辑,仅仅只是为了测试而用。
public interface OrderService {
Order getById(Long oid,Long pid);
}
复制代码
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
OrderDao orderDao;
@Override
public Order getById(Long oid, Long pid) {
return orderDao.getOne(oid);
}
}
复制代码
4.6.6、创建Controller
@RestController
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("getById")
public Order getById(Long oid,Long pid){
return orderService.getById(oid, pid);
}
复制代码
4.7、服务之间如何进行调用
假设我们在订单的服务里面需要调用到商品服务,先查询出id为1的商品,然后再查询出他的订单,这个1时候就涉及到服务之间的调用问题了。 服务之间的1调用本质上是通过Java代码去发起一个Http请求,我们可以使用RestTemplate来进行调用。
4.7.1、在shop-order-server的启动类中添加注解
我们既然需要RestTemplate类,就需要将RestTemplate类注入到容器中。
@SpringBootApplication
public class OrderServer {
public static void main(String[] args) {
SpringApplication.run(OrderServer.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
复制代码
4.7.2、修改Controller代码
@RestController
public class OrderController {
@Autowired
RestTemplate restTemplate;
@Autowired
OrderService orderService;
@RequestMapping("getById")
public Order getById(Long oid,Long pid){
Product product = restTemplate.getForObject(
"http://localhost:8083/product?productId="+pid,Product.class); // 这里通过restTemplate去发起http请求,请求地址是http://localhost:8083/product,携带的参数是productId
Order order = orderService.getById(oid, product.getPid());
order.setUsername(product.getPname());
return order;
}
}
这样我们就完成了服务之间的互相调用。
4.8、存在的问题
虽然我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:
-
一旦服务提供者地址变化,就需要手工修改代码。
-
一旦是多个服务提供者,无法实现负载均衡功能。
-
一旦服务变得越来越多,人工维护调用关系困难。
那么应该怎么解决呢?这时候就需要通过注册中心动态的实现服务治理。