目录
一、服务治理介绍
常见的注册中心
二、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,并将我们的两个微服务注册上去。
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.添加相关依赖
对父模块添加代码
4.0.0 com.zking springcloud-shop 1.0-SNAPSHOT shop-common shop-user shop-product shop-order pom 1.8 UTF-8 UTF-8 2.3.2.RELEASE Hoxton.SR9 2.2.6.RELEASE org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba.version} pom import
基础模块添加注册中心:
springcloud-shop com.zking 1.0-SNAPSHOT 4.0.0 shop-common org.projectlombok lombok com.alibaba fastjson 1.2.56 mysql mysql-connector-java 5.1.44 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
5.配置文件
spring:application:name: shop-orderspring: 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);}@Beanpublic RestTemplate getRestTemplate(){return new RestTemplate();}
}
最后启动所有微服务 查看服务列表

如图操作:复制微服务
更新名字,快捷键(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){// 通过服务名拿到这个节点的实例数
Listinstances = 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@Beanpublic RestTemplate getRestTemplate(){return new RestTemplate();}
}


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@Beanpublic 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 {@Autowiredprivate 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;}
}
导入依赖
springcloud-shop com.zking 1.0-SNAPSHOT 4.0.0 shop-common org.springframework.cloud spring-cloud-starter-openfeign org.projectlombok lombok com.alibaba fastjson 1.2.56 mysql mysql-connector-java 5.1.44 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
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 {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate 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@Beanpublic RestTemplate getRestTemplate(){return new RestTemplate();}
}
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") //声明调用提供者的namepublic 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 {@Autowiredprivate 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;}
}