【SpringCloud】 Re01

简单理解

接口跨服务调用 说白了 就是原来只是调用一个接口就能得到资源,现在是调用两个或者更多接口,这些接口来自不同的服务

(从前端的角度来看依然只是调用这个接口,只是这个接口背后又去调用其他的接口了)

没有SpringCloud提供的东西也能做到,例如使用最底层的HttpClient,是Java桌面端 和 安卓的一些通信的解决办法,同样使用于JavaWeb

RestTemplate的使用

1、首先任何组件都需要注册到SpringBoot中

package cn.itcast.order;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.config.DefaultFeignConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class)
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    /**
     * 创建RestTemplate并注入Spring容器
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

   /* @Bean
    public IRule randomRule() {
        return new RandomRule();
    }*/
}

通常来说这种注册的实例,都会被集中管理到配置类编写

第二步是调用

package cn.itcast.order.service;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    @Autowired
    private RestTemplate restTemplate;

//    public Order queryOrderById(Long orderId) {
//        // 1.查询订单
//        Order order = orderMapper.findById(orderId);
//        // 2.用Feign远程调用
//        User user = userClient.findById(order.getUserId());
//        // 3.封装user到Order
//        order.setUser(user);
//        // 4.返回
//        return order;
//    }



    public Order queryOrderById(Long orderId) {
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.利用RestTemplate发起http请求,查询用户
        // 2.1.url路径
        String url = "http://userservice/user/" + order.getUserId();
        // 2.2.发送http请求,实现远程调用
        User user = restTemplate.getForObject(url, User.class);
        // 3.封装user到Order
        order.setUser(user);
        // 4.返回
        return order;
    }
}

提供者 & 消费者 概念

提供者,这个服务提供给其他服务进行消费(所谓消费就是调用的意思,提供也可以是生产的意思)

消费者,这个服务调用其他服务的接口

对于真实的企业架构而言,会存在一个服务链,例如这样 A -> B -> C -> .... 这样

所有提供者和消费者的概念是相对的,也可以说一个服务既可以是提供者,也可以是一个消费者

Eureka 服务注册中心

发现的问题:

public Order queryOrderById(Long orderId) {
    // 1.查询订单
    Order order = orderMapper.findById(orderId);
    // 2.利用RestTemplate发起http请求,查询用户
    // 2.1.url路径
    String url = "http://userservice/user/" + order.getUserId();
    // 2.2.发送http请求,实现远程调用
    User user = restTemplate.getForObject(url, User.class);
    // 3.封装user到Order
    order.setUser(user);
    // 4.返回
    return order;
}

真实开发环境是有

1测试环境DEV

2生产环境PROD

3预生产环境PREPROD

等等

而且一个服务还有可能架设服务集群,服务01服务02服务03...

所以不能写死一个服务地址,在以前没有Eureka注册中心的时候,解决方案是配置文件 + 注释,要切换环境就找注释的环境换回来

配置文件中记载所有的环境信息,但是这些信息只能让我们知道有哪些服务,并不知道服务的健康状态,

所谓健康状态,就是不知道这个服务是否正常运行中的

概念与作用

Eureka本身也是一个服务,这个服务脱离业务需求功能,专门用于注册服务信息

注册中心收集所有的服务信息,这些服务就可以通过Eureka来获取 业务需要的服务信息

被注册的服务就是客户端,注册中心则是服务端

Eureka同时还要检测服务是否正常,如何检测?就是向服务发送请求,如果服务正常响应 那就是正常的,反之则不正常

代码片段(搭建注册中心):

依赖:

        <!--eureka服务端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

执行类配置Eureka注解:

package cn.itcast.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

Eureka服务的application.yml配置信息

eureka自身也需要注册进来,因为Eureka作为服务也可能是一个服务集群的存在

server:
  port: 10086 # 服务端口
spring:
  application:
    name: eurekaserver # eureka的服务名称
eureka:
  client:
    service-url:  # eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

运行Eureka之后访问地址:

http://localhost:10086/

 

注册服务:

将需要被注册的服务添加Eureka的注册组件,编写注册信息即可

所有需要注册的服务都添加上Eureka注册组件

        <!--eureka客户端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

在服务的application.yml中配置注册信息:

spring:
    application:
        name: userservice
eureka:
  client:
    service-url:  # eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

一个服务启动多个实例的操作:

 

发现服务

被注册的服务都有服务的名称

spring:
    application:
        name: userservice

名称代表了 ip地址 + 端口

所以在远程调用的时候就可以直接使用服务名称了

String url = "http://userservice/user/" + order.getUserId();

为了实现负载均衡,(在视频的案例中一个服务可以开启集群,他们的名字相同,但是IP和端口会不同)

要保证请求的调用压力都平均的分摊到集群中去,就要解决这个负载均衡的问题

在RestTemplate上注解@LoadBalanced

package cn.itcast.order;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.config.DefaultFeignConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class)
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    /**
     * 创建RestTemplate并注入Spring容器
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

   /* @Bean
    public IRule randomRule() {
        return new RandomRule();
    }*/
}

Ribbon 负载均衡组件

实现流程

 

均衡规则:

1、轮巡调度 RoundRobin

2、重试? Retry

3、随机访问 RandomRule

ZoneAvoidance规则

https://blog.csdn.net/chengqiuming/article/details/81290016

 

 配置均衡策略:

使用Bean实例配置

要使用什么策略,就返回对应的策略实例

package cn.itcast.order;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.config.DefaultFeignConfiguration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class)
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    /**
     * 创建RestTemplate并注入Spring容器
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule randomRule() {
        return new RandomRule();
    }
}

第二种配置方式使用application.yml配置

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule  # 负载均衡规则

Ribbon饥饿加载配置:

Ribbon默认是进行懒加载的,也就是第一次调用会非常慢,以后调用才会正常速度

在订单服务中指定用户服务饥饿加载

也就是消费者中指定提供者配置

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients: # 指定饥饿加载的服务名称
      - userservice

所以饥渴加载的意义在于降低首次访问的耗时,在服务启动的时候准备就绪

原文地址:https://www.cnblogs.com/mindzone/p/15312185.html