Nacos-消费者服务调用生产者服务(负载均衡实现调用多个服务器)

业务描述

先创建两个项目module分别为服务提供者和服务消费者,两个都要注册到NacosServer中(本质上就是一个web服务,端口默认是8848),服务提供者可以为服务消费者提供远程调用服务.

在这里插入图片描述

调用方式

说明:

1.RestTemplate方式:定义URL直接调用 ,多个端口则需定义多个URL,使用自定义算法实现负载均衡(如随机).

2.RestTemplate+LoadBalancerClient方式:使用LoadBalancerClient帮助我们获取nacos服务列表,采用轮询方式实现负载均衡

3.RestTemplate+@LoadBalanced方式:采用@LoadBalanced注解,起一个拦截器的作用,拦截之后底层帮我们获取nacos服务列表.(相比方式2就有点耗时)

4.@EnableFeignClients+@FeignClient方式:@EnableFeignClients注解描述启动类时,用于告诉springboot在启动时,扫描启动类所在包或子包中的类,假如接口上有@FeignClient注解描述,则对这样的接口创建其实现类,在实现类内部帮我们进行远程服务调用.

生产者服务的创建

创建服务提供者工程

创建服务提供者工程(module名为sca-provider),继承parent工程(01-sca),添加相关依赖包,其pom.xml文件内容如下:

<?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>01-sca</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sca-provider</artifactId>
    <dependencies>
        <!--web服务-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--服务的注册和发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

创建配置文件application.yml

创建并修改配置文件application.yml(或者application.properties),实现服务注册,关键代码如下:

server:
  port: 8082
spring:
  application:
    name: sca-provider #服务名
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #服务注册和发现(默认是注册到这里)

注意这里需要三个端口,分别为8080,8081,8082.服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。

配置idea同时启动多个服务

需要先进行如下配置,才能启动多个服务.

 进入编辑配置界面

创建启动类

创建启动类,并定义处理请求的控制层对象和方法,关键代码如下:

@SpringBootApplication
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
    @Value("${server.port}")
    private String server;
    @RestController
    public class ProviderController{
        @GetMapping(value = "/provider/echo/{msg}")
        public String doEcho(@PathVariable String msg){
            return server+"say:Hello Nacos Discovery"+msg;
        }
    }
}

刷新nacos服务

启动启动类,然后刷先nacos服务,检测是否服务注册成功,如图所示:

因为我提供了3个端口,所以这里有3个实例数.注意每次修改端口号之后还需再次运行启动类.

消费者服务的创建 

创建服务消费者工程

创建服务消费者(module名为sca-consumer),继承parent工程(01-sca),其pom.xml文件内容如下:

<?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>01-sca</artifactId>
        <groupId>com.jt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>sca-consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

创建配置文件application.yml

修改配置文件application.yml,关键代码如下:

server:
  port: 8090
spring:
  application:
    name: sca-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #从哪里去查找服务

创建启动类

创建启动类并实现服务消费,关键代码如下:

@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    /**创建对象,进行远程服务调用*/
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    @Bean
    @LoadBalanced//这个注解描述RestTemplate对象时,系统底层会对RestTemplate对象的请求进行拦截
    public RestTemplate loadBalanceRestTemplate(){
        return new RestTemplate();
    }
    @RestController
    public class ConsumerController{
        @Value("${spring.application.name}")
        private String appName;
        //负载均衡客户端对象(基于此对象可以从nacos中获取服务列表,
        // 并且可以基于一定的算法从列表中获取一个服务实例)
        @Autowired
        private LoadBalancerClient loadBalancerClient;
        @Autowired
        private RestTemplate restTemplate;
        @GetMapping("/consumer/doRestEchol1")
        public String doRestEchol01(){
            //serviceId要在nacos的服务列表中
            ServiceInstance choose = loadBalancerClient.choose("sca-provider");
            String ip = choose.getHost();
            int port = choose.getPort();
            String url = String.format("http://%s:%s/provider/echo/%s",ip,port,appName);
            return restTemplate.getForObject(url, String.class);//使用了负载均衡
        }
        @Autowired//此注解优先按属性类型进行对象的查找,如果相同类型存在多个,则按照属性名与方法名对应查找
        @Qualifier("loadBalanceRestTemplate")//可以使用此注解指定要注入的具体对象
        private RestTemplate loadBalanceRT;
        @GetMapping("/consumer/doRestEchol2")
        public String doRestEchol02(){
            String url = String.format("http://sca-provider/provider/echo/%s",appName);
            return loadBalanceRT.getForObject(url, String.class);//使用了负载均衡
        }
    }
}

消费者服务调用生产者服务

启动消费者服务,并在浏览器输入http://localhost:8090/consumer/doRestEchol2地址进行访问,假如访问成功会出现,如图所示效果:

Feign方式请求

添加依赖

使用feign进行服务调用时就需要添加如下依赖:

<!--当基于 feign方法进行服务调用时就需要此依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

创建远程调用接口(service层)

创建远程调用接口,接口使用@FeignClient注解。还需要在启动类上面添加@EnableFeignClients注解。

@FeignClient(name = "sca-provider",contextId = "remoteProviderService")
public interface RemoteProviderService {
    //@GetMapping表示以get请求方式访问远程服务
    //"/provider/echo/{msg}"为远程服务的URL
    @GetMapping("/provider/echo/{msg}")
    String echoMsg(@PathVariable("msg") String msg);
}

@EnableFeignClients注解说明:

@EnableFeignClients注解描述启动类时,用于告诉springboot在启动时,扫描启动类所在包或子包中的类,假如接口上有@FeignClient注解描述,则对这样的接口创建其实现类,在实现类内部帮我们进行远程服务调用。

@FeignClient注解说明:

@FeignClient注解用于描述远程调用接口,这个接口不需要你写实现类,你只需要定义访问规则即可(例如请求方式,请求URL,请求参数)。
@FeignClient 注解描述的接口的实现类对象会默认交给spring管理,这个bean对象的名字默认就是name属性指定的值,这个name还有一个层面的含义,就是你远程调用的服务名。
假如@FeignClient注解中添加了contextId属性,则这个属性值默认会作为当前bean对象的名字,此时name的值仅仅作为要调用的服务名对待,一般推荐contextId的值默认为@FeignClient注解描述的接口的名字(首字母小写)。

创建业务控制层

@RestController
@RequestMapping("/consumer/")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    @Value("${server.port}")
    private String server;
    //基于feign方式的服务调用
    @GetMapping("/echo/{msg}")
    public String doFeignEcho(@PathVariable("msg") String msg){
        System.out.println(server);
        //基于feign方式进行远端调用服务(前提是服务必须存在)
        return remoteProviderService.echoMsg(msg+"www");
    }
}

访问异常处理

如超时连接、服务提供方断开等现象,用户提供地址访问的时候则会出现500等访问出错提示,影响用户体验。试验方式:我们在服务提供者的方法里面设置了睡眠时间3s.

修改application.yml配置文件

方式1:在application.yml配置文件中设置建立连接超时时间和读取响应资源超时时间.设置的时间需>睡眠时间,注意不要等于(亲测很大可能同样会异常)

ribbon:
  #建立连接超时时间
  ConnectTimeout: 5000
  #建立连接之后,读取响应资源超时时间
  ReadTimeout: 5000

方式2:如果请求出错,直接采用feign方式的服务熔断处理,提示用户系统繁忙等信息,提高用户体验.

#feign方式的服务熔断处理
feign:
  hystrix: #hystrix 含义是熔断(就相当于服务停止了)或降级
    enabled: true

创建异常处理对象

前提是采用feign方式的服务熔断处理.

//此对象可以作为Feign方式的远程调用异常处理对象
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
    //当正常远程调用的服务不可用时,系统可以调用此方法进行请求处理
    @Override
    public RemoteProviderService create(Throwable throwable) {
        return new RemoteProviderService() {//匿名内部类
            @Override
            public String echoMsg(String msg) {
                //通知运维人员(发短信,发邮件,电话)
                return "服务维护中";
            }
        };
    }
}

整体项目结构

Tomcat请求处理分析 

Nacos配置管理

Nacos获取配置信息的方式

采用的是pull长轮询的方式,每隔30秒向nacos配置中心发起请求,如果没有获取到数据,则等待,最长等待29.5秒,因为到30秒时会有新的请求发送过来.在这里插入图片描述

具体流程:客户端会轮询向服务端发出一个长连接请求,这个长连接最多30s就会超时,服务端收到客户端的请求会先判断当前是否有配置更新,有则立即返回,如果没有服务端会将这个请求拿住“hold”29.5s加入队列,最后0.5s再检查配置文件无论有没有更新都正常进行返回,但等待的29.5s期间如果有配置更新则可以提前结束并返回。

对应需要经常变化的数据一般都写到nacos配置中心,如数据库密码、端口号、日志记录等级等信息。

心跳机制

默认情况下,启动服务后,每隔5秒会向nacos发送一个"心跳包",这个心跳包中包含了当前服务的基本信息。

Nacos收到这个"心跳包"如果发现这个服务的信息不在注册列表中,就进行注册,如果这个服务的信息在注册列表中就表明这个服务还是健康的

如果Nacos15秒内没接收到某个服务的心跳包,Nacos会将这个服务标记为不健康的状态

如果30秒内没有接收到这个服务的心跳包,Nacos会将这个服务从注册列表中剔除

这些时间都是可以通过配置修改的
 

修改application.yml配置文件名称和内容

将application.yml文件名更改为bootstrap.yml.文件的调用顺序的等级为bootstrap.yml>bootstrap.properties>application.yml>application.properties.

注意:如果bootstrap.yml配置文件与nacos配置中心配置了相同内容,则配置中心会覆盖掉配置文件的内容.因为服务启动的时候会先执行配置文件的内容,然后再执行nacos配置中心的内容,后者会覆盖前者.

nacos配置中心文件的配置管理模型应用由namespace,group,service/dataId组成.

  • Namespace:命名空间,对不同的环境进⾏隔离,⽐如隔离开发环境和⽣产环境。
  • Group:分组,将若⼲个服务或者若⼲个配置集归为⼀组。
  • Service/DataId:某⼀个服务或配置集,一般对应一个配置文件。
server:
  port: 8082
spring:
  application:
    name: sca-provider #服务名
  cloud:
    nacos:
      config: #配置中心的配置
        server-addr: localhost:8848
        #namespace: 362c931e-5b01-4c6f-9423-550195659965 #没有指定namespace的话则默认使用public
        file-extension: yml
        #group: DEV_GROUP_1111  #没有指定group的话则默认使用DEFAULT_GROUP     
      discovery: #注册中心
        server-addr: localhost:8848 #服务注册和发现(默认是注册到这里)

#经常变化的数据一般都写到配置中心
logging:
  level:
    com.jt: error

Nacos新建配置

在配置中心设置日志记录等级为warn,bootstrap.yml配置文件的日志记录等级为error,前者会覆盖后者.

查看配置

测试日志记录等级

添加日志打印相关包,日志规范是SLF4J,创建日志打印对象.

//创建java中的日志对象(slf4j是java中的日志规范,是日志对外的窗口,是门面)
    //目前市场上对slf4j规范的实现主要有两种:log4j,logback
    private static final Logger log =
            LoggerFactory.getLogger(ProviderController.class);//成员变量

 创建测试方法,输出日志等级.日志的级别是trace<debug<info<warn<error.

 @Value("${logging.level.com.jt:trace}")//取不到值则使用默认值trace
        private String logLevel;//修改配置中心的等级,此属性值不会改变,因为重新初始化属性需要重新创建对象
        @GetMapping(value = "/provider/doGetLogLevel")
        public String doGetLogLevel(){
            //日志级别trace<debug<info<warn<error
            log.trace("--trace--");
            log.debug("--debug--");
            log.info("--info--");
            log.warn("--warn--");
            log.error("--error--");
            return "log level is "+logLevel;
        }

启动服务,地址栏输入地址http://127.0.0.1:8082/provider/doGetLogLevel.查看结果

由测试结果可以看出,虽然bootstrap.yml配置文件设置了日志等级为error,但输出的是warn,即再次说明nacos配置中心会覆盖配置文件内容.

查看控制台打印结果

可以看到,比warn等级高的也打印出来了,说明日志记录会保留比设置的日志等级高的日志.

修改配置中心的日志等级为info,不用再次启动服务,刷新上面网页,查看结果.

刷新网页,查看结果

 

 可以看到网页内容并没有修改,我们再看一下控制台的结果.

可以看到控制台的结果已经有了改变,说明我们不用重新启动服务也可以对配置文件内容进行修改

但是为什么网页内容没有改变呢?这是因为重新初始化属性需要重新创建对象,如果想要获取修改之后的属性,需要在本类上添加一个注解@RefreshScope.

@RefreshScope 这个注解描述类时,当配置中心数据发生变化会对属性进行重新初始化

添加注解后再次重复以上测试,则可以看到修改之后的结果.

新建命名空间完成配置中心配置测试

 创建nacos配置文件,文件内容如下:

 查看结果

 修改bootstrap.yml配置文件内容

server:
  port: 8082
  tomcat:
    threads:
      max: 300
spring:
  application:
    name: sca-provider #服务名
  cloud:
    nacos:
      config: #配置中心的配置
        server-addr: localhost:8848
        namespace: 362c931e-5b01-4c6f-9423-550195659965
        file-extension: yml
        group: DEV_GROUP_1111
      discovery: #注册中心
        server-addr: localhost:8848 #服务注册和发现(默认是注册到这里)

#经常变化的数据一般都写到配置中心
logging:
  level:
    com.jt: error

创建测试方法,输出最大线程数和核心线程数.

        @Value("${server.tomcat.threads.max:300}")
        private Integer maxThread;
        @Value("${server.tomcat.threads.min-spare:4}")
        private Integer minSpare;
        @GetMapping("/provider/doGetMaxThread")
        public String doGetMaxThread(){
            return "server.threads.max is "+maxThread+" minSpare "+minSpare;
        }

 暂时不重启服务,地址栏输入地址:http://127.0.0.1:8082/provider/doGetMaxThread,查看结果

 可以看到得到的结果是我们设置的默认值,因为我们没有重新启动服务,所以拿不到真实的结果.

重启服务后,查看结果

可以看到,已经拿到nacos配置中心的内容,并且也覆盖掉bootstrap.yml配置文件定义的内容. 

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Nacos和Dubbo都是阿里巴巴开源的项目,Nacos是一款基于云原生架构的动态服务发现、配置管理和服务管理平台,而Dubbo是一款高性能的分布式服务框架。 在使用Nacos和Dubbo进行服务调用时,需要使用Nacos提供的服务发现功能来获取可用的Dubbo服务,然后使用Dubbo提供的RPC框架进行远程调用。 下面是一个使用Nacos和Dubbo进行服务调用的客户端实现示例: 1. 添加依赖 在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>${nacos.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> ``` 其中,${nacos.version}和${dubbo.version}需要根据您使用的版本进行替换。 2. 配置Nacos客户端 在使用Nacos之前,需要先配置Nacos客户端。可以在application.properties文件中添加以下配置: ```properties # Nacos配置 spring.cloud.nacos.config.server-addr=localhost:8848 spring.cloud.nacos.config.namespace= spring.cloud.nacos.config.file-extension=properties # Dubbo配置 dubbo.registry.address=nacos://localhost:8848 ``` 其中,spring.cloud.nacos.config.server-addr为Nacos服务器地址,dubbo.registry.address为Dubbo注册中心地址。 3. 获取Dubbo服务 使用Nacos提供的服务发现功能,获取可用的Dubbo服务。可以在代码中添加以下方法: ```java public List<Invoker<?>> getDubboService(String serviceName) throws NacosException { // 创建Nacos服务发现客户端 NamingService namingService = NacosFactory.createNamingService(nacosProperties.getConfigServerAddr()); // 获取可用的Dubbo服务 List<Instance> instances = namingService.getAllInstances(serviceName); if (instances == null || instances.isEmpty()) { throw new RuntimeException("No available Dubbo service"); } // 将Dubbo服务转换为Invoker List<Invoker<?>> invokers = new ArrayList<>(); for (Instance instance : instances) { URL url = new URL("dubbo", instance.getIp(), instance.getPort(), serviceName); Invoker<?> invoker = new DubboInvoker<Object>(Object.class, url, new RpcClientWrapper()); invokers.add(invoker); } return invokers; } ``` 其中,serviceName为Dubbo服务名称。 4. 远程调用Dubbo服务 获取到可用的Dubbo服务之后,就可以使用Dubbo提供的RPC框架进行远程调用。可以在代码中添加以下方法: ```java public Object invokeDubboService(List<Invoker<?>> invokers, String methodName, Object... args) throws RpcException { // 创建Dubbo调用上下文 RpcContext rpcContext = RpcContext.getContext(); // 随机选择一个Dubbo服务 Invoker<?> invoker = invokers.get(new Random().nextInt(invokers.size())); // 设置Dubbo调用上下文 rpcContext.setInvoker(invoker); rpcContext.setMethodName(methodName); rpcContext.setArguments(args); // 远程调用Dubbo服务 Result result = invoker.invoke(new RpcInvocation(methodName, new Class<?>[0], args)); if (result.hasException()) { throw result.getException(); } return result.getValue(); } ``` 其中,invokers为获取到的Dubbo服务列表,methodName为Dubbo服务方法名,args为Dubbo服务方法参数。 使用以上方法,就可以在Nacos和Dubbo的帮助下,轻松实现服务调用客户端。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值