Nacos注册中心-RestTemplate
文章目录
前言
本文主要介绍了创建父子项目,多个模块向Nacos注册中心注册、服务间的远程调用、以及基于Feign实现负载策略
1 项目结构
注:
fast-common-grpc : GRPC注册模块,如未使用GRPC可忽略此模块
2 模块搭建
2.1 创建父工程
创建一个maven工程,然后在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>springcloud-alibaba</artifactId>
<groupId>com.itsqh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shop-common</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</project>
2.2 创建基础模块
- 创建 shop-common 模块,在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>springcloud-alibaba</artifactId>
<groupId>com.itsqh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shop-common</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</project>
- 创建实体类
//用户
@Entity(name = "shop_user")//实体类跟数据表的对应
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)//数据库自增
private Integer uid;//主键
private String username;//用户名
private String password;//密码
private String telephone;//手机号
}
//商品
@Entity(name = "shop_product")
@Data
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer pid;//主键
private String pname;//商品名称
private Double pprice;//商品价格
private Integer stock;//库存
}
//订单
@Entity(name = "shop_order")
@Data
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long oid;//订单id
//用户
private Integer uid;//用户id
private String username;//用户名
//商品
private Integer pid;//商品id
private String pname;//商品名称
private Double pprice;//商品单价
//数量
private Integer number;//购买数量
}
2.3 创建用户微服务
- 新建一个 shop-user 模块,导入依赖
<?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>springcloud-alibaba</artifactId>
<groupId>com.itsqh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shop-user</artifactId>
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common-->
<dependency>
<groupId>com.itsqh</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 编写主类
package com.itsqh;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
*@description: 用户服务
*@author: Shiqinghu
*@time: 2020/12/2 14:15
*/
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class);
}
}
- 创建配置文件
server:
port: 8071
spring:
application:
name: service-user
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
jpa:
properties:
hibernate:
hbm2ddl:
auto: update #Springjpa默认创建数据表
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
2.4 创建商品微服务
- 创建一个名为 shop_product 的模块,并添加springboot依赖
<?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>springcloud-alibaba</artifactId>
<groupId>com.itsqh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shop-product</artifactId>
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common-->
<dependency>
<groupId>com.itsqh</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 编写主类
package com.itsqh;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
*
*@description: 商品服务
*@author: Shiqinghu
*@time: 2020/12/2 14:24
*/
@SpringBootApplication
@EnableDiscoveryClient//开启nacos客户端
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class,args);
}
}
- 创建配置文件
server:
port: 8081
spring:
application:
name: service-product
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:///shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
jpa:
properties:
hibernate:
hbm2ddl:
auto: update #Springjpa默认创建数据表
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
- 创建ProductDao接口
package com.itsqh.dao;
import com.itsqh.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductDao extends JpaRepository<Product, Integer> {
}
- 创建ProductService接口和实现类
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductDao productDao;
/**
* @description: 根据pid查询商品信息
* @return: com.itsqh.service.ProductService
* @author: qinghuShi
* @time: 2020/12/2 14:50
*
*/
@Override
public Product findByPid(Integer pid) {
return productDao.findById(pid).get();
}
}
- 创建Controller
@Slf4j
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/product/{pid}")
public Product getProduct(@PathVariable("pid") Integer pid){
log.info("接下来要进行{}号商品信息的查询", pid);
Product product = productService.findByPid(pid);
log.info("商品信息查询成功,内容为{}", JSON.toJSONString(product));
return product;
}
}
- 启动工程,等到数据库表创建完毕之后,加入测试数据
INSERT INTO shop_product VALUE(NULL,'小米','1000','5000');
INSERT INTO shop_product VALUE(NULL,'华为','2000','5000');
INSERT INTO shop_product VALUE(NULL,'苹果','3000','5000');
INSERT INTO shop_product VALUE(NULL,'OPPO','4000','5000');
2.5 创建订单微服务
- 创建一个名为 shop-order 的模块,并添加springboot依赖
<?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>springcloud-alibaba</artifactId>
<groupId>com.itsqh</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shop-order</artifactId>
<dependencies>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shop-common-->
<dependency>
<groupId>com.itsqh</groupId>
<artifactId>shop-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
- 创建工程的主类
/**
*
*@description: 订单服务
*@author: Shiqinghu
*@time: 2020/12/2 14:33
*/
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
- 创建配置文件application.yml
server:
port: 8091
spring:
application:
name: service-order
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
username: root
password: root
jpa:
properties:
hibernate:
hbm2ddl:
auto: update #Springjpa默认创建数据表
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
- 创建OrderDao接口
public interface OrderDao extends JpaRepository<Order,Long> {
}
- 创建OrderService接口和实现类
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Override
public void createOrder(Order order) {
orderDao.save(order);
}
}
- 创建RestTemplate
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
-
创建Controller
@RestController @Slf4j public class OrderController { @Autowired private RestTemplate restTemplate; @Autowired private OrderService orderService; //准备买1件商品 @GetMapping("/order/prod/{pid}") public Order order(@PathVariable("pid") Integer pid) { log.info(">>客户下单,这时候要调用商品微服务查询商品信息"); //通过restTemplate调用商品微服务 Product product = restTemplate.getForObject("http://localhost:8081/product/" + pid, Product.class); log.info(">>商品信息,查询结果:" + JSON.toJSONString(product)); Order order = new Order(); order.setUid(1); order.setUsername("测试用户"); order.setPid(product.getPid()); order.setPname(product.getPname()); order.setPprice(product.getPprice()); order.setNumber(1); orderService.save(order); return order; } }
读者需要启动以上微服务测试通过,方可继续添加注册Nacos
3 Nacos Discovery–服务治理
3.1 搭建Nacos环境
第1步: 安装nacos
下载地址: https://github.com/alibaba/nacos/releases
下载zip格式的安装包,然后进行解压缩操作
第2步: 启动nacos
#切换目录
cd nacos/bin
#命令启动
startup.cmd -m standalone
第3步: 访问nacos 打开浏览器输入http://localhost:8848/nacos,即可访问服务, 默认密码是nacos/nacos
3.2 将商品微服务注册到nacos
接下来开始修改 shop-product 模块的代码, 将其注册到nacos服务上
- 在pom.xml中添加nacos的依赖
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
- 在主类上添加@EnableDiscoveryClient注解
@SpringBootApplication
@EnableDiscoveryClient//开启nacos客户端
public class OrderApplication {
- 在application.yml中添加nacos服务的地址
Spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
- 启动服务, 观察nacos的控制面板中是否有注册上来的商品微服务
3.3 将订单微服务注册到nacos
同3.2一样操作1、2、3步骤即可,重复步骤此处省略…
修改代码通过Nacos注册中心获取服务端IP地址,核心代码如下
- 引入Bean
@Autowired
private DiscoveryClient discoveryClient;
- 通过discoveryClient方法,传入参数服务名称获取Nacos服务端的注册表
//从nacos中获取服务地址
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
ServiceInstance serviceInstance = instances.get(0);
//服务端IP
String host = serviceInstance.getHost();
//服务端端口
int port = serviceInstance.getPort();
3. 修改Rest请求方式
//通过restTemplate调用商品微服务
restTemplate.getForObject("http://" + host + ":" + port + "/product/" + pid,Product.class);
- 完整代码
@RestController
@Slf4j
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OrderService orderService;
@Autowired
private DiscoveryClient discoveryClient;
//准备买1件商品
@GetMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
log.info(">>客户下单,这时候要调用商品微服务查询商品信息");
//从nacos中获取服务地址
ServiceInstance serviceInstance =
discoveryClient.getInstances("service-product").get(0);
String url = serviceInstance.getHost() + ":" + serviceInstance.getPort();
log.info(">>从nacos中获取到的微服务地址为:" + url);
//通过restTemplate调用商品微服务
Product product = restTemplate.getForObject(
"http://" + url + "/product/" + pid, Product.class);
log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.save(order);
return order;
}
}
3.4 基于Ribbon实现负载均衡
通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上 进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。
服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡
而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求
Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡
第1步:在RestTemplate 的生成方法上添加@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
第2步:修改服务调用的方法
//直接使用微服务名字, 从nacos中获取服务地址
String url = "service-product";
//通过restTemplate调用商品微服务
Product product = restTemplate.getForObject("http://" + url + "/product/" + pid,Product.class);
Ribbon支持的负载均衡策略
策略名 | 策略描述 | 实现说明 |
---|---|---|
BestAvailableRule | 选择一个最小的并发 请求的serverv | 逐个考察Server,如果Server被 tripped了,则忽略,在选择其中 ActiveRequestsCount最小的server |
AvailabilityFilteringRule | 过滤掉那些因为一直 连接失败的被标记为 circuit tripped的后 端server,并过滤掉 那些高并发的的后端 server(active connections 超过配 置的阈值) | 使用一个AvailabilityPredicate来包含 过滤server的逻辑,其实就就是检查 status里记录的各个server的运行状 态 |
WeightedResponseTimeRule | 根据相应时间分配一 个weight,相应时 间越长,weight越 小,被选中的可能性 越低。 | 一个后台线程定期的从status里面读 取评价响应时间,为每个server计算 一个weight。Weight的计算也比较简 单responsetime 减去每个server自己 平均的responsetime是server的权 重。当刚开始运行,没有形成statas 时,使用roubine策略选择server。 |
RetryRuleRetryRule | 对选定的负载均衡策 略机上重试机制。 | 在一个配置时间段内当选择server不 成功,则一直尝试使用subRule的方 式选择一个可用的server |
RoundRobinRule | 轮询方式轮询选择 server | 轮询index,选择index对应位置的 server |
RandomRule | 随机选择一个server | 在index上随机,选择index对应位置 的server |
ZoneAvoidanceRule | 复合判断server所在 区域的性能和server 的可用性选择server | 使用ZoneAvoidancePredicate和 AvailabilityPredicate来判断是否选择 某个server,前一个判断判定一个 zone的运行性能是否可用,剔除不可 用的zone(的所有server), AvailabilityPredicate用于过滤掉连接 数过多的Server。 |
我们可以通过修改配置来调整Ribbon的负载均衡策略,具体代码如下
service-product: # 调用的提供者的名称
ribbon:
#RoundRobinRule 轮询(默认) RandomRule 随机 BestAvailableRule 最小的并发请求
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
3.5 基于Feign实现服务调用和负载
Feign是Spring Cloud提供的一个声明式的伪Http客户端
它使得调用远程服务就像调用本地服务 一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了Feign, Feign默认集成了 Ribbon
所以在Nacos下使用Fegin默认就实现了负 载均衡的效果。
- 加入Feign依赖
<!--fegin-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 在主类上添加Feign注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//开启Fegin
public class OrderApplication {}
- 创建一个service, 并使用Fegin实现微服务调用
@FeignClient("service-product")//声明调用的提供者的name
public interface ProductService {
//指定调用提供者的哪个方法
//@FeignClient+@GetMapping 就是一个完整的请求路径 http://serviceproduct/product/{pid}
@GetMapping(value = "/product/{pid}")
Product findByPid(@PathVariable("pid") Integer pid);
}
- 修改controller代码,并启动验证
@RestController
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private ProductService productService;
//准备买1件商品
@GetMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
log.info(">>客户下单,这时候要调用商品微服务查询商品信息");
//通过fegin调用商品微服务
Product product = productService.findByPid(pid);
log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.save(order);
return order;
}
}
参考资料:
https://www.bilibili.com/video/BV1VJ411X7xX?from=search&seid=1805218605156498019
以上仅为博主学习笔记,仅供参考!如有错误,感谢指出!
获取源码,关注公共号 Java编程与思想,后台回复:nacosym