一个归档包(例如war包)包含所有功能的应用程序,我们通常称为单体应用;架构单体应用的方法论,就是单体应用架构。
核心思想:一个归档包解决所有问题
一个war包包含了用户、选课、课程分类等模块,使用一个数据库
单体架构缺点:
微服务架构风格是一种将一个单体应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常HTTP资源API),这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务公用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
每个微服务可独立运行在自己的进程里
一系列独立运行的微服务共同构建起整个系统
每个微服务为独立的业务开发,只关注某个特定的功能
全自动机制(CI/CD) 微服务的基石 能够自动化的自动化
异构(不同语言 java php python 与数据存储 mongo redis mysql)
轻量的通信机制 通信协议应该是轻量的 webservice soap 这些协议就重量级 轻量型 还需要 能跨平台
微服务架构通览:
通过网关达到系统内部
每个微服务都有自己的web服务器(比如tomcat)
每个微服务之间通过轻量级通信机制进行通信
每个微服务都有自己的数据库
存在服务发现组件
微服务并不是黑科技,而是利用分而治之的思想将一个大型的服务拆分成若干个小型的应用。从微观来看,每个微服务也是一个单体应用
微服务拆分方法——个人心得总结
按照职责划分
订单服务只关注订单方面的
按照通用性划分
比如用户中心 很多的微服务都会用到用户中心 比如中台战略(多个微服务组成的能力中心)
微服务粒度
太细 -》 额外的网络开销 增加运维成本
太粗 -》 单个服务的复杂度依然过高
良好地满足业务需求
增量迭代–》保持相对的独立 一次迭代只涉及部分的微服务
持续进化–》更换语言 技术的更替(更换框架)
团队幸福感
拆分-》建表-》写代码
用户微服务(通用性划分)
课程微服务(职责划分)
Spring Boot
Spring MVC
Spring Data JPA
Spring Cloud
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
<relativePath />
parent>
<groupId>com.cloudgroupId>
<artifactId>ms-userartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>ms-username>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/ms_user
hikari:
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none # 让hibernate不去操作表结构
# 打印执行的sql
show-sql: true
package com.cloud.msuser.domain.entity;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "user")
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // mysql的自增策略
private Integer id;
@Column
private String username;
@Column
private String password;
@Column
private BigDecimal money;
@Column
private String role;
@Column
private Date regTime;
setter/getter略
}
package com.cloud.msuser.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.cloud.msuser.domain.entity.User;
@Repository
public interface UserRepository extends CrudRepository<User, Integer> {
}
package com.cloud.msuser.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cloud.msuser.domain.entity.User;
import com.cloud.msuser.repository.UserRepository;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User findById(Integer id) {
// 如果有就返回 否则抛出异常
return this.userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("用户不存在"));
}
}
package com.cloud.msuser.controller;
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 com.cloud.msuser.domain.entity.User;
import com.cloud.msuser.service.UserService;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public User findById(@PathVariable Integer id) {
return this.userService.findById(id);
}
}
package com.cloud.msuser;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MsUserApplication {
public static void main(String[] args) {
SpringApplication.run(MsUserApplication.class, args);
}
}
java.sql.SQLException: The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
修改application.yml文件:
# 增加url的属性serverTimezone
url: jdbc:mysql://127.0.0.1:3306/ms_user?serverTimezone=UTC
另外如果url定义错误比如:jdbc:mysql//127.0.0.1:3306/ms_user?serverTimezone=UTC(msyql后面少一个:),启动会导致异常:
Description:
Failed to configure a DataSource: no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
http://localhost:8081/users/1
{"id":1,"username":"itmuch","password":"1111","money":5,"role":"user","regTime":"2020-02-15T14:37:20.000+0000"}
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.7.RELEASEversion>
<relativePath />
parent>
<groupId>com.cloudgroupId>
<artifactId>ms-classartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>ms-classname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>1.8source>
<target>1.8target>
configuration>
plugin>
plugins>
build>
project>
server:
port: 8010
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/ms_class?serverTimezone=UTC
hikari:
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: none # 让hibernate不去操作表结构
# 打印执行的sql
show-sql: true
package com.cloud.msclass.domain.entity;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "lesson")
@Entity
public class Lesson {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String title;
@Column
private String cover;
@Column
private BigDecimal price;
@Column
private String description;
@Column
private Date createTime;
@Column
private String videoUrl;
}
package com.cloud.msclass.domain.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Table(name = "lesson_user")
@Entity
public class LessonUser {
@Column
@Id
private Integer lessonId;
@Column
private Integer userId;
public Integer getLessonId() {
return lessonId;
}
public void setLessonId(Integer lessonId) {
this.lessonId = lessonId;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
创建DTO
package com.cloud.msclass.domain.dto;
import java.math.BigDecimal;
import java.util.Date;
public class UserDTO {
private Integer id;
private String username;
// 实际项目中会隐藏password
private String password;
private BigDecimal money;
private String role;
private Date regTime;
}
package com.cloud.msclass.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.cloud.msclass.domain.entity.Lesson;
@Repository
public interface LessonRepository extends CrudRepository<Lesson, Integer> {
}
package com.cloud.msclass.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.cloud.msclass.domain.entity.LessonUser;
@Repository
public interface LessonUserRepository extends CrudRepository<LessonUser, Integer> {
LessonUser findByLessonId(Integer id);
}
package com.cloud.msclass.service;
import java.math.BigDecimal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.cloud.msclass.domain.dto.UserDTO;
import com.cloud.msclass.domain.entity.Lesson;
import com.cloud.msclass.domain.entity.LessonUser;
import com.cloud.msclass.repository.LessonRepository;
import com.cloud.msclass.repository.LessonUserRepository;
@Service
public class LessonService {
@Autowired
private LessonRepository lessonRepository;
@Autowired
private LessonUserRepository lessonUserRepository;
@Autowired
private RestTemplate restTemplate;
public Lesson buyById(Integer id) {
// 1. 根据id查询lesson
Lesson lesson = this.lessonRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("该课程不存在"));
// 2. 根据lesson.id查询user_lesson,那么直接返回lesson
LessonUser lessonUser = this.lessonUserRepository.findByLessonId(id);
if (lessonUser != null) {
return lesson;
}
// TODO 登录实现后需重构
Integer userId = 1;
// 3. 如果user_lesson==null && 用户的余额 > lesson.price 则购买成功
UserDTO userDTO = restTemplate.getForObject("http://localhost:8081/users/{userId}", UserDTO.class, userId);
BigDecimal money = userDTO.getMoney().subtract(lesson.getPrice());
if (money.doubleValue() < 0) {
throw new IllegalArgumentException("余额不足");
}
// TODO 购买逻辑 ... 1. 调用用户微服务的扣减金额接口 2.向lesson_user表插入数据
return lesson;
}
}
package com.cloud.msclass.controller;
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 com.cloud.msclass.domain.entity.Lesson;
import com.cloud.msclass.service.LessonService;
@RestController
@RequestMapping("lesssons")
public class LessonController {
@Autowired
private LessonService lessonService;
/**
* http://localhost:8010/lesssons/buy/1
* 购买指定id的课程
* @param id
*/
@GetMapping("/buy/{id}")
public Lesson buyById(@PathVariable Integer id) {
return this.lessonService.buyById(id);
}
}
package com.cloud.msclass;
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 MsClassApplication {
public static void main(String[] args) {
SpringApplication.run(MsClassApplication.class, args);
}
/**
* spring web提供的轻量级http client
* @return
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
http://localhost:8010/lesssons/buy/1
{"id":1,"title":"SpringCloud视频教程","cover":"xxx","price":5,"description":"SpringCloud视频教程","createTime":"2020-02-15T15:50:35.000+0000","videoUrl":"https://ke.qq.com/classroom/index.html"}
此时数据库存在如下测试数据:
此时lesson_user表没有数据