学习之前,请大家按照视频教程搭建好自己的微服务学习项目,视频链接如下:https://www.bilibili.com/video/BV1UJc2ezEFU/?spm_id_from=333.788.videopod.sections&vd_source=a96efff6f52f30c2fbceb5a2bc1d04c7&p=4
接着,请大家先去官网下载Nacos,按照上面视频构建的学习项目,我们的Nacos可以选2.5.4版本
最后我们在powershell里面cd到我们安装Nacos的目录的bin目录,在该目录下输入:
.\startup.cmd -m standalone
让Nacos以单机模式启动
启动完成,接着访问localhost:8848/nacos,好了我们就开始来今天的学习吧
服务注册

依赖导入
导入服务发现依赖
在构建好的项目中的Service目录,找到它的xml配置文件,导入"服务发现"依赖,用于让网关或者微服务在调用前好去在注册中心发现课调用的其他微服务

导入SpringWeb依赖
在每个微服务的xml配置问价导入简单的SpringWeb依赖,因为每个微服务本质上都是一个简单的web应用

微服务配置文件编写
依赖导入完成,我们需要编写配置文件才能把微服务注册到注册中心
打开每个微服务的properties配置文件(你也可以是yml文件,更加优雅),编写如下配置:
前面两行内容很好理解,服务名称和服务端口号
最后一行一定要写,指的是Nacos的地址,不写的话服务都不知道要注册到哪里 
启动微服务完成注册
最后直接启动我们的每一个微服务
可以在控制台日志看到服务的注册

回到我们Nacos控制台
来到服务管理的服务列表当中,可以看到注册好的微服务

注册中心清楚的记录着他们的服务IP以及端口号
服务发现
先声明:后续的服务发现过程我们会直接自动化,接下来的实例代码并不是特别重要,稍微了解即可
微服务的启动类上可以加上 @EnableDiscoveryClient注解,用于开启服务发现功能
但是说实话,这个注解到现在已经没啥用了,可加可不加

测试方法:
两个都是获取服务名称和服务端口号,先引入discoveryClient然后使用了discoveryClient的方法
(后续服务发现都是直接自动化,这种底层代码了解即可)
测试结果如下:
成功发现微服务端口号和服务名称

远程调用
完成了服务注册和服务发现,接下来我们就可以完成"远程调用了"
在此之前请先回顾一下远程调用的基本流程**(非常重要!)**:
下图是我们这次实践远程调用的场景:

实现该场景我们就简单的做一下两个微服务的初始化准备吧
初始场景构建
我们的目标是通过订单"Order"微服务去远程调用商品"Product"微服务,那我们就对这些微服务简单的做一些初始构建(Product同理)

bean里面的实体类,简单的controller和service层
由于图方便,这次实践不带上数据库了,关于"请求商品/订单"我们直接new对象返回就好
示例:根据产品ID和用户ID来获取相关订单
微服务Order示例
微服务Product示例:


其他实际的具体内容我们就不展示了,这里就简单说一下其实就是根据订单相关信息可以查询订单,根据商品相关信息可以查询商品
实际远程调用
我们接下来开始实际远程调用:
回看我们"创建订单"的服务,发现"订单总金额"和"商品列表"这两个"订单属性"需要用到"商品属性"------商品单价price、商品数量amount、商品名称productName
为了获取这些数据,我们就必须远程调用Product微服务 ,让他从它自己对应的数据库 (本次例子没有用到数据库,上文构造了简单对象来模拟数据库)返回相关数据给我们的Order微服务

因此我们继续完成业务的编写,我们来尝试获取这些商品信息的数据

但是当我们像之前一样直接写获取Product的方法时,发现报错了,无论如何都找不到有关Product的任何类
这正式因为我们现在用的时微服务的分布式架构,每一个微服务都是一个独立的Web程序,Product微服务是独立于我们Order微服务的,因此这才互相访问不到,Order无法访问Product的实体类访问自然报错了
对于这种"微服务互相调用各自实体类"的情况,在分布式架构中我们会新建一个"model"模块
model模块--微服务"公共模型层"
微服务中,难免有需要各个微服务都用到的"公用数据",分布式架构常用的方案就是新建一个模块,把公用的实体都装在该模块中,然后各个微服务可以直接来该模块拿实体
我们新增model模块,然后把之前分布在各个微服务中的实体层bean移到该层

同时,在微服务的公用Service层的xml中添加对model模块的引入
这样各个微服务想要引入数据就都可以从model层面引入了
(本质上 就是创了一个模块,把需要公用的实体放里面,大家都可以拿公用实体)
再回头看之前报错的方法,发现已经不报错了,可以从共用模型层中获取实体
然后我们编写代码使用discoveryClient来获取相应微服务的IP和port:
远程调用的url拼好了,接下来就得开始发送远程调用请求了
请求发送
请求发送我们常用RestTemplate中的方法来快捷发送各种请求
可以先定义一个注册类,这样待会使用的时候不用new了,方便一点

getForObject就是代表要通过发送GET请求自动把获得返回的JSON数据变为Object对象,第二个参数就是要转为的对象
至此,远程调用完毕:
java
private Product getProductFromRemote(Long productId) {
//获取Product微服务的所有IP和port端口号
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
//获取列表中第一台机器的IP和port端口号
ServiceInstance instance = instances.get(0);
//拼写远程url,方便后续远程调用请求的发送
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/product/getProduct?id=" + productId;
//远程调用--发送请求
log.info("发送请求:{}", url);
Product product = restTemplate.getForObject(url, Product.class);
return product;
}
最后补齐我们之前的返回订单的方法即可:
java
@Override
public Order createOrder(Long productId, Long userId) {
//获取商品信息
Product product = getProductFromRemote(productId);
log.info("商品信息:{}", product);
//这里图方便,不使用数据库了,直接通过模拟数据库的方式来实现
Order order = new Order();
order.setId(1L);
//订单总金额
order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
order.setUserId(userId);
order.setNickName("路人");
order.setAddress("91市场");
// 商品列表(这里假设列表中只有一个商品)
order.setProductList(Arrays.asList(product));
return order;
}
最后我们直接访问方法http://localhost:8000/order/create?userId=1&productId=2
发现成功从Product中获取到了数据

并且在该结构下使用集群工作,我们停止提供Product的9001端口,9002立马顶上来

我们把9001端口重新启动后,发现全部请求打到了9001端口

这是因为我们刚刚在"请求发送"写的代码,获取url端口号时总是获取所有端口中的第0个,因此总是把第一个端口拼接了然后发送请求
很显然,实际开发中这么干服务器肯定顶不住,因此回想我们之前提及的"负载均衡"解决方案,这样可以让服务器的承载获得相对保障
优化--负载均衡
之前总是压榨同一台服务器的罪魁祸首就是:
discoveryClient的getInstanse总是获取所有微服务的端口号,然后我们后面又总是要第0个,这才导致了悲剧的发生

在该情况下,我们可以手写一套负载均衡的算法,让instance获取到的不总是第0个,但是这是在太麻烦了,SpringCloud为我们提供了**非常简单(真的简单,懒人福音)**的方案:
注解@LoadBlanced
是的,我们只需要在配置类中的RestTemplete的头上加上**@LoadBlanced**注解,那么后面发送请求的restTemplate就会自动向SpringCloud找到负载均衡的服务器发送请求
不过在此之前需要引入依赖,再去写注解,不然不生效

因此,我们之前的discoveryClient几乎用不上了
只需要打好注解,**然后url改成http://微服务名称/其他请求参数**的形式即可

整体方法都很简洁
java
/**
* 优化:获取商品信息(使用轮询负载均衡)
*/
private Product getProductFromRemoteWithBalence(Long productId) {
//在LoadBalanced的帮助下,我们只需要写服务名,不需要写IP和端口号,SpringCloud会自动帮我们找到对应的IP和端口号
String url = "http://service-product/product/" + productId;
//远程调用--发送请求
log.info("发送请求:{}", url);
Product product = restTemplate.getForObject(url, Product.class);
return product;
}
我们来测试一下:
稍微做了一点小小的改动,只要Product微服务被调用,他就会输出hello




可以看到我们的负载均衡生效了

