【nacos】RestTemplate

远程调用的一种方式
RestTemplate

package com.jt.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Random;

@RestController
public class ConsumerController {
    //可以实现远程调用
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    //此对象可以基于服务名从nacos中获取服务实例,然后在nacos客户端采用负载均衡实现
    private LoadBalancerClient loadBalancerClient;//实现呢类RibbbonLoadBalancerClient
    //Ribbbon调用IRrule下面的算法获取到实例
    @Value("${spring.application.name:8090}")
    private String appName;

//http://localhost:8090/consumer/do02
    @GetMapping("/consumer/do02")
    public String do02(){
        String url = "http://localhost:8081/provider/8090";
        return restTemplate.getForObject(url,//远端服务的URl
                String.class);//远端读物url对应的返回值类型
    }
    /**方式1
     *  http://localhost:8090/consumer/do04
     *  负载均衡实现
     */
    @GetMapping("/consumer/do03")
    public String do03(){
        //不推荐使用 这个实现负载均衡
        String url = "http://localhost:8081/provider/8090";
        String url1 = "http://localhost:8082/provider/8090";
        String[] strings = new String[]{url,url1};
        int num = new Random().nextInt(2);
        return restTemplate.getForObject(strings[num],//远端服务的URl
                String.class);//远端读物url对应的返回值类型
    }

    /**方式2
     *  http://localhost:8090/consumer/do04
     *  负载均衡实现
     */

    @GetMapping("/consumer/do04")
    public String do04(){
        //基于服务名获取实例
        ServiceInstance provider = loadBalancerClient.choose("provider");//服务名
        String ip = provider.getHost();
        int port = provider.getPort();
        //构建远程服务url
        String url = "http://"+ip+":"+port+"/provider/8090";
        return restTemplate.getForObject(url,//远端服务的URl
                String.class);//远端读物url对应的返回值类型
    }

    /**方式3
     *  http://localhost:8090/consumer/do04
     *  负载均衡实现
     */
    @Autowired
    @Qualifier("LoadBalancedRestTemplate")//可以指定方法名另一种写法
    private RestTemplate restTemplate2;
    @GetMapping("/consumer/do05")
    public String do05(){
//    String serviceNeme="provider";
    String url = String.format("http://%s/provider/%s","provider",appName);
        return restTemplate2.getForObject(url,//远端服务的URl
                String.class);//远端读物url对应的返回值类型
    }
}

启动类
注入对象

package com.jt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
    /**
     * spring web模块中提供了一个RestTemplate对象可以完成远程调用
     *
     * consumer--->resttemplare--->provider
     * @return
     */
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    /**
     * 当使用@LoadBalanced描述RestTemplate对象时
     * 拦截请求基于服务名找到服务实例(负载均衡)
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate LoadBalancedRestTemplate(){
        return new RestTemplate();
    }
}

被调用放:启动多个服务

package com.jt.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * 构建Controller对象,用于处理客户端请求
 */
@RestController
public class ProviderController {
    /**
     * 从项目配置文件中读取server.port的值,然后赋值给serverPort属性,
     * 冒号右边的8080为一个默认值,假如从配置文件读取不到server.port
     * ,此时会将默认值赋值给属性serverPort
     */
    @Value("${server.port:8080}")
    private String serverPort;

    //http://localhost:8081/provider/echo/client
    @GetMapping("/provider/{msg}")
    public String doRestEcho1(@PathVariable("msg") String msg) throws InterruptedException {
        //模拟耗时操作
        //Thread.sleep(5000);
        return serverPort+" say hello "+msg;
    }
}

基于Feign的远程服务调用
背景分析
服务消费方基于rest方式请求服务提供方的服务时,一种直接的方式就是自己拼接url,拼接参数然后实现服务调用,但每次服务调用都需要这样拼接,代码量复杂且不易维护.

Feign是什么
Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现。如图所示:

Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,最终 Feign 由一些社区进行维护,更名为 OpenFeign。

Feign应用实践(掌握)
第一步:在服务消费方,添加项目依赖(SpringCloud团队基于OpenFeign研发了starter),代码如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

第二步:在启动类上添加@EnableFeignClients注解,代码如下:

@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {}

第三步:定义Http请求API,基于此API借助OpenFeign访问远端服务,代码如下:

package com.jt.servicer;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * FeignClient描述feign远程服务调用接口
 */
@FeignClient(name = "provider")
public interface RemoteProviderService {

    @GetMapping("/provider/{msg}")
    String Message(@PathVariable("msg") String msg);
}

其中,@FeignClient描述的接口底层会为其创建实现类。

第四步:

package com.jt.controller;

import com.jt.servicer.RemoteProviderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("consumer")
public class FeignConsumerController {
    @Autowired
    private RemoteProviderService remoteProviderService;
    //http://localhost:8090/consumer/do01/8090
    @GetMapping("/do01/{msg}")
    public String do01(@PathVariable("msg")String msg){
        return remoteProviderService.Message(msg);//调用接口
    }

}

Feign配置进阶实践
一个服务提供方通常会提供很多资源服务,服务消费方基于同一个服务提供方写了很多服务调用接口,此时假如没有指定contextId,服务
启动就会失败,例如,假如在服务消费方再添加一个如下接口,消费方启动时就会启动失败,例如:

@FeignClient(name = "Provider",contextId = "OtherService")
public interface OtherService {
    @GetMapping("/doSomeThing")
    public String doSomeThing();
}

其启动异常如下:

The bean ‘optimization-user.FeignClientSpecification’, defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.

此时我们需要为远程调用服务接口指定一个contextId,作为远程调用服务的唯一标识(这个标识是Bean对象的名字)即可,例如:

@FeignClient(name = "provider",
            contextId = "RemoteProviderService",
           )
public interface RemoteProviderService {

    @GetMapping("/provider/{msg}")
    String Message(@PathVariable("msg") String msg);
}

还有,当我们在进行远程服务调用时,假如调用的服务突然不可用了或者调用过程超时了,怎么办呢?一般服务消费端会给出具体的容错方案,例如,在Feign应用中通过FallbackFactory接口的实现类进行默认的相关处理,例如:

第一步:定义FallbackFactory接口的实现,代码如下:

/**

  • 基于此对象处理RemoteProviderService接口调用时出现的服务中断,超时等问题
    */
package com.jt.servicer;

import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

/**
 * 服务回调工厂,当远程服务不可用或者远程服务掉用超时,可以通过
 * 配置的方式,调用本地服务返回一个结果
 */
@Component
public class RremoteproviderFallbackFactory implements FallbackFactory<RemoteProviderService> {

    @Override
    public RemoteProviderService create(Throwable throwable) {
        return new RemoteProviderService() {
            @Override
            public String Message(String msg) {
                return "服务维护中,一会在访问";
            }
        } ;
    }
}

第二步:在Feign访问接口中应用FallbackFactory对象,例如:

@FeignClient(name = "provider",
            contextId = "RemoteProviderService",
            fallbackFactory = RremoteproviderFallbackFactory.class)
public interface RemoteProviderService {

    @GetMapping("/provider/{msg}")
    String Message(@PathVariable("msg") String msg);
}

第三步:在配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制.

#启动feign方式调用时的服务中断处理机制
feign:
  hystrix:
    enabled: false  #falsetrue表示启动超时熔断机制 优先级高

第四步:在服务提供方对应的调用方法中添加Thread.sleep(5000)模拟耗时操作,然后启动服务进行访问测试.

Feign 调用过程分析(了解)
Feign应用过程分析(底层逻辑先了解):
1)通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
2) Feign Starter 会在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
3) Feign接口被调用时,底层代理对象会将接口中的请求信息通过编码器创建 Request对象,基于此对象进行远程过程调用。
4) Feign客户端请求对象会经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
5) Feign客户端会携带 Request 调用远端服务并返回一个响应。
6) Feign客户端对象对Response信息进行解析然后返回客户端。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值