目录
一、服务治理介绍
常见的注册中心
二、Nacos注册中心介绍
三、运用Nacos搭建环境
四、DiscoveryClient实现负载均衡
五、Ribbon实现负载均衡
六、基于Feign实现服务调用
七、Feign传参
一、服务治理介绍
通过上一章的操作,我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址 (ip,端口)等硬编码到了代码中,这种做法存在许多问题:
一旦服务提供者地址变化,就需要手工修改代码
一旦是多个服务提供者,无法实现负载均衡功能
一旦服务变得越来越多,人工维护调用关系困难
为解决以上麻烦:就需要通过注册中心动态的实现服务治理。
常见的注册中心
Zookeeper zookeeper 是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式 应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用 配置项的管理等。
Eureka Eureka 是Springcloud Netflix中的重要组件,主要作用就是做服务注册和发现。但是现在已经闭 源
Consul Consul 是基于GO语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现 和配置管理的功能。Consul的功能都很实用,其中包括:服务注册/发现、健康检查、Key/Value 存储、多数据中心和分布式一致性保证等特性。Consul本身只是一个二进制的可执行文件,所以 安装和部署都非常简单,只需要从官网下载后,在执行对应的启动脚本即可。
Nacos Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是 Spring Cloud Alibaba 组件之一,负责服务注册发现和服务配置,可以这样认为nacos=eureka+config。
二、Nacos注册中心介绍
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速 实现动态服务发现、服务配置、服务元数据及流量管理。 从上面的介绍就可以看出,nacos的作用就是一个注册中心,用来管理注册上来的各个微服务。
三、运用Nacos搭建环境
接下来,我们就在现有的环境中加入nacos,并将我们的两个微服务注册上去。
1.将下载的安装包(Nacos)在非中文目录下进行解压
解压后找到bin目录下的startup.cmd 点击编辑,
--->把 set MODE="cluster" 改为 set MODE="standalone"
----->保存编辑,在bin目录下加入cmd命令
2.启动Nacos
重启命令:输入startup.cmd -m standalone
3.登录Nacos账号
谷歌浏览器地址栏输入:http://localhost:8848/nacos/#/login
注意这里用户名小写:nocas 密码与用户名一致
4.添加相关依赖
对父模块添加代码
<?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.zking</groupId> <artifactId>springcloud-shop</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>shop-common</module> <module>shop-user</module> <module>shop-product</module> <module>shop-order</module> </modules> <packaging>pom</packaging> <!--依赖版本的锁定--> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.3.2.RELEASE</spring-boot.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> <spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!-- SpringBoot 依赖配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <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>
基础模块添加注册中心:
<?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-shop</artifactId> <groupId>com.zking</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.44</version> </dependency> <!--nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>
5.配置文件
spring: application: name: shop-order spring: cloud: nacos: discovery: server-addr: localhost:8848 server: port: 8090
6.运行时添加注解@EnableDiscoveryClient
package com.zking.shoporder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication public class ShopOrderApplication { public static void main(String[] args) { SpringApplication.run(ShopOrderApplication.class, args); } @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
最后启动所有微服务 查看服务列表
四、DiscoveryClient实现负载均衡
如图操作:复制微服务
更新名字,快捷键(Alt+insert)添加port端口号8081-->最后使用
启动该新添微服务
效果:
package com.zking.shoporder.controller;
import com.zking.model.Order;
import com.zking.model.Product;
import com.zking.model.User;
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.PathVariable;
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;/**
* @author小李飞刀
* @site www.javaxl.com
* @company xxx公司
* @create 2022-11-20 23:30
*/
@RestController
@RequestMapping("/order")
public class OrderController_DiscoveryClient {
@Autowired
private RestTemplate restTemplate;@Autowired
private DiscoveryClient discoveryClient;@RequestMapping("/get/{uid}/{pid}")
public Order get(@PathVariable("uid") Integer uid,
@PathVariable("pid") Integer pid){// 通过服务名拿到这个节点的实例数
List<ServiceInstance> instances = discoveryClient.getInstances("shop-product");
int index = new Random().nextInt(instances.size());
ServiceInstance serviceInstance = instances.get(index);
String url = serviceInstance.getHost() + ":" +
serviceInstance.getPort();User user = restTemplate.getForObject("http://localhost:8070/user/get/" + uid, User.class);
Product product = restTemplate.getForObject("http://"+url+"/product/get/" + pid, Product.class);
Order order = new Order();
order.setNumber(2);
order.setOid(System.currentTimeMillis());
order.setPid(pid);
order.setPname(product.getPname());
order.setPprice(product.getPprice() * order.getNumber());
order.setUid(user.getUid());
order.setUsername(user.getUsername());
return order;
}
}
package com.zking.shopproduct.controller; import com.zking.model.Product; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; /** * @author小李飞刀 * @site www.javaxl.com * @company xxx公司 * @create 2022-11-20 23:24 */ @RestController @RequestMapping("/product") public class ProductController { @RequestMapping("/get/{pid}") public Product get(@PathVariable("pid") Integer pid, HttpServletRequest request){ System.out.println("======================="+request.getServerPort()); Product p = new Product(pid, "元气奶茶", 36d, 36); return p; } }
package com.zking.shoporder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; //@EnableDiscoveryClient //@SpringBootApplication public class ShopOrderApplication_DiscoveryClient { public static void main(String[] args) { SpringApplication.run(ShopOrderApplication_DiscoveryClient.class, args); } // @LoadBalanced DiscoveryClient这种方式,切记不要添加这个注解,否则会报错No instances available for localhost @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
五、Ribbon实现负载均衡
package com.zking.shoporder; import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; //@EnableDiscoveryClient //@SpringBootApplication public class ShopOrderApplication_Ribbon { public static void main(String[] args) { SpringApplication.run(ShopOrderApplication_Ribbon.class, args); } @LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
package com.zking.shoporder.controller; import com.zking.model.Order; import com.zking.model.Product; import com.zking.model.User; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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; /** * @author lucy * @site www.javaxl.com * @company xxx公司 * @create 2022-11-20 23:30 */ @Slf4j //@RestController //@RequestMapping("/order") public class OrderController_Ribbon { @Autowired private RestTemplate restTemplate; @RequestMapping("/get/{uid}/{pid}") public Order get(@PathVariable("uid") Integer uid, @PathVariable("pid") Integer pid){ log.info(">>客户下单,这时候要调用商品微服务查询商品信息"); User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class); Product product = restTemplate.getForObject("http://shop-product/product/get/" + pid, Product.class); Order order = new Order(); order.setNumber(2); order.setOid(System.currentTimeMillis()); order.setPid(pid); order.setPname(product.getPname()); order.setPprice(product.getPprice() * order.getNumber()); order.setUid(user.getUid()); order.setUsername(user.getUsername()); return order; } }
六、基于Feign实现服务调用
导入依赖
<?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-shop</artifactId> <groupId>com.zking</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>--> <!--fegin组件--> <!--Feign微服务调用--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</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.44</version> </dependency> <!--nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> </project>
OrderController_Feign
package com.zking.shoporder.controller; import com.zking.model.Order; import com.zking.model.Product; import com.zking.model.User; import com.zking.shoporder.Service.ProductService; import lombok.extern.slf4j.Slf4j; 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.PathVariable; 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; /** * @author lucy * @site www.javaxl.com * @company xxx公司 * @create 2022-11-20 23:30 */ @Slf4j @RestController @RequestMapping("/order") public class OrderController_Feign { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @Autowired private ProductService productService; @RequestMapping("/get/{uid}/{pid}") public Order get(@PathVariable("uid") Integer uid, @PathVariable("pid") Integer pid) { // 用了注册中心以及@LoadBalanced(discoveryClient模式不需要@LoadBalanced),就不能用localhost:8070的方式远程调用,只能通过应用名shop-user的方式访问 // java.lang.IllegalStateException: No instances available for localhost // User user = restTemplate.getForObject("http://localhost:8070/user/get/" + uid, User.class); // User user = restTemplate.getForObject("http://192.168.1.5:8070/user/get/" + uid, User.class); User user = restTemplate.getForObject("http://shop-user/user/get/" + uid, User.class); Product product = productService.get(pid); Order order = new Order(); order.setNumber(2); order.setOid(System.currentTimeMillis()); order.setPid(pid); order.setPname(product.getPname()); order.setPprice(product.getPprice() * order.getNumber()); order.setUid(user.getUid()); order.setUsername(user.getUsername()); return order; } }
ShopOrderApplication_Fegin
package com.zking.shoporder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableDiscoveryClient @SpringBootApplication @EnableFeignClients //开启Fegin public class ShopOrderApplication_Fegin { public static void main(String[] args) { SpringApplication.run(ShopOrderApplication_Fegin.class, args); } 负载均衡添加 // @LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
七、Feign传参
ProductService
package com.zking.shoporder.Service; import com.zking.model.Product; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletRequest; /** * @author 锦鲤 * @site www.lucy.com * @company xxx公司 * @create 2022-11-27 19:55 * 帮助消费者 shop-order调用生产者shop-product */ @FeignClient("shop-product") //声明调用提供者的name public interface ProductService { @RequestMapping("/product/get/{pid}") public Product get(@PathVariable("pid") Integer pid); @RequestMapping("/findByParameter") public String findByParameter( @RequestParam("name")String name, @RequestParam("price")Double price); @RequestMapping("/findByParameter2") public String findByParameter2( @RequestParam("name") String name, @RequestParam("price") Double price); @RequestMapping("/findByPathVariable/{name}") public String findByPathVariable(@PathVariable("name") String name); @RequestMapping("/findByRequestBody") public Product findByRequestBody(Product product); }
FeignClientController
package com.zking.shoporder.controller; import com.zking.model.Product; import com.zking.shoporder.Service.ProductService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @Slf4j @RestController @RequestMapping("/feignClient") public class FeignClientController { @Autowired private ProductService productService; @RequestMapping("/findByParameter") public String findByParameter( @RequestParam("name")String name, @RequestParam("price") Double price){ log.info("服务消费者日志:{}",name); return productService.findByParameter(name,price); } @RequestMapping("/findByParameter2") public String findByParameter2( @RequestParam("name") String name, @RequestParam("price") Double price){ log.info("服务消费者日志:{},{}",name,price); return productService.findByParameter2(name, price); } @RequestMapping("/findByPathVariable/{name}") public String findByPathVariable(@PathVariable("name") String name){ log.info("服务消费者日志:{}",name); return productService.findByPathVariable(name); } @RequestMapping("/findByRequestBody") public Product findByRequestBody(Product product){ log.info("服务消费者日志:{}",product.getPname()); return productService.findByRequestBody(product); } }
FeignServerController
package com.zking.shopproduct.controller; import com.zking.model.Product; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @Slf4j @RestController @RequestMapping("/feignserver") public class FeignServerController { @RequestMapping("/findByParameter") public String findByParameter(String name,Double price){ log.info("服务提供者日志:{}",name); return "hello:"+name; } @RequestMapping("/findByParameter2") public String findByParameter2( @RequestParam("name") String name, @RequestParam("price") Double price){ log.info("服务提供者日志:{},{}",name,price); return "hello:"+name+price; } @RequestMapping("/findByPathVariable/{name}") public String findByPathVariable(@PathVariable("name") String name){ log.info("服务提供者日志:{}",name); return "hello:"+name; } @RequestMapping("/findByRequestBody") public Product findByRequestBody(@RequestBody Product product){ log.info("服务提供者日志:{}",product.getPname()); return product; } }