微服务 ≠ SpringCloud

更具体点:

平时普通的项目都是单体架构,不利于大型项目开发。

大型项目常常是分布式架构

微服务架构特征:

单体架构、分布式架构、微服务架构特点:

微服务技术对比:

还是新建一个 SpringBoot 项目cloud-demo,然后再其下面再建 SpringBoot 项目来代指不同功能模块。
父项目pom文件主要组成:
<modules>
<module>user-servicemodule>
<module>order-servicemodule>
modules>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.9.RELEASEversion>
<relativePath/>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.SR10spring-cloud.version>
<mysql.version>5.1.47mysql.version>
<mybatis.version>2.1.1mybatis.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
dependencyManagement>
这里举例了两个模块:用户模块和订单模块。
用户模块写了个根据用户id查询用户的功能。
订单模块写了个根据订单id查询到订单功能,并根据订单上的用户id查询到用户。
因为订单模块要跨模块去访问用户模块,因此在订单模块的启动类中,创建RestTemplate并注入Spring容器,且配置负载均衡,当访问的用户模块有多个实例,则通过负载均衡可以指定规则
@Bean
@LoadBalanced // 负载均衡注解,比如要访问的用户模块有多个实例,则通过负载均衡可以指定规则,可以是每次随机,也可以是均匀等。
public RestTemplate restTemplate() {
return new RestTemplate();
}
注册中心是用来统一管理各个微服务模块的地址的。 用来管理各个微服务模块之间的访问。
下载安装包:
https://github.com/alibaba/nacos/releases
windows:
解压后进入 bin 目录
进入cmd,执行命令: startup.cmd -m standalone
父工程添加 nacos 的 管理依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.5.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
两个子工程都添加 nacos 的客户端依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
订单模块 application.yml 配置:
server:
port: 8088
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
application: # 订单模块服务名称配置
name: orderservice
cloud: # nacos 配置
nacos:
server-addr: localhost:8848 # nacos服务地址
userservice: # 用户模块的服务名称为userservice,这里意思是仅针对此服务多个实例的负载均衡规则。
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
ribbon:
eager-load:
enabled: true # 开启饥饿加载:普通加载是在发起请求时才加载配置,会导致第一次请求很慢,开启饥饿加载后会在项目启动后就加载配置,发起请求后的响应就会快一点。
clients: # 指定饥饿加载的服务名称
- userservice
用户模块 application.yml 配置:
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
application: # 用户模块服务名称配置,用于别的模块调用
name: userservice
cloud:
nacos:
server-addr: localhost:8848
用户模块新增实例:


配置完后,可以在订单模块获取到用户模块信息:

一般情况下 会将一个服务模块 的多个实例部署在多个不同的地方(机房)!在同一个机房的实例称为一个集群。

修改用户模块的 application.yml 文件,添加如下内容:
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务端地址
discovery:
cluster-name: SH # 配置集群名称,也是机房位置,例如:SH,上海
然后启动 UserApplication 和 UserApplication 2
将 cluster-name 改为 HZ 再启动 UserApplication 3
在 nacos 服务器网站上 http://192.168.31.41:8848/ 查看:

服务详情中:

总结:

订单模块可通过配置自定义选择用户模块的实例:
在订单模块 application.yml 下也做相似配置:
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务端地址
discovery:
cluster-name: HZ # 配置集群名称,也是机房位置,例如:HZ,杭州
# 还需配置负载均衡。否则不起效,配置为优先选择同集群下实例
userservice: # 仅针对 userservice服务,即订单模块某一实例访问用户模块时,会优先选择与订单模块实例同集群的用户模块实例
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则,优先选择同集群下的实例,若有多个,随机访问。若同集群下没有则会跨级访问并发出警告。

有时我们想让某些实例优先访问,可以配置实例的权重!比如有些实例要更新可以先把它权重调为0,有些性能差的可以调低一点让访问它的次数小一点。
在 nacos 服务器上查看到一个服务的各个实例权重相同。

通过编辑可以修改权重。

修改过后,订单模块每次访问用户模块优先访问到权重高的实例。

默认情况下 所有的服务都位于 nacos 默认的 public 空间下。

新建一个 命名空间

默认服务都在 public 命名空间下,dev为空。需要自己在 代码配置文件中 根据命名空间id 指定。

将用户模块 application.yml 文件更改如下:
spring:
cloud:
nacos:
server-addr: localhost:8848 # nacos 服务端地址
discovery:
cluster-name: HZ # 配置集群名称,也是机房位置,例如:HZ,杭州
namespace: 3dc09582-5898-4b36-be3c-4eac2b31ef07
然后重新启动 在 nacos 服务器查看到 userservice 服务已被添加到 dev 命名空间

这个时候如果 订单模块 再调用 用户模块的话就会报错,因为这两个服务已经不在同一个命名空间内了,由于隔离,无法相互访问。

总结:

默认采用临时实例:服务定时给 nacos 注册中心发送心跳,若超过一定时间没发,则剔除此服务。
可以指定服务application.yml配置设定为非临时实例:不采用心跳方式,而是 nacos 注册中心主动去检测服务是否还在。
spring:
cloud:
nacos:
discovery:
ephemeral: false # 是否是临时实例


AP强调可用性和可靠性
CP强调一致性和可靠性
考虑一个场景:当一个配置文件需要修改,且这次修改可能与数十个微服务实例都有关系。那么如果每个微服务实例都修改后重启的话太麻烦了。因此我们希望这些个配置可以统一的修改,在一个地方完成改动,并且改动完后不需要重启,这些服务就立马能够生效!为实现它,引入 nacos 统一配置管理。
在 nacos 服务器可新增配置


项目启动读取过程:

1、每个服务都导入nacos配置管理依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
2、在userservice中的resource目录添加一个 bootstrap.yml 文件,这个文件是引导文件,优先级高于 application.yml。
spring:
application:
name: userservice
profiles:
active: dev # 这里指的不是命名空间,这里指定的 userservice 和 dev 则启动的实例会在nacos服务器中拿到 userservice-dev.yaml 文件中的配置。
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
config:
file-extension: yaml # 文件后缀名
# 这个bootstrap的作用就是找到nacos中的 userservice-dev.yaml 配置文件
3、书写接口拿到userservice-dev.yaml中的数据
方式一:通过@Value拿到配置值
@Value("${pattern.dateformat}")
private String dateformat;
@GetMapping("now")
public String now(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
}

使用@Value的这种方式,要想实现热更新,还需在类上加上 @RefreshScope 注解

方式二:通过@ConfigurationProperties

@Autowired
private PatternProperties properties;
@GetMapping("prop")
public String properties(){
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat()));
}
两种方式下,修改配置后:

刷新请求页面,立即生效!

总结:

在nacos服务器中 配置文件 多个实例可以共享:
新建一个配置 userservice.yaml 这种命名形式意为共享的,每个指定服务名称为userservice的实例都会共享到此配置。
还有一个创建的配置文件是 userservice-dev.yaml 指代 dev 环境实例配置文件,每个指定服务名称为userservice,且指定环境 profiles.active 为 dev可访问到此配置文件。

然后启动两个userservice实例
一个指定profile.active 为 dev,即它可以拿到 userservice.yaml 和 userservice-dev.yaml 的值
一个指定profile.active 为 test(不存在此配置),它只能拿到 userservice.yaml 的值,拿不到 userservice-dev.yaml 的值
测试如下:


userservice 1:既能访问到共享的配置文件 userservice.yaml 的值,也能访问到 userservice-dev.yaml 中的值

userservice 2:只能访问到共享的配置文件 userservice.yaml 的值,不能访问到 userservice-dev.yaml 中的值

最后再说一下优先级问题。如果在本地代码中也设置了与 nacos 服务器中配置相同的属性。则默认会优先执行 nacos 服务器的。

由上图可以看到优先级配置顺序为:
(1)bootstrap.yml 指定的 服务名-profile.yml
(2) bootstrap.yml 指定的 服务名.yml
(3) 本地 application.yml 配置。

在 localhost 下 新建一个 nacos 数据库,然后执行以下sql语句
CREATE TABLE `config_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) DEFAULT NULL,
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
`c_desc` varchar(256) DEFAULT NULL,
`c_use` varchar(64) DEFAULT NULL,
`effect` varchar(64) DEFAULT NULL,
`type` varchar(64) DEFAULT NULL,
`c_schema` text,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_aggr */
/******************************************/
CREATE TABLE `config_info_aggr` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(255) NOT NULL COMMENT 'group_id',
`datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
`content` longtext NOT NULL COMMENT '内容',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`app_name` varchar(128) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_beta */
/******************************************/
CREATE TABLE `config_info_beta` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info_tag */
/******************************************/
CREATE TABLE `config_info_tag` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL COMMENT 'content',
`md5` varchar(32) DEFAULT NULL COMMENT 'md5',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
`src_user` text COMMENT 'source user',
`src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_tags_relation */
/******************************************/
CREATE TABLE `config_tags_relation` (
`id` bigint(20) NOT NULL COMMENT 'id',
`tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
`tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
`data_id` varchar(255) NOT NULL COMMENT 'data_id',
`group_id` varchar(128) NOT NULL COMMENT 'group_id',
`tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
`nid` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`nid`),
UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = group_capacity */
/******************************************/
CREATE TABLE `group_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = his_config_info */
/******************************************/
CREATE TABLE `his_config_info` (
`id` bigint(64) unsigned NOT NULL,
`nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`data_id` varchar(255) NOT NULL,
`group_id` varchar(128) NOT NULL,
`app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
`content` longtext NOT NULL,
`md5` varchar(32) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`src_user` text,
`src_ip` varchar(50) DEFAULT NULL,
`op_type` char(10) DEFAULT NULL,
`tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
PRIMARY KEY (`nid`),
KEY `idx_gmt_create` (`gmt_create`),
KEY `idx_gmt_modified` (`gmt_modified`),
KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = tenant_capacity */
/******************************************/
CREATE TABLE `tenant_capacity` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
`quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
`usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
`max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
`max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
`max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
`max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
CREATE TABLE `tenant_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`kp` varchar(128) NOT NULL COMMENT 'kp',
`tenant_id` varchar(128) default '' COMMENT 'tenant_id',
`tenant_name` varchar(128) default '' COMMENT 'tenant_name',
`tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
`create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
`gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
`gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
CREATE TABLE `users` (
`username` varchar(50) NOT NULL PRIMARY KEY,
`password` varchar(500) NOT NULL,
`enabled` boolean NOT NULL
);
CREATE TABLE `roles` (
`username` varchar(50) NOT NULL,
`role` varchar(50) NOT NULL,
UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE
);
CREATE TABLE `permissions` (
`role` varchar(50) NOT NULL,
`resource` varchar(255) NOT NULL,
`action` varchar(8) NOT NULL,
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
在一台电脑上部署三个nacos节点。
进入nacos 的 conf目录,修改配置文件 cluster.conf.example,重命名为 cluster.conf 然后打开添加如下内容:

192.168.18.110 是我本机的实际地址(通过cmd 输入 ifconfig 查看无线局域网的那个 ipv4)。
然后修改 application.properties文件,添加数据库配置

然后将nacos 文件夹赋值为3份,重命名为 nacos1 nacos2 nacos3。
分别修改 三个文件夹中 application.properties文件,修改端口号为 8845 8846 8847
随后分别进入三个文件夹中的bin 打开cmd 输入 startup.cmd 启动三个 nacos
然后配置nginx,修改 conf下的 nginx.conf 文件
在 http {} 中任意位置 增添如下内容:
upstream nacos-cluster {
server 127.0.0.1:8845;
server 127.0.0.1:8846;
server 127.0.0.1:8847;
}
server {
listen 80; // 只要访问80/nacos 就会代理到 127.0.0.1:8845/nacos 或 127.0.0.1:8846/nacos 或 127.0.0.1:8847/nacos
server_name localhost;
location /nacos {
proxy_pass http://nacos-cluster;
}
}
配置过后 在nginx 文件夹打开 cmd 然后 start nginx.exe 启动 nginx。
这些都配置好后,浏览器访问 localhost:80/nacos
跳转到 nacos 服务器页面!

此时java代码修改的话 只需将连接的nacos 端口号改为 80 即可!

在nacos服务器 添加一个配置:

添加后会发现数据库 config_info 表 多了一条记录,完成了持久化。

总结:

上面在介绍跨模块访问时,采用的是注入 RestTemplate 模板的方式。
RestTemplate 方式调用存在的问题:

Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
1、引入依赖。我们这里是订单模块调用用户模块, 因此在订单模块的pom文件下加依赖(哪个模块要跨模块访问就在哪个模块下引入依赖)
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
2、在订单模块的启动类添加注解开启Feign的功能:
@EnableFeignClients
3、编写Feign客户端
订单模块调用用户模块,在订单模块定义一个访问用户模块的接口。使用 @FeignClient 注解,并指定 value值为 userservice,指定访问的是用户模块。

然后在查询接口中注入此客户端,并调用

启动 orderservice 和 3个userservice实例,访问 localhost:8088/order/101,访问15次。会发现每个实例几乎是均匀5次。也就是说Feign自带负载均衡!
总结:






Feign添加HttpClient的支持,在订单模块pom文件引入依赖:
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-httpclientartifactId>
dependency>
appliation.yml 配置连接池:
feign:
client:
config:
default: # 默认影响到全局的服务
loggerLevel: basic # 日志级别,basic就是基本的请求和响应信息
httpclient:
enabled: true # 开启对feign对HttpClient的支持
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 每个路径的最大连接数
考虑到这样一个问题:当有很多服务模块都调用 用户服务,那么每一个服务都写一个 UserClient,可见比较冗余。
因此可采用抽取的方式:将 FeignClient 抽取为独立模块,并且把与 FeignClient 接口有关的 pojo、默认的Feign配置都放到这个模块中,提供给所有消费者使用。

实现步骤:

新建module feign-api

在 order-service 添加feign的统一api

然后使用的FeignClient、pojo类 都是 feign 模块的

在启动类上需要加上引入的 Feign类,这里订单模块引入要访问用户模块的 Feign类,引入过之后就可以在业务层通过注入 feign类,远程调用用户模块。


网关的作用:
1、新建一个module 作为网关模块。并增添如下依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
2、在 application.yml 中增添如下:
server:
port: 10010
spring:
application:
name: gateway # 在注册中心中的服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址,连接到注册中心
gateway:
routes:
- id: user-service # 路由标示,必须唯一
uri: lb://userservice # 路由的目标地址 lb是负载均衡。 根据服务名找到服务,并负载均衡其多个实例
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合就转发到路由目的地
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
3、启动后测试:
orderservice 启动了一个实例
userservice 启动了三个实例,用来查看负载均衡

然后在网页端测试:

多刷几次,在idea控制台查看,会发现 userservice 三个实例中 接收到的请求数基本相同。

访问流程图:

总结:


PredicateFactory的作用就是读取用户定义的断言条件,然后对请求做出判断。
Spring提供了11种基本的 Predicate 工厂:

例如设置:

此时再访问的话就会404,因为不满足访问时间在2031年之后

改成Before,即可访问。


具体做的处理由路由过滤器工厂GatewayFilterFactory决定。
Spring 提供了31种不同的路由过滤器工厂。
例子:

在controller方法中可以将此参数从请求头获取并打印:

然后在网页发请求,只要是userservice服务相关的,都会被在请求头中增加一个Truth参数!
可以设置为全局的过滤器 default-filters,即所有经过网关的请求都会走这个过滤!


比如需要定义很复杂的处理逻辑,这时候GatewayFilter可能就不满足需求,需要手动写代码实现逻辑。
自己写一个类,实现 GlobalFilter接口。
对于下面这个场景:

定义全局过滤器实现:

网页中测试:

总结:




在 application.yml 文件中配置即可。
