搭建nacos环境
安装nacos(解压缩、启动)
- 下载地址: https://github.com/alibaba/nacos/releases 下载zip格式的安装包,然后进行解压缩操作
- 启动nacos
启动方式
- #切换目录 cd nacos/bin
- #命令启动 startup.cmd -m standalone 或者直接双击startup.cmd运行
登录nacos
- http://localhost:8848/nacos
- 账号:nacos 密码:nacos
将商品服务注册nacos (服务注册)
在pom.xml中添加nacos的依赖
1 2 3 4 5
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
|
在主类上添加@EnableDiscoveryClient注解(启动类)
在application.yml中添加nacos服务的地址
1 2 3 4 5
| spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848
|
使用nacos
使用nacos需借用工具类 DiscoveryClient须在启动类上加载 @EnableDiscoveryClient
使用discoveryClient.getInstances(“service-product”)获取当前nacos中,名叫service-product的元数据(所有数据)
方法中使用
nacos的一些方法
- getInstances(“nacos中的服务名”) 获取nacos中该服务的元数据
- getHost() 获取主机号(IP)
- getPort() 获取端口号
1 2 3 4 5 6 7 8 9 10 11 12
| ServiceInstance serviceInstance = discoveryClient.getInstances("service-product").get(0); String url = serviceInstance.getHost()+":"+serviceInstance.getPort(); ServiceInstance serviceInstance2 = discoveryClient.getInstances("service-user").get(0); String url2 = serviceInstance2.getHost()+":"+serviceInstance2.getPort(); *log*.info(">>从nacos中获取到的微服务地址为:"+url); *log*.info(">>从nacos中获取到的微服务地址为url2:"+url2);
Product product = restTemplate.getForObject("http://" + url + "/product/" + id, Product.class); User user = restTemplate.getForObject("http://" + url2 + "/user/" + 2, User.class); *log*.info("查询到{}号商品的信息,内容是:{}",id, JSON.*toJSONString*(product)); *log*.info("查询到{}号用户的信息,内容是:{}",2, JSON.*toJSONString*(user));
|
负载均衡
通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。
根据负载均衡发生位置的不同,一般分为服务端负载均衡 和客户端负载均衡 。
服务端负载均衡指的是发生在服务提供者一方,比如常见的nginx负载均衡而客户端负载均衡指的是发生在服务请求的一方,也就是在发送请求之前已经选好了由哪个实例处理请求。
Namespace命名空间
命名空间用于进行隔离,Namespace 的常用场景之一是不同环境的隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
命名空间管理
前面已经介绍过,命名空间(Namespace)是用于隔离多个环境的(如开发、测试、生产),而每个应用在不同环 境的同一个配置(如数据库数据源)的值是不一样的。因此,我们应针对企业项目实际研发流程、环境进行规划。 如某软件公司拥有开发、测试、生产三套环境,那么我们应该针对这三个环境分别建立三个namespace注册实例
1 2 3 4 5 6 7 8 9 10 11
| spring: application: name: server-product cloud: nacos: discovery: server-addr: 127.0.0.1:8848 namespace: 命名空间ID cluster-name: DEFAULT
|
硬核实现 负载均衡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @RequestMapping("/order/prod/{pid}") public Order order(@PathVariable("pid") Integer pid) { log.info(">>客户下单,这时候要调用商品微服务查询商品信息"); List<ServiceInstance> instances = discoveryClient.getInstances("service-product"); int index = new Random().nextInt(instances.size()); ServiceInstance serviceInstance = instances.get(index); String url = serviceInstance.getHost() + ":" + serviceInstance.getPort(); log.info(">>从nacos中获取到的微服务地址为:" + url); Product product = restTemplate.getForObject( "http://" + url + "/product/" + pid, Product.class); log.info("查询到{}号商品的信息,内容是:{}", pid, JSON.toJSONString(product));
Order order = new Order(); order.setUid(1); order.setUsername("测试用户"); order.setPid(pid); order.setPname(product.getPname()); order.setPprice(product.getPprice()); order.setNumber(1);
orderService.createOrder(order);
log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
return order; }
|
Ribbon 实现负载均衡
概念
Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡
就是当商品服务的负载过大时,将商品服务代码复制多份,同时部署在好几台服务器上,其spring.application.name: 都为同一名称,如有服务器性能不好,则会通过 Ribbon 来实现负载均衡策略
步骤
1.在RestTemplate 的生成方法上添加 @LoadBalanced 注解
2.修改服务调用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @Configuration public class RestTemplateConfig { @LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } } @RequestMapping("/order/prod/{pid}") public Order order(@PathVariable("pid") Integer pid) { String url = "service-product"; Product product = restTemplate.getForObject( "http://" + url + "/product/" + pid, Product.class);
Order order = new Order(); order.setUid(1); order.setUsername("测试用户"); order.setPid(pid); order.setPname(product.getPname()); order.setPprice(product.getPprice()); order.setNumber(1);
orderService.createOrder(order); return order; }
|
Ribbon支持的负载均衡策略
策略名 |
策略描述 |
实现说明 |
BestAvailableRule |
选择一个最小的并发 请求的server |
逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server |
AvailabilityFilteringRule |
过滤掉那些因为一直连接失败的被标记为 circuit tripped的后端server,并过滤掉那些 高并发的的后端server(activeconnections 超过配置的阈值) |
使用一个AvailabilityPredicate来包含 过滤server的逻辑,其实就就是检查 status里记录的各个server的运行状态 |
WeightedResponseTimeRule |
根据相应时间分配一 个weight,相应时间越长,weight越小,被选中的可能性越低 |
一个后台线程定期的从status里面读 取评价响应时间,为每个server计算一个 weight。Weight的计算也比较简单responsetime 减去每个server自己平均的 responsetime是server的权重。当刚开始运行,没有形成statas 时,使用 roubine策略选择server |
RetryRule |
对选定的负载均衡策 略机上重试机制。 |
在一个配置时间段内当选择server不 成功,则一直尝试使用subRule的方式选择一个可用的server |
RoundRobinRule |
轮询方式轮询选择 server |
轮询index,选择index对应位置的 server |
RandomRule |
随机选择一个server |
在index上随机,选择index对应位置 |
调整Ribbon的负载均衡策略
我们可以通过修改配置来调整Ribbon的负载均衡策略,具体代码如下 不写则是默认配置: 轮询
1 2 3
| service-product: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
|
基于Feign实现服务调用
概念
Feign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解即可。
Nacos很好的兼容了Feign,Feign默认集成了 Ribbon,所以在Nacos下使用Fegin默认就实现了负载均衡的效果
Feign的使用
1.加入Fegin 依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
2 在主类上添加Fegin的注解 (启动类)
3 创建一个service, 并使用Fegin实现微服务调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @FeignClient("service-product") public interface ProductService { @GetMapping(value = "/product/{pid}") Product findByPid(@PathVariable("pid") Integer pid); }
说明:当请求进入对应的接口时,当调用当前业务类方法时,会先进入findByPid 方法中,通过get请求访问 该配置的路径 类似于: String url = "service-product"; Product product = restTemplate.getForObject( "http://"+url+"/product/"+pid,Product.class);
|
4 修改controller代码,并启动验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Autowired private ProductService productService;
@RequestMapping("/{pid}") public Order order(@PathVariable("pid") Integer pid){ log.info(">>客户下单,这时候要调用商品微服务查询商品信息"); Product product = productService.findByPid(pid); log.info(">>商品信息,查询结果:"+JSON.toJSONString(product)); Order order = new Order(); order.setUid(1); order.setUsername("测试用户"); order.setPid(pid); order.setPname(product.getPname()); order.setPprice(product.getPprice()); order.setNumber(1);
orderService.saveOrder(order);
log.info("创建订单成功,订单信息为{}",JSON.toJSONString(order)); return order; }
|
5 重启order微服务,查看效果