一、微服务技术栈
另外,为了监控,还有"分布式日志服务"和"系统监控链路追踪":
最后,利用Jenkins技术对微服务进行自动化编译,再利用docker进行打包形成镜像,再基于k8s或者rancher去实现自动化的部署,这一套机制称为持续集成。
微服务技术栈包括微服务技术和持续集成。
1.1 认识微服务
1.1.1 单体架构
单体架构 :将业务的所有功能集中在一个项目中开发,打成一个包部署。
优点:架构简单;部署成本低;
缺点:耦合度高。
1.1.2 分布式架构
分布式架构 :根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。
优点:降低服务耦合;有利于服务升级拓展。
分布式架构中一个服务调用另一个项目的接口需要利用远程调用。
微服务是一种经过良好架构设计的分布式架构方案。
二、服务拆分与远程调用
比如现在有一个需求是查询订单,同时把订单里关联的用户信息、商品信息都查出来。以前的开发模式是先查订单,再根据订单里的用户id去查用户信息及商品id去查商品信息。这些步骤违背了分布式架构的单一职责原则 ,订单模块只做与订单相关的业务,用户查询和商品查询的任务不由订单模块来做。
2.1 服务拆分注意事项
微服务拆分有以下注意事项:
(1)不同微服务,不要重复开发相同业务;
(2)微服务数据独立,不要访问其它微服务的数据库;
(3)微服务可以将自己的业务暴露为接口,供其它微服务调用。
2.2 案例
打开Services窗口可以查看项目中所有的服务:
有两个服务:user_service和order-service,分别连接两个不同的数据库(这里为了测试方便放在一个mysql服务中)。
不同的数据库分别执行预先准备好的sql文件,加载数据:
cloud-order.sql内容如下:
sql
/*
Navicat Premium Data Transfer
Source Server : local
Source Server Type : MySQL
Source Server Version : 50622
Source Host : localhost:3306
Source Schema : heima
Target Server Type : MySQL
Target Server Version : 50622
File Encoding : 65001
Date: 01/04/2021 14:57:18
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_order
-- ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名称',
`price` bigint(20) NOT NULL COMMENT '商品价格',
`num` int(10) NULL DEFAULT 0 COMMENT '商品数量',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of tb_order
-- ----------------------------
INSERT INTO `tb_order` VALUES (101, 1, 'Apple 苹果 iPhone 12 ', 699900, 1);
INSERT INTO `tb_order` VALUES (102, 2, '雅迪 yadea 新国标电动车', 209900, 1);
INSERT INTO `tb_order` VALUES (103, 3, '骆驼(CAMEL)休闲运动鞋女', 43900, 1);
INSERT INTO `tb_order` VALUES (104, 4, '小米10 双模5G 骁龙865', 359900, 1);
INSERT INTO `tb_order` VALUES (105, 5, 'OPPO Reno3 Pro 双模5G 视频双防抖', 299900, 1);
INSERT INTO `tb_order` VALUES (106, 6, '美的(Midea) 新能效 冷静星II ', 544900, 1);
INSERT INTO `tb_order` VALUES (107, 2, '西昊/SIHOO 人体工学电脑椅子', 79900, 1);
INSERT INTO `tb_order` VALUES (108, 3, '梵班(FAMDBANN)休闲男鞋', 31900, 1);
SET FOREIGN_KEY_CHECKS = 1;
cloud-user.sql内容如下:
sql
/*
Navicat Premium Data Transfer
Source Server : local
Source Server Type : MySQL
Source Server Version : 50622
Source Host : localhost:3306
Source Schema : heima
Target Server Type : MySQL
Target Server Version : 50622
File Encoding : 65001
Date: 01/04/2021 14:57:18
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人',
`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, '柳岩', '湖南省衡阳市');
INSERT INTO `tb_user` VALUES (2, '文二狗', '陕西省西安市');
INSERT INTO `tb_user` VALUES (3, '华沉鱼', '湖北省十堰市');
INSERT INTO `tb_user` VALUES (4, '张必沉', '天津市');
INSERT INTO `tb_user` VALUES (5, '郑爽爽', '辽宁省沈阳市大东区');
INSERT INTO `tb_user` VALUES (6, '范兵兵', '山东省青岛市');
SET FOREIGN_KEY_CHECKS = 1;
刷新一下表就导进来了:
2.2.1 需求1
要实现传订单id给订单模块查询订单信息时订单模块返回订单信息和用户信息的功能,需要订单模块根据订单里的用户id发送HTTP请求给用户模块查询用户信息,再将获得的用户信息及查出的订单信息一起返回。
2.2.1.1 注册RestTemplate(发送HTTP请求)
未使用远程调用之前:
查询结果:
使用restTemplate发起http请求查询用户信息:
效果:
注意:http请求做远程调用是与语言无关的调用,只要知道对方的ip、端口、接口路径、请求参数即可。
三、Eureka(发音"衣瑞可")
在2.2.1.1的案例中,http请求的ip地址和端口号是写死在代码中的,,这在实际生产环境中是十分不适用的。项目开发有开发环境、测试环境和生产环境,且生产环境可能将服务部署到多台服务器上,且需要保证选中的接受请求的服务器正常,这些工作可以用Eureka来实现。
3.1 作用
Eureka是一个注册中心。
user-service服务启动时向Eureka注册。
注意:上图中order-service想要调用user-service的接口,先告知注册中心服务名(user-service),注册中心返回对应的ip和端口号,order-service再用ip和端口号去调用需要的接口。这样,当user-service的ip和端口号改变或者是集群模式时,order-service调用的方式不变。
3.2 实践
3.2.1 搭建一个Eurake服务
上述配置文件是在做服务的注册。
将一个服务复制一份,
注意:上图中设置的名字不是服务的名字,只是为了便于区分。服务名字在项目的配置文件中设置了,
右边的-Dserver是设置复制的服务的端口号。
复制的服务也启动:
3.2.2 将服务注册到Eureka
注意:这里的依赖是client,而eureka服务的依赖是server。
3.2.3 在另一个服务中完成服务拉取
@LoadBalanced为负载均衡注解。
可以看到,url里的ip地址和端口号用服务名代替。
3.3 Ribbon负载均衡
3.3.1 饥饿加载
四、Nacos
Nacos也是注册中心,是阿里巴巴的产品,现在是springcloud中的一个组件。相比Eureka功能更加丰富。
4.1 安装启动
安装解压nacos。
在bin目录下启动:
powershell
startup.cmd -m standalone
打开上图中的console地址:
默认账号和密码都是nacos。
4.2 服务注册到nacos
修改完项目配置,启动服务后:
4.3 Nacos服务分级存储模型
4.3.1 集群的引入
4.3.2 集群属性的配置
配置集群的属性:
一个例子:
如下图所示,将UserApplication和UserApplication2配置为HZ集群,UserApplication3配置为SH集群:
order-service配置HZ集群:
但其实这里时候order-service去访问user-service的服务,还是会采用轮询的方式,也即集群模式并未生效。想要集群模式生效还需要配置负载均衡规则。
4.3.3 配置负载均衡规则NacosRule
配置完后,order-service访问user-service会有限访问UserApplication和UserApplication2。
若是将UserApplication和UserApplication2关闭,则会访问UserApplication3且会再日志打印跨集群访问信息。
4.3.4 根据权重负载均衡
在实际部署中,服务器设备性能有差异,部分示例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。
Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
配置方式如下:
如下图:
将Application(8081)和Application2(8082)权重分别配置为0.1和0.2,测试发现连续访问20次user-service接口,18次落在Application(8081),2次Application2(8082)。
补充:权重负载均衡应用-重启服务
在实际生产中,我们想要升级一个服务,不能直接将其重启(可能还有用户访问)。我们可以将集群中的一个实例的权重设为0,该实例逐渐地没有了请求,此时可以将该服务关闭,并重启升级了地服务。重启后也将服务权重设置得较小,则是少量用户的访问情况,若没问题则加大该实例的权重。
4.4 Nacos环境隔离namespace
Nacos不仅是一个注册中心,也是一个数据中心。Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离。
Nacos的默认命名空间为public:
服务默认在该命名空间:
新建命名空间:
配置服务所属的命名空间:
将命名空间的ID改为生成的:
重启服务:
此时,order-service和user-service属于两个命名空间,不能相互访问:
所以,想要服务能互相访问需要放到统一命名空间。
4.5 Nacos与Eureka的区别
4.5.1 Nacos和Eureka的共同点
(1)都支持服务注册和服务拉取;
(2)都支持服务提供者心跳方式做健康检测(即服务提供者定时告知注册中心自己在正常运行)。
4.5.2 Nacos和Eureka的区别
4.5.2.1 Nacos包括临时和非临时实例
Nacos的实例包括临时实例和非临时实例(默认是临时实例):
临时实例采用心跳检测(与Eureka类似):
若是注册中心检测不到临时实例的心跳,则将其直接从服务列表中剔除。
非临时实例不要求做心跳,而是由nacos注册中心直接发请求询问,且检测不到不会 将其从服务列表中剔除 ,等待非临时实例回复正常:
设置临时实例与非临时实例的方法:
例如:
4.5.2.2 Nacos支持服务列表变更的消息推送模式
服务消费者并不是每次发请求都向注册中心拉取,而是有一个服务列表缓存,拉取完一次在短时间内不用拉取而是直接使用列表中的值,在这些值中选择一个发起远程调用。
若是服务提供者的实例挂了,服务消费者使用服务列表缓存中的值去发送远程调用会出问题。nacos的注册中心由主动推送并更消息的机制(每隔一段时间推送一次,且若有服务挂了立即推送消息)
4.6 Nacos配置管理
统一配置管理:
当生产中有多个微服务时,多个服务的配置文件(项目中的application.yml)的设置项相同,我们希望对其统一管理,则可以使用一个统一的配置文件。服务启动时会读取配置服务管理中的配置并与本地配置结合生效。且若需要修改配置服务管理中的配置信息,可以直接修改,nacos会通知到服务,服务会重新读取,不需要重启。
4.6.1 Nacos实现配置管理
说明:Data ID为配置文件的id([服务名称].[profile].[后缀名])。
上图中的配置会写在一个优先级比application.yml高的文件(后续论述)。
4.6.2 微服务配置拉取
如上图,spring中有一个读取优先级比application.yml更高的bootstrap.yml文件,可以在该文件中配置nacos的地址,之后读取nacos的配置文件,再去读取本地的配置文件。
步骤如下:
后两步内容如下:
之后修改配置文件即可。
补充:可以看出,配置文件包括nacos地址与nacos配置文件ID:
例如:
在nacos配置文件中配置一个dateformat字段。
可以看到服务生效:
4.6.3 配置热更新
此时去访问服务并未生效:
4.6.4 多环境共享配置
多环境共享的目标是不论是开发、测试还是生产环境都读取该nacos配置文件。
注意:
若是多个配置文件中,优先级如下:
4.6.5 Nacos集群搭建
步骤:
(1)搭建MySQL集群并初始化数据库表;
(2)下载解压nacos安装包;
(3)修改集群配置(节点信息)、数据库配置;
(4)启动nacos集群;
(5)nginx(恩居客丝)反向代理。