一、什么是服务治理
服务治理是微服务架构中最核心最基本的模块,用于实现各个微服务的自动化注册与发现。
- 服务注册:在服务治理框架中,都会构建一个注册中心,每个服务单元向注册中心登记自己提供的服务的详细信息。并在注册中心形成一张服务清单,服务注册中心需要以心跳的方式去监测清单中的服务是否可用,若不可用,需要再服务清单中剔除不可用的服务。
- 服务发现:服务调用方向服务注册中心咨询服务,保宁获取所有服务的实例清单,实现对具体服务实例的访问。
通过上面的图会发现,除了微服务,还有一个组件是服务注册中心,它是微服务架构中非常重要的一个组件,在微服务架构里起到了一个协调者的作用。
注册中心一般包含以下几个功能:
服务发现
- 服务注册:保存服务提供者和服务调用者信息
- 服务订阅:服务调用者订阅服务提供者的信息,注册中心向订阅者推送提供者信息
服务配置
- 配置订阅:服务提供者和服务调用者订阅微服务相关配置
- 配置下发:主动将配置推送给服务提供者和服务调用者
服务健康检测
检测服务提供者的健康状况,如果发现异常,执行服务剔除
常见的服务注册中心包括:Zookeeper、Eureka、Consul、Nacos。
Nacos是Spring Cloud Alibaba组件之一,负责服务注册发现和服务配置,因为我们使用Spring Cloud Alibaba,所以这里只介绍Nacos的使用。
二、Nacos简介
Nacos致力于帮助您发现、配置和管理微服务。Nacos提供了一组简单易用的特性及,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
三、搭建Nacos环境
注意
使用Nacos之前,需要先配置好Java环境变量。
我这里使用的服务器环境是Ubuntu 20.04,以下Nacos安装使用均以此为准,目前Nacos最新版本是2.1.2
Nacos下载地址:Releases · alibaba/nacos (github.com)
这里只介绍Nacos的基本使用,具体集群等高级用法,可以自行查找相关资料。
3.1、下载Nacos
下载nacos-server-2.1.2.tar.gz
后,加压到任意位置。
3.2、Nacos数据库文件
运行Nacos之前,需要将Nacos数据库配置文件导入,我这里使用的是MySql,我直接导入上面Demo里面的数据库了。
MySql的语句如下
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/******************************************/
/* 数据库全名 = nacos_config */
/* 表名称 = config_info */
/******************************************/
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,
`encrypted_data_key` text NOT NULL COMMENT '秘钥',
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 '租户字段',
`encrypted_data_key` text NOT NULL 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(20) 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 '租户字段',
`encrypted_data_key` text NOT NULL 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');
3.3、配置Nacos
在conf
文件夹下有一个application.properties
,我们需要配置里面的数据库连接信息
把大概34
行往下的位置,取消注释并根据自己情况进行配置
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/shop?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
3.4、运行Nacos
进入bin
文件夹
执行
./startup.sh -m standalone
Nacos启动后,浏览器输入localhost:8848/nacos
默认用户名及密码都是nacos
四、将商品微服务注册到Nacos
我们改造商品微服务,以便支持Nacos
4.1、修改配置文件
增加Nacos依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.0.4.0</version>
</dependency>
4.2、主类上注解@EnableDiscoveryClient
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
@EnableDiscoveryClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
4.3、配置文件添加nacos服务的地址
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.236.2:8848
修改后配置文件如下
server:
port: 8081
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.236.2:8848
application:
name: service-user
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.236.2/shop?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
username: root
password: root
jpa:
hibernate:
#指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
ddl-auto: update
naming:
#指定jpa的自动表生成策略,驼峰自动映射为下划线格式
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
# 默认false,在日志里显示执行的sql语句
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
4.4、查看服务是否注册成功
重新启动product微服务
服务名就是我们配置文件配置的应用名称。
五、将订单微服务注册到Nacos
我们改造订单微服务,以便支持Nacos
5.1、修改配置文件
增加Nacos依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.0.4.0</version>
</dependency>
5.2、主类上注解@EnableDiscoveryClient
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
5.3、配置文件添加nacos服务的地址
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.236.2:8848
修改后配置文件如下
server:
port: 8091
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.236.2:8848
application:
name: service-user
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.236.2/shop?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
username: root
password: root
jpa:
hibernate:
#指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建,如果指定create,则每次启动项目都会清空数据并删除表,再新建
ddl-auto: update
naming:
#指定jpa的自动表生成策略,驼峰自动映射为下划线格式
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
# 默认false,在日志里显示执行的sql语句
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
database: mysql
database-platform: org.hibernate.dialect.MySQL5Dialect
5.4、查看服务是否注册成功
重新启动product微服务
服务名就是我们配置文件配置的应用名称。
5.5、改造订单接口,实现微服务调用
package net.xiangcaowuyu.shop.order.controller;
import lombok.extern.slf4j.Slf4j;
import net.xiangcaowuyu.shop.common.entity.Product;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("order")
@Slf4j
public class OrderController {
@Resource
private RestTemplate restTemplate;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("product/{id}")
public Product order(@PathVariable("id") Integer productID) {
List<ServiceInstance> serviceInstanceLList = discoveryClient.getInstances("service-product");
log.info("获取到服务:" + serviceInstanceLList.size());
//忽略下面可能导致的错误
ServiceInstance serviceInstance = serviceInstanceLList.get(0);
return restTemplate.getForObject(serviceInstance.getUri() + "/product/1", Product.class);
}
}
浏览器访问订单接口
六、负载均衡
通俗的讲,负载均衡就是将负载(工作任务、访问请求)进行分摊到多个操作单元(服务器、组件)上进行执行。
根据负载均衡发生的位置不同,一般分为服务端负载均衡
和客户端负载均衡
。
服务端负载均衡指的是发生在服务提供者一方,比如nginx
负载均衡
客户端负载均衡指的是发生在服务请求一方,也就是在发送请求之前已经选好了由那个实例处理请求。
微服务调用关系中一般会选择客户端负载均衡。
6.1、准备负载均衡环境
为了实现负载均衡,我们需要准备至少两个服务,这里以商品服务为例。
在上面的代码中,我们pom.xml
文件都没有加入打包的插件,为了启动两个服务,我们需要现将程序打包成jar
包。在shop-user
、shop-product
、shop-order
三个模块都加入打包插件。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
6.2、准备商品微服务
为了区分调用的服务,我们在商品服务输出一下端口号。
package net.xiangcaowuyu.shop.product.controller;
import lombok.extern.slf4j.Slf4j;
import net.xiangcaowuyu.shop.common.entity.Product;
import net.xiangcaowuyu.shop.product.service.ProductService;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
public class ProductController {
@Resource
private ProductService productService;
@Resource
private Environment environment;
@GetMapping("/product/{id}")
public Product product(@PathVariable("id") Integer ID) {
log.error("当前端口号:" + environment.getProperty("local.server.port"));
return productService.findByID(ID);
}
}
然后将shop-product
打包成jar
包,通过一下命令,启动两个服务
java -jar shop-product-1.0-SNAPSHOT.jar --server.port=8081
java -jar shop-product-1.0-SNAPSHOT.jar --server.port=8082
此时打开Nacos,可以看到shop-product
有两个服务
此时,我们运行订单工程,访问接口
http://127.0.0.1:8091/order/product/1
多次刷新之后,可以用看到服务全部都打到了8081
端口上。
8082
一条都没有,可见并没有实现负载均衡。
6.3、基于Ribbon实现负载均衡
Ribbon是Spring Cloud的一个组件,它可以让我们使用一个注解就能轻松搞定负载均衡。
nacos 2021
版本已经没有自带Ribbon的整合,所以需要引入另一个支持的jar包 loadbalancer
早shop-order
中引入loadbalancer
实现Ribbon支持
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
在RestTemplate
上注解上@LoadBalanced
即可。
package net.xiangcaowuyu.shop.order.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class OrderConfiguration {
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
改造接口,通过服务名访问
package net.xiangcaowuyu.shop.order.controller;
import lombok.extern.slf4j.Slf4j;
import net.xiangcaowuyu.shop.common.entity.Product;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("order")
@Slf4j
public class OrderController {
@Resource
RestTemplate restTemplate;
@Resource
DiscoveryClient discoveryClient;
@GetMapping("product/{id}")
public Product order(@PathVariable("id") Integer productID) {
String serviceName = "service-product";
return restTemplate.getForObject("http://"+serviceName + "/product/1", Product.class);
}
}
此时,我们运行订单工程,访问接口
http://127.0.0.1:8091/order/product/1
多次刷新之后,可以用看到服务平均打到了8081
和8082
端口。
Ribbon默认的均衡策略是轮训。
Ribbon自带的负载均衡策略
我们可以通过修改配置文件改变默认的负载均衡策略。
Ribbon
已经在最新的Spring Cloud
版本中被废弃,Spring Cloud Loadbalancer
是官方正式推出的一款新负载均衡利器,在未来,LoadBalancer
很有可能取代Ribbon的地位成为新一代的负载均衡器
6.4、基于Feign实现负载均衡
Feign是Spring Cloud提供的一个声明式的伪HTTP客户端,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加注解即可。
Nacos很好的兼容了Feign,Feign默认集成了Ribbon,所以在Nacos下使用Feign默认就实现了负载均衡的效果。
在进行一下代码之前,记得先移除6.3添加的Ribbon相关的代码
我们改造shop-order
工程,实现Feign的使用
6.4.1、引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
6.4.2、在主类上添加Feign注解
package net.xiangcaowuyu.shop.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
6.4.3、添加一个service
package net.xiangcaowuyu.shop.order.Service;
import net.xiangcaowuyu.shop.common.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("service-product")
public interface ProductService {
@GetMapping("/product/{id}")
Product findByID(@PathVariable("id") Integer id);
}
Feign调用服务的地址就是@FeignClient
+@GetMapping
(获取其他映射)的地址
6.4.4、改造Controller
package net.xiangcaowuyu.shop.order.controller;
import lombok.extern.slf4j.Slf4j;
import net.xiangcaowuyu.shop.common.entity.Product;
import net.xiangcaowuyu.shop.order.Service.ProductService;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.openfeign.FeignClient;
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;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("order")
@Slf4j
public class OrderController {
@Resource
ProductService productService;
@GetMapping("product/{id}")
public Product order(@PathVariable("id") Integer productID) {
return productService.findByID(productID);
}
}
此时,我们运行订单工程,访问接口
http://127.0.0.1:8091/order/product/1
多次刷新之后,可以用看到服务平均打到了8081
和8082
端口。
6.4.5、修改轮训策略
以前的Ribbon
有多种负载均衡策略但LoadBalancer
貌似只提供了两种负载均衡器,不指定的时候默认用的是轮询。
- RandomLoadBalancer 随机
- RoundRobinLoadBalancer 轮询
添加LoadBalance
配置类
package net.xiangcaowuyu.shop.order.config;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import javax.annotation.Resource;
public class MyLoadBalancerConfig {
@Resource
private NacosDiscoveryProperties nacosDiscoveryProperties;
//自定义loadBlancer负载均衡策略
@Bean
public ReactorServiceInstanceLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
//返回随机轮询负载均衡方式
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
//返回加权随机轮询负载均衡方式
//return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
//nacos服务注册中心权重的负载均衡策略
// return new NacosLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name, nacosDiscoveryProperties);
}
}
启动类上配置服务使用的负载均衡策略
package net.xiangcaowuyu.shop.order;
import net.xiangcaowuyu.shop.order.config.MyLoadBalancerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EntityScan({"net.xiangcaowuyu.shop.common.entity"})
@EnableDiscoveryClient
@EnableFeignClients
@LoadBalancerClients(value =
@LoadBalancerClient(name = "service-product",configuration = MyLoadBalancerConfig.class))
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
此时,我们运行订单工程,访问接口
http://127.0.0.1:8091/order/product/1
多次刷新之后,可以用看到服务随机打到了8081
和8082
端口。
评论 (0)