什么是微服务
微服务架构是一项在云中部署应用和服务的新技术。大部分围绕微服务的争论都集中在容器或其他技术是否能很好的实施微服务,而红帽说API应该是重点。
微服务可以在“自己的程序”中运行,并通过“轻量级设备与HTTP型API进行沟通”。关键在于该服务可以在自己的程序中运行。通过这一点我们就可以将服务公开与微服务架构(在现有系统中分布一个API)区分开来。在服务公开中,许多服务都可以被内部独立进程所限制。如果其中任何一个服务需要增加某种功能,那么就必须缩小进程范围。在微服务架构中,只需要在特定的某种服务中增加所需功能,而不影响整体进程的架构。
简单来说,微服务就是将一个大型项目的各个业务代码,拆分成多个互不干扰的小项目,而这些小项目专心完成自己的功能,而且可以调用别的小项目的方法,从而完成整体功能
京东\淘宝这样的大型互联网应用程序,基本每个操作都是一个单独的微服务在支持:
- 登录服务器
- 搜索服务器
- 商品信息服务器
- 购物车服务器
- 订单服务器
- 支付服务器
- 物流服务器
- 等等
1.微服务架构的优势
1.1易于开发和维护
一个微服务只关注一个特定的业务功能,所以它的业务清晰、代码量较少。开发和维护单个微服务相对比较简单,整个应用是由若干个微服务构建而成,所以整个应用也会维持在可控状态;
1.2单个微服务启动较快
单个微服务代码量较少,所以启动会比较快;
1.3局部修改容易部署
单体应用只要有修改,就要重新部署整个应用,微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可;
1.4技术栈不受限
在微服务中,我们可以结合项目业务及团队的特点,合理地选择技术栈
1.5按需伸缩
焦虑系统访问量大,只需要对焦虑系统进行扩展
2.微服务与SpringCloud的关系
SpringCloud简单来说是上述微服务架构技术落地实现的集合体,是微服务架构下的一站式解决方案。
SpringCloud是一个技术的集合体,将上述的微服务架构进行落地。下面看一下这个集合体里包含哪些组件:
2.1注册中心Eureka:
拆分成多个服务之后,总得有个管理多个服务的模块吧,这就是注册中心的作用。起到服务注册和服务发现的作用。
2.2网关Zuul:
拆分成多个服务之后,涉及到服务之间的调用吧,一个服务调用了三个服务的模块,那在这个服务里,配置三个调用地址,看起来是不是很麻烦呀,所以就出现了网关,所有的服务调用都调用到网关,然后在网关里配置路由,进行服务的转发,类似于代理的作用。当然网关需要配合注册中心进行使用,去发现转发到哪个服务上去
2.3负载均衡Ribbon:
网关找到请求发送到哪个服务后,如果这个服务做了集群,有多个实例,那该发送到哪个实例上呢,于是就出现了Ribbon组件,进行负载均衡。
2.4远程调用方式Feign:
服务之间相互调用的时候,可以使用RestTemplate进行调用,SpringCloud也提供了Feign客户端访问,即提供了一种远程调用方式。
2.5断路器Hystrix:
SpringCloud提供了一个监控组件,用于监控服务接口的状态和断路情况。Hystrix Dashboard是个监控面板,提供了可视化界面查看服务调用的耗时等等。
上述就是微服务的五大组件。微服务架构就是对服务进行拆分,多个小的项目,组成一个大型项目。那么,拆分后的服务,肯定需要增加中间件,来统一维护这些服务,于是就有了注册中心。拆分成小型服务后,涉及到服务之间接口的调用,于是产生了Feign技术。为了实现高可用,每个小型服务都需要集群,那么集群如何选举呢?就出现了负载均衡技术Ribbon。最后通过网关统一管理请求路径的配置,进行请求的转发。SpringCloud还为我们提供了断路器,用于监控服务的各种指标信息。
所以,SpringCloud是一套微服务架构的落地技术方案,对于程序员而言,只要在这套技术框架里面,进行业务模块的开发实现即可。
同时可以发现,我们要使用SpringCloud,还需要维护注册中心,网关等等技术,这也是使用微服务架构需要花费的成本。
3.案例
3.1创建一个父工程
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>
<modules>
<module>qy163-common</module>
<module>qy163-product</module>
<module>qy163-order</module>
</modules>
<!--引入父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<groupId>com.aaa</groupId>
<artifactId>qy163-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>
3.2公共模块
在父类下创建子类
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">
<parent>
<artifactId>qy163-parent</artifactId>
<groupId>com.aaa</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qy163-common</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<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>
</dependencies>
</project>
创建pojo实体类
Order
package com.aaa.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
/**
* @program: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:16
**/
@Data
@TableName(value = "shop_order")
public class Order {
@TableId(value="oid",type = IdType.AUTO )
private Integer oid;
private Integer uid;
private String username;
private Integer pid;
private String pname;
private BigDecimal pprice;
private Integer number;
}
Product
package com.aaa.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
/**
* @program: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:16
**/
@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;
}
3.3product商品微服务模块
pom.xml
<dependencies>
<dependency>
<groupId>com.aaa</groupId>
<artifactId>qy163-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>
配置文件
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud-product?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
主启动类
ProductApp
package com.aaa.product;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @program: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:20
**/
@SpringBootApplication
@MapperScan(basePackages = "com.aaa.product.dao")
public class ProductApp {
public static void main(String[] args) {
SpringApplication.run(ProductApp.class, args);
}
}
dao层
ProductDao
package com.aaa.product.dao;
import com.aaa.pojo.Product;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface ProductDao extends BaseMapper<Product> {
}
service层
ProductService
package com.aaa.product.service;
import com.aaa.pojo.Product;
public interface ProductService {
Product findById(Integer pid);
}
impl
ProductServiceImpl
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: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:24
**/
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product findById(Integer pid) {
Product product = productDao.selectById(pid);
return product;
}
}
controller层
ProductController
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: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:26
**/
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("getById/{pid}")
public Product getById(@PathVariable Integer pid){
Product byId = productService.findById(pid);
return byId;
}
}
启动运行
3.3order订单微服务模块
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">
<parent>
<artifactId>qy163-parent</artifactId>
<groupId>com.aaa</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>qy163-order</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.aaa</groupId>
<artifactId>qy163-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>
</project>
配置文件
server.port=8090
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud-order?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
主启动类
package com.aaa.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @program: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:33
**/
@SpringBootApplication
@MapperScan(value = "com.aaa.order.dao")
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
dao层
OrderDao
package com.aaa.order.dao;
import com.aaa.pojo.Order;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface OrderDao extends BaseMapper<Order> {
}
service层
OrderService
package com.aaa.order.service;
import com.aaa.pojo.Order;
import com.sun.org.apache.xpath.internal.operations.Or;
public interface OrderService {
Integer save(Order order);
}
impl
package com.aaa.order.service.impl;
import com.aaa.order.dao.OrderDao;
import com.aaa.order.service.OrderService;
import com.aaa.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @program: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:36
**/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public Integer save(Order order) {
return orderDao.insert(order);
}
}
controller
OrderController
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: QY163-parent
* @description:
* @author: 李云霄
* @create: 2023-05-12 20:38
**/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
//该类默认没有交给spring容器管理
@Autowired
private RestTemplate restTemplate;
@GetMapping("save")
public String save(Integer pid, Integer num) {
Order order = new Order();
order.setUid(1);
order.setUsername("李云霄");
order.setNumber(num);
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 "下单成功";
}
}
启动运行
查询数据库