1.微服务架构
1.1.单体应用架构
将项目所有模块(功能)打成jar或者war,然后部署一个进程
优点:
1:部署简单:由于是完整的结构体,可以直接部署在一个服务器上即可。
2:技术单一:项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发。
缺点:
1:系统启动慢,一个进程包含了所有的业务逻辑,涉及到的启动模块过多,导致系统的启动、重启时间周期过长;
2:系统错误隔离性差、可用性差,任何一个模块的错误均可能造成整个系统的宕机;
3:可伸缩性差:系统的扩容只能只对这个应用进行扩容,无法结合业务模块的特点进行伸缩。
4: 线上问题修复周期长:任何一个线上问题修复需要对整个应用系统进行全面升级。5: 跨语言程度差
6: 不利于安全管理,所有开发人员都拥有全量代码。
小型项目适合单体应用架构. 比如:OA办公系统。管理类的项目 仓库管理系统。
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)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
一个项目被拆分若干个项目
每个项目可以独立运行
每个项目可以使用不同的语言开发
每个项目可以拥有独立的数据库
项目与项目之间可以通信[http协议]
解读微服务特点:
1:微服务是一种==项目架构思想==(风格)
2:微服务架构是一系列小服务的组合(组件化与多服务)
3:任何一个微服务,都是一个独立的进程(独立开发、独立维护、独立部署)
4:轻量级通信http协议(跨语言,跨平台)
5:服务粒度(围绕业务功能拆分---模块拆分【系统管理服务】【日志服务】【焦虑测试】【抑郁测试系统】)
1.3微服务架构的优势
1)易于开发和维护
一个微服务只关注一个特定的业务功能,所以它的业务清晰、代码量较少。开发和维护单个微服务相对比较简单,整个应用是由若干个微服务构建而成,所以整个应用也会维持在可控状态;
⒉)单个微服务启动较快
单个微服务代码量较少,所以启动会比较快;
3)局部修改容易部署
单体应用只要有修改,就要重新部署整个应用,微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可;
4)技术栈不受限
在微服务中,我们可以结合项目业务及团队的特点,合理地选择技术栈
5)按需伸缩
焦虑系统访问量大,只需要对焦虑系统进行扩展
1.4微服务架构的缺点(挑战)
1)服务太多,导致服务间的依赖错综复杂,运维难度大
2)微服务放大了分布式架构的系列问题
- 分布式事务(seata)
- 分布式锁怎么处理(redisson) ,
- 服务注册与发现(nacos) .
- 依赖服务不稳定(sentinel)导致服务雪崩怎么办?
3)运维复杂度陡增,部署数量多、监控进程多导致整体运维复杂度提升。
如何解决上面的挑战----springcloud
1.5. SpringCloud与微服务关系
- Springcloud为微服务思想提供了完美的==解决方案==
- Springcloud是一些列框架的集合体(不同的框架解决微服务面临的不同的挑战)
springcloud提供了两款模式:第一种netflix公司 第二款:alibaba-阿里巴巴。
1.6SpringBoot和SpringCloud关系
- SpringBoot专注于快速方便的开发单个个体微服务。
- SpringCloud是关注全局的微服务协调、整理、治理的框架,它将SpringBoot开发的单体整合并管理起来。
- SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。
2.创建微服务工程
技术栈:
mysql+mybatis[mp]+springboot+springcloud alibaba
2.1订单数据库:
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 NULL DEFAULT NULL COMMENT '用户名',
`pid` bigint(0) NULL DEFAULT NULL COMMENT '商品id',
`pname` varchar(255) CHARACTER SET utf8mb4 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 ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of shop_order
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
商品数据库
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 NULL DEFAULT NULL COMMENT '用户名',
`pid` bigint(0) NULL DEFAULT NULL COMMENT '商品id',
`pname` varchar(255) CHARACTER SET utf8mb4 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 ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of shop_order
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
2.2搭建父工程
1)创建maven工程
2)引入依赖
<?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>
<groupId>com.hmq</groupId>
<artifactId>hmq-springcloud-parent01</artifactId>
<version>1.0-SNAPSHOT</version>
<!--如果你的工程是父类,那么删除src目录以及打包方式pom-->
<packaging>pom</packaging>
<!--继承springboot父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<!--定义版本号-->
<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: 它只负责jar的管理 不负责jar的下载 子类使用该类型的jar包时,直接引用而无需写版本-->
<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>
</dependencies>
</dependencyManagement>
</project>
springboot 和 springcloud 以及springcloud alibaba他们的版本必须匹配
2.3创建公共模块
1)创建maven工程
2)pom依赖
<?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>com.hmq</groupId>
<artifactId>hmq-springcloud-parent01</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>hmq-springcloud-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>
3)创建相应的实体类
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@TableName(value = "shop_product")
public class Product implements Serializable {//Serializable java序列化
//商品
@TableId(value = "pid",type = IdType.AUTO)
private Long pid;
private String pname;
private BigDecimal pprice; //double // 防止精度丢失
private Integer stock;
}
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@TableName("shop_order")
public class Order implements Serializable {
//订单
@TableId(value = "oid",type = IdType.AUTO)
private Long oid;
private Long uid;
private String username;
private Long pid;
private String pname;
private BigDecimal pprice;
private Integer number;
}
2.4商品系统
1)创建maven项目 hmq-springboot-product
2)导入pom依赖
<?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>com.hmq</groupId>
<artifactId>hmq-springcloud-parent01</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>hmq-springboot-product</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.hmq</groupId>
<artifactId>hmq-springcloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
3)常见配置文件application.properties
#端口号8001~8008
server.port=8001
#数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///shop_product?serverTimezone=Asia/Shanghai
spring.datasource.password=123456
spring.datasource.username=root
#mpd的日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
4)创建主启动类
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = "com.hmq.dao")
public class ProductApp {
public static void main(String[] args) {
SpringApplication.run(ProductApp.class,args);
}
}
5)创建dao接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmq.entiy.Product;
public interface ProductDao extends BaseMapper<Product> {
}
6)创建service层以及实现类
import com.hmq.entiy.Product;
public interface ProductService {
/**
* 根据商品id查询商品信息
* @param pid 商品id
* @return 商品信息
* */
public Product findById(Long pid);
}
实现类
import com.hmq.dao.ProductDao;
import com.hmq.entiy.Product;
import com.hmq.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
@Override
public Product findById(Long pid) {
return productDao.selectById(pid);
}
}
7)创建controller层
import com.hmq.entiy.Product;
import com.hmq.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;
@RestController
@RequestMapping("product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("getById/{id}")
public Product getById(@PathVariable Long id){
Product productServiceById = productService.findById(id);
return productServiceById;
}
}
2.5订单微服务
1)创建maven工程hmq-springcloud-order
2)引入pom依赖
<?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>com.hmq</groupId>
<artifactId>hmq-springcloud-parent01</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>hmq-springcloud-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.hmq</groupId>
<artifactId>hmq-springcloud-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
3)创建配置文件
#端口号9001~9008
server.port=9001
#数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///shop_order?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
spring.datasource.password=123456
spring.datasource.username=root
#mpd的日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
4)创建主启动类
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;
@SpringBootApplication
@MapperScan(basePackages = "com.hmq.dao")
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class,args);
}
}
5)创建dao接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmq.entiy.Order;
public interface OrderDao extends BaseMapper<Order> {
}
6)创建service层以及实现类
import com.hmq.entiy.Order;
public interface OrderService {
//添加订单
public Order save(Order order);
}
实现类
import com.hmq.dao.OrderDao;
import com.hmq.entiy.Order;
import com.hmq.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public Order save(Order order) {
int insert = orderDao.insert(order);
if (insert > 0){
return order;
}else {
throw new RuntimeException("订单添加失败!!!!!!!!!!!");
}
}
}
7)在主启动类中加入 RestTemplate
8)创建controller层
import com.hmq.entiy.Order;
import com.hmq.entiy.Product;
import com.hmq.service.OrderService;
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;
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("insert")
public String insert(Long pid, Integer num) {
//创建一个订单对象
Order order = new Order();
order.setUid(1L);
order.setUsername("锤子手机");
order.setNumber(num);
//商品信息---调用商品微服务的接口。---如何调用商品微服务的接口?--远程调用。[1]借助rabbitmq [2]http远程调用【在java端模拟浏览器调用】。
//spring封装了http远程调用 RestTemplate.默认该类没有交于spring容器管理. 创建一个配置类 写一个方法@Bean注解
/**
* String url, 远程调用的服务器的地址
* Class<T> responseType, 远程调用的接口返回类型的反射类
* Object... uriVariables: 远程接口需要传递的参数。
*/
Product product = restTemplate.getForObject("http://localhost:8001/product/getById/" + pid, Product.class);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
int i = orderService.save(order).getNumber();
return i > 0 ? "下单成功" : "下单失败";
}
}
隐藏不想展示的文件:
3.注册中心
通过RestTemplate完成远程调用存在一些问题
1.把提供者的地址写死在代码中,提供者部署的服务发生改变。那么消费者也要改变。
如果提供者是一个集群,那么消费者如何负载均衡的调用
我们如果想解决上述问题 我们需要使用注册中心。
服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现。
服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供服
务的详细信息。并在注册中心形成一张服务的清单,服务注册中心需要以心跳30s 90s的方式去监测清单中 的服务是否可用,如果不可用,需要在服务清单中剔除不可用的服务。
服务发现:服务调用方向服务注册中心咨询服务,并获取所有服务的实例清单,实现对具体服务实例的访问。
通过上面的调用图会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构非常重要
的一个组件,在微服务架构里主要起到了协调者的一个作用。注册中心一般包含如下几个功能:
1)服务注册:保存服务提供者和服务调用者的信息
服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者的信息
2)服务配置:
配置订阅:服务提供者和服务调用者订阅微服务相关的配置
配置下发:主动将配置推送给服务提供者和服务调用者
3)服务健康检测
检测服务提供者的健康情况,如果发现异常,执行服务剔除
常见的注册中心组件有哪些?
nacos:---它是阿里巴巴的组件.-----70%
eureka: ---它是netflix公司的组件---该组件已经停止更新---29% [早期的微服务项目]
zookeeper---它是apache公司的
consul:
3.1nacos注册中心
默认它启动模式为--集群模式---修改它为单机模式
双击启动脚本:
访问:
账号和密码: nacos
3.2微服务注册和拉取注册中心的内容
1)往product项目引入nacos的依赖
<!--nacos的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2)配置注册中心的地址--默认端口号8848
#为微服务定义名称
spring.application.name=hmq-product
# nacos注册中心的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
3)发现
3.3消费端
1)依赖
<!--nacos的依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2)配置文件
#为微服务定义名称
spring.application.name=hmq-order
# nacos注册中心的地址
spring.cloud.nacos.discovery.server-addr=localhost:8848
# 是否把该服务注册到注册中心 默认为true
# spring.cloud.nacos.discovery.register-enabled=false
3)order项目修改controller代码
import com.hmq.entiy.Order;
import com.hmq.entiy.Product;
import com.hmq.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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;
import java.util.List;
@RestController
@RequestMapping("order")
public class OrderController02 {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
//在springcloud中封装了一个类DiscoveryClient:该类可以获取注册中心的服务信息
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("insert")
public String insert(Long pid, Integer num) {
//创建一个订单对象
Order order = new Order();
order.setUid(1L);
order.setUsername("锤子手机");
order.setNumber(num);
//商品信息---调用商品微服务的接口。---如何调用商品微服务的接口?--远程调用。[1]借助rabbitmq [2]http远程调用【在java端模拟浏览器调用】。
//spring封装了http远程调用 RestTemplate.默认该类没有交于spring容器管理. 创建一个配置类 写一个方法@Bean注解
List<ServiceInstance> instances = discoveryClient.getInstances("hmq-product");
ServiceInstance serviceInstance = instances.get(0);
//serviceInstance.getScheme();//获取http协议
//serviceInstance.getPort();//获取服务实例的端口号
//System.out.println(serviceInstance.getUri().toString());//协议://ip:port端口号
//System.out.println(serviceInstance.getHost());//获取实例所在的ip地址
String path = serviceInstance.getUri().toString();
/**
* String url, 远程调用的服务器的地址
* Class<T> responseType, 远程调用的接口返回类型的反射类
* Object... uriVariables: 远程接口需要传递的参数。
*/
Product product = restTemplate.getForObject(path+"/product/getById/" + pid, Product.class);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
int i = orderService.save(order).getNumber();
return i > 0 ? "下单成功" : "下单失败";
}
}
4.负载均衡
4.1什么是负载均衡
通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡
而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求
我们在微服务调用关系中一般会选择
客户端负载均衡,也就是在服务调用的一方来决定服务由哪个提供者执行.
演示:---手动完成负载均衡
1)创建一个新的测试
端口号修改
开动俩个测试
上面通过手动完成了负载均衡的调用,存在的问题: 它采用的随机负载均衡,如何我想使用轮询负载均衡策略。只能修改源代码。耦合。---springcloud提供了一个组件--可以灵活的完成负载均衡。--ribbon
1)OrderApp中告诉restTemplate使用ribbon完成负载均衡
@Bean
@LoadBalanced //告诉resttemplate使用ribbon作为负载均衡器
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
2)修改controller
import com.hmq.entiy.Order;
import com.hmq.entiy.Product;
import com.hmq.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
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;
import java.util.List;
import java.util.Random;
@RestController
@RequestMapping("order")
public class OrderController03 {
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("insert")
public String insert(Long pid, Integer num) {
//创建一个订单对象
Order order = new Order();
order.setUid(1L);
order.setUsername("锤子手机");
order.setNumber(num);
//商品信息---调用商品微服务的接口。---如何调用商品微服务的接口?--远程调用。[1]借助rabbitmq [2]http远程调用【在java端模拟浏览器调用】。
//spring封装了http远程调用 RestTemplate.默认该类没有交于spring容器管理. 创建一个配置类 写一个方法@Bean注解
/**
* String url, 远程调用的服务器的地址
* Class<T> responseType, 远程调用的接口返回类型的反射类
* Object... uriVariables: 远程接口需要传递的参数。
*/
Product product = restTemplate.getForObject("http://hmq-product/product/getById/" + pid, Product.class);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
int i = orderService.save(order).getNumber();
return i > 0 ? "下单成功" : "下单失败";
}
}
3)测试
4.2ribbon提供哪些负载均衡策略
上面是ribbon内置的负载均衡策略,你也可以自定义负载均衡。
如何改变ribbon的负载均衡策略呢?---修改配置文件
# 修改调用hmq-product微服务采用的ribbon负载均衡策略
hmq-product.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
5.openfeign完成服务调用
resttemplate在完成服务调用时存在什么不好的。
Product product = restTemplate.getForObject("http://hmq-product/product/getById/" + pid, Product.class);
我们的编写程序的习惯: controller---server---dao
controller注入一个service对象---调用该对象中的方法并传递参数----返回结果交于controller层。
而openfeign它就可以完成我们编程的调用风格。
5.1什么是OpenFeign
OpenFeign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地方法一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了OpenFeign, OpenFeign负载均衡默认集成了 Ribbon, 所以在Nacos下使用OpenFeign默认就实现了负载均衡的效果。
完成openfeign的调用
1)order中依赖
<!--openfeign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)创建openfeign接口
import com.hmq.entiy.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "hmq-product")
public interface ProductFeign {
//接口的方法,必须和被调用者的接口的参数一致
@GetMapping("/product/getById/{id}")
public Product getById(@PathVariable Integer id);//springcloud 扫描到@FeignClient注解时--生产一个代理实现类
}
3)开启openfeign注解驱动
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@MapperScan(basePackages = "com.hmq.dao")
@EnableFeignClients //开启openfeign注解驱动
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class,args);
}
//RestTemplate交于spring容器管理
@Bean
@LoadBalanced //告诉resttemplate使用ribbon作为负载均衡器
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
4)修改controller
import com.hmq.entiy.Order;
import com.hmq.entiy.Product;
import com.hmq.feign.ProductFeign;
import com.hmq.service.OrderService;
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;
@RestController
@RequestMapping("order")
public class OrderController04 {
@Autowired
private OrderService orderService;
@Autowired
private ProductFeign productFeign;
@GetMapping("insert")
public String insert(int pid, Integer num) {
//创建一个订单对象
Order order = new Order();
order.setUid(1L);
order.setUsername("锤子手机");
order.setNumber(num);
//商品信息---调用商品微服务的接口。---如何调用商品微服务的接口?--远程调用。[1]借助rabbitmq [2]http远程调用【在java端模拟浏览器调用】。
//spring封装了http远程调用 RestTemplate.默认该类没有交于spring容器管理. 创建一个配置类 写一个方法@Bean注解
/**
* String url, 远程调用的服务器的地址
* Class<T> responseType, 远程调用的接口返回类型的反射类
* Object... uriVariables: 远程接口需要传递的参数。
*/
Product product = productFeign.getById(pid);
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
int i = orderService.save(order).getNumber();
return i > 0 ? "下单成功" : "下单失败";
}
}
6.gateway网关