1、微服务架构
1.1 单体应用架构
将项目所有模块(功能)打成jar或者war,然后部署一个进程
优点: 1:部署简单:由于是完整的结构体,可以直接部署在一个服务器上即可。 2:技术单一:项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发。
缺点: 1:系统启动慢,一个进程包含了所有的业务逻辑,涉及到的启动模块过多,导致系统的启动、重启时间周期过长;
2:系统错误隔离性差、可用性差,任何一个约块的错误均可能造成整个系统的宕机;
3:可伸缩性差:系统的扩容只能只对这个应用进行扩容,无法结合业务模块的特点进行伸缩。
4: 线上问题修复周期长:任何一个线上问题修复需要对整个应用系统进行全面升级。
5: 跨语言程度差
6: 不利于安全管理,所有开发人员都拥有全量代码
1.2 微服务应用
微服务架构论文: Microservices
译文: 微服务译文理解_微服务架构翻译_发了个版的博客-CSDN博客
In short, the microservice architectural style [1] is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
简单来说,微服务架构风格[1]是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
解读微服务特点:
1:微服务是一种项目架构思想(风格)
2:微服务架构是一系列小服务的组合(组件化与多服务)
3:任何一个微服务,都是一个独立的进程(独立开发、独立维护、独立部署)
4:轻量级通信http协议(跨语言,跨平台)
5:服务粒度(围绕业务功能拆分)
6:去中心化管理(去中心化"地治理技术、去中心化地管理数据)
1.3 微服务架构的优势
1.易于开发和维护 一个微服务只关注一个特定的业务功能,所以它的业务清晰、代码量较少。开发和维护单个微服务相对比较简单,整个应用是由若干个微服务构建而成,所以整个应用也会维持在可控状态;
⒉.单个微服务启动较快 单个微服务代码量较少,所以启动会比较快;
3.局部修改容易部署 单体应用只要有修改,就要重新部署整个应用,微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可;
4.技术栈不受限 在微服务中,我们可以结合项目业务及团队的特点,合理地选择技术栈
5.按需伸缩
1.4:微服务架构的缺点(挑战)
1、服务太多,导致服务间的依赖错综复杂,运维难度大
2、微服务放大了分布式架构的系列问题
分布式事务(seata)
分布式锁怎么处理(redisson) ,
服务注册与发现(nacos) .
依赖服务不稳定(sentinel)导致服务雪崩怎么办?
3、运维复杂度陡增,部署数量多、监控进程多导致整体运维复杂度提升。
1.5. SpringCloud与微服务关系
Springcloud为微服务思想提供了完美的解决方案
Springcloud是一些列框架的集合体(服务的注册与发现【注册中心】、服务间远程调用、服务降级、服务熔断、服务限流、分布式事务等)
一般我们说springc1oud 其实指的是Springc1oud-netflix,Springcloud并不是造轮子,只是把Netflix公司的组件做二次开发
1.6: SpringBoot和SpringCloud关系
SpringBoot专注于快速方便的开发单个个体微服务。
SpringCloud是关注全局的微服务协调、整理、治理的框架,它将SpringBoot开发的单体整合并管理起来。
SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系
2、SpringCloud架构
maven:3.5.0+
数据库:MySQL5.7以上
持久层:Mybatis-plus 《Mybatis Mapper Mybatis-plus》
其他:SpringCloud Alibaba技术栈 druid
2.1 创建一个父工程
src可以删除
package pom打包
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.3.12.RELEASE</version>
</parent>
<groupId>com.jmy</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!--父工程那么它的打包方式必须为pom打包-->
<packaging>pom</packaging>
<!--定义版本号-->
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<!--管理了cloud版本-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--alibaba的版本-->
<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>
</dependencies>
</dependencyManagement>
</project>
2.2 创建公共工程
(1) 公共工程pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jmy</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>common</finalName>
</build>
</project>
(2)sql 两个数据表的sql语句
先新建两个数据库
shop_product表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for shop_product
-- ----------------------------
DROP TABLE IF EXISTS `shop_product`;
CREATE TABLE `shop_product` (
`pid` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '商品id',
`pname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品名',
`pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格',
`stock` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品库存',
PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of shop_product
-- ----------------------------
INSERT INTO `shop_product` VALUES (1, '华为手机', 1999.00, '100');
INSERT INTO `shop_product` VALUES (2, 'vivo手机', 2999.00, '100');
INSERT INTO `shop_product` VALUES (3, '小敏', 2222.00, '1000');
SET FOREIGN_KEY_CHECKS = 1;
shop_order表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for shop_order
-- ----------------------------
DROP TABLE IF EXISTS `shop_order`;
CREATE TABLE `shop_order` (
`oid` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '订单id',
`uid` int(0) NULL DEFAULT NULL COMMENT '用户id',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
`pid` bigint(0) NULL DEFAULT NULL COMMENT '商品id',
`pname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品名称',
`pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格',
`number` int(0) NULL DEFAULT NULL COMMENT '购买数量',
PRIMARY KEY (`oid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 44956 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of shop_order
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
(3)实体类
@Data
@TableName(value = "shop_product")
public class Product {
@TableId(value = "pid",type = IdType.AUTO)
private Integer pid;
private String pname;
//小数类型必须使用BigDecimal
private BigDecimal pprice;
private Integer stock;
}
@Data
@TableName(value = "shop_order")
public class Order {
@TableId(value = "ord",type = IdType.AUTO)
private Integer oid;
private Integer uid;
private String username;
private Integer pid;
private String pname;
//小数类型必须使用BigDecimal
private BigDecimal pprice;
private Integer number;
}
2.3 product商品微服务
(1)pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.aaa</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>product</artifactId>
<dependencies>
<dependency>
<groupId>com.aaa</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>product</finalName>
</build>
</project>
(2)application.properties 配置文件
# 端口号:8080~8090
server.port=8080
#数据源
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud-product?serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=admin
#打印 sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(3)主启动类
package com.aaa.dao;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 17:44
**/
@SpringBootApplication
@MapperScan(basePackages = "com.aaa.product.dao")
public class ProductApp {
public static void main(String[] args) {
SpringApplication.run(ProductApp.class,args);
}
}
(4)dao层
package com.aaa.product.dao;
import com.aaa.pojo.Product;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 17:50
**/
public interface ProductDao extends BaseMapper<Product> {
}
(5)service层
package com.aaa.product.service;
import com.aaa.pojo.Product;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 17:53
**/
public interface ProductService {
//根据id查询商品信息
public Product findById(Integer pid);
}
(6) service实现类impl
package com.aaa.product.service.impl;
import com.aaa.pojo.Product;
import com.aaa.product.dao.ProductDao;
import com.aaa.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 17:57
**/
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product findById(Integer pid) {
Product product = productDao.selectById(pid);
return product;
}
}
(7) controller层
package com.aaa.product.controller;
import com.aaa.pojo.Product;
import com.aaa.product.service.ProductService;
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;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 17:59
**/
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("getById/{pid}")
public Product getById(@PathVariable Integer pid){
Product product = productService.findById(pid);
return product;
}
}
(8)启动项目
2.4 order订单微服务
(1)pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.aaa</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>order</artifactId>
<dependencies>
<dependency>
<groupId>com.aaa</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>order</finalName>
</build>
</project>
(2) application.properties 配置文件
# 端口号:8091~8099
server.port=8090
#数据源
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud-order?serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=admin
#打印 sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(3) 主启动类
package com.aaa.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 18:46
**/
@SpringBootApplication
@MapperScan(basePackages = "com.aaa.order.dao")
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class,args);
}
}
(4) dao层
public interface OrderDao extends BaseMapper<Order> {
}
(5)service层
public interface OrderService {
public Integer save(Order order);
}
(6) service实现类impl
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public Integer save(Order order) {
return orderDao.insert(order);
}
}
(7) controller层
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
controller代码
package com.aaa.order.controller;
import com.aaa.order.service.OrderService;
import com.aaa.pojo.Order;
import com.aaa.pojo.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @program: parent
* @author: Jmy
* @create: 2023-05-12 18:56
**/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
//该类默认没有交给spring容器管理
@Autowired
private RestTemplate restTemplate;
@GetMapping("save")
public String save(Integer pid,Integer num){ //需要传入 商品id,数量信息
Order order = new Order();
order.setUid(1);
order.setUsername("猪八戒");
order.setNumber(num);
//商品信息的设置:远程调用商品微服务的接口,基于Http协议进行远程调用
//两种基于HTTP调用的方式:
//第一种:可以使用httpclient工具jar 自己封装工具类(不用)
//第二种:spring框架中提供了一个工具类,RestTemplate
Product product = restTemplate.getForObject("http://localhost:8080/product/getById/" + pid, Product.class);
if(product == null){
return "下单失败";
}
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
orderService.save(order);
return "下单成功";
}
}
(8)启动项目
设置订单id从1开始递增
保存到数据库
3、面试题
1.重载和重写的区别?
重载发生在一个类中,同名的方法如果有不同的参数列表(类型不同、个数不同、顺序不同)则视为重载。重写发生在子类与父类之间,重写要求子类重写之后的方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常。
2.String 和 StringBuffer、StringBuilder 的区别是什么?
可变性String类中使用final关键字修饰字符数组来保存字符串,所以String对象是不可变的。而StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[]value但是没有用final关键字修饰,所以这两种对象都是可变的。线程安全性String中的对象是不可变的,也就可以理解为常量,线程安全。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。性能每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的风险
3.== 与 equals 的区别?
对于基本类型和引用类型==的作用效果是不同的,基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同;
equals本质上就是==,只不过String和Integer等重写了equals方法,把它变成了值比较
4.说说自己是怎么使用synchronized 关键字,在项目中用到了吗?
修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。双重校验锁实现对象单例(线程安全)
5.抽象类和接口的区别是什么?
实现:抽象类的子类使用extends来继承;接口必须使用implements来实现接口。构造函数:抽象类可以有构造函数;接口不能有。实现数量:类可以实现很多个接口;但只能继承一个抽象类【java只支持单继承】。访问修饰符:接口中的方法默认使用public修饰;抽象类中的抽象方法可以使用Public和Protected修饰,如果抽象方法修饰符为Private,则报错:Theabstractmethod方法名intypeTestcanonlysetavisibilitymodifier,oneofpublicorprotected。接口中除了static、final变量,不能有其他变量,而抽象类中则不一定设计层面:抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。