美文网首页springbootSpring cloudSpring
SpringCloud 服务间的调用

SpringCloud 服务间的调用

作者: weisen | 来源:发表于2018-09-26 15:47 被阅读4次

    总结:

    spring-cloud调用服务有两种方式,一种是Ribbon+RestTemplate, 另外一种是Feign。
    Ribbon是一个基于HTTP和TCP客户端的负载均衡器,其实feign也使用了ribbon, 只要使用@FeignClient时,ribbon就会自动使用。

    一、Ribbon

    1.1
    pom文件

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

    新建bootstrap.yml

    server:
      port: 8910
    
    eureka:
      client:
        serviceUrl:
              defaultZone: http://localhost:8010/eureka/
    
    spring:
      application:
          name: client-a
    

    ClientApplication, 这里我们需要注册一个RestTemplate,并且使用@LoadBalanced开启负载功能

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ClientApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ClientApplication.class, args);
        }
    
        @Bean
        @LoadBalanced
        RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    

    测试用的controller

    @RestController
    public class TestController {
    
        @Autowired
        RestTemplate restTemplate;
    
        @RequestMapping("/hi")
        public String hi(@RequestParam String id){
            return restTemplate.getForObject("http://service-a/hi?id="+id, String.class);
        }
    }
    

    1.2
    为了测试负载功能,这里要再新建一个模块service-b, 和上一篇的service-a的代码基本相同,只把端口修改了就可以。
    把client-a和service-b都启动成功后,打开eureka中心应该看到:

    1.3
    打开http://localhost:8910/hi?id=123

    image

    可以看到服务已经成功调用。
    然后刷新页面


    image

    看到端口已经改变,说明负载功能成功实现

    二、feign

    Feign是Netflix开发的声明式、模块化的HTTP客户端。Feign可帮助我们更好更快的便捷、优雅地调用HTTP API。

    上面我们访问其它微服务方式都是通过Ribbon组件提供的一些模板方法去访问的,使用过程中会发现一个问题就是对某服务接口调用往往不是一处的,于是我们需要这个服务接口进行一个封装,其它地方统一调用这个封装,而每个封装其实也就是对用RestTemplate的模板方法对目标服务接口的调用,这样代码里就会有很多冗余的RestTemplate方法了,代码很不优雅,而使用了Spring Cloud Feign就不一样了,它是Spring Cloud Hystrix和Spring Cloud Ribbon整合,有了它在框架层面Spring Cloud就帮我们封装好这些东西了,我们只需要定义一个需要访问目标服务方法的接口,就可以直接通过依赖注入直接使用了,省去了我们自己封装那一步,代码也更加优雅了。
    2.1
    新建模块client-b
    pom文件

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

    bootstrap.yml

    server:
      port: 8911
    
    eureka:
      client:
        serviceUrl:
              defaultZone: http://localhost:8010/eureka/
    
    spring:
      application:
          name: client-b
    

    ClientApplication, 在程序的启动类加上注解@EnableFeignClients开启feiginClient功能

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

    通过以上步骤,该程序已经具备了Feign的功能,现在来实现一个简单的feign client。新建一个EurekaClientFeign的接口,在接口上加@FeignClient注解来声明一个Feign Client。value为远程调用其他服务的服务名,FeignConfig.class为配置类

    @Component
    @FeignClient(value = "service-a",configuration = FeignConfig.class) //这里的name对应调用服务的spring.applicatoin.name
    public interface ServiceAFeignClient {
    
        @RequestMapping(value = "/hi")
        String hi(@RequestParam("id") String id);
    
    }
    

    在FeignConfig类上加上@Configuration注解,表明这是一个配置类,并注入一个BeanName为feignRetryer的Retryer的Bean。可使feign在远程调用失败后会进行重试。

    @Configuration
    public class FeignConfig {
        @Bean
        public Retryer feignRetryer(){
            return new Retryer.Default(100,TimeUnit.SECONDS.toMillis(1),5);
        }
    }
    

    Controller

    @RestController
    public class TestController {
    
        @Autowired
        ServiceAFeignClient serviceAFeignClient;
    
        @RequestMapping("/hi")
        public String hi(@RequestParam String id){
            return serviceAFeignClient.hi(id);
        }
    }
    

    2.2

    运行后的结果应该是和ribbon的相同。
    个人感觉使用feign比较舒服,代码比较简洁。

    总结:feign源码实现过程如下

    1.首先通过@EnableFeignClients注解开启FeignClient功能。只有这个注解的存在,程序启动时才会开启对@FeignConfig注解的包的扫描,
    2.根据Feig的规则实现接口,并在接口上面加上@FeignClient注解。
    3.程序启动后会进行包扫描,扫描所有@FeignClient注解的类,并将这些信息注入IOC容器中。
    4.当接口的方法被调用时,通过JDK的代理来生成具体的RequestTemplate模板对象。
    5.根据RequestTemplate再生成Http请求的Request对象。
    6.Request对象交给Client去处理,其中Client的网络请求框架可以是HttpURLConnection、HttpClient和OkHttp。
    7.最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

    三、Feign Hystrix支持

    pom引入:

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

    在配置文件application.yml中需要加上配置项目

    feign:
      hystrix:
        enabled: true
    

    在启动类中加上注解:@EnableHystrix

    Hystrix支持回调,当断路器打开的时候会回调默认的代码。为了回调指定的方法,可以在@FeignClient中设置fallback属性,它的值是类的名字。例如:

    @FeignClient(name = "bd-util-kafka",fallback = KafkaServiceHystric.class)
    public interface KafkaClient {
    
        @RequestMapping(value = "/kafka/sendMessage", method = RequestMethod.POST)
        public String sendMessage(@RequestParam("topic") String topic, @RequestParam("value") String value);
    
    }
    

    KafkaServiceHystric中的代码如下:

    @Component
    public class KafkaServiceHystric implements KafkaClient {
        @Override
        public String sendMessage(String topic, String value) {
            return null;
        }
    

    如果需要获得回调触发的原因,可以使用@FeignClient的fallbackFactory属性。

    @FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
    protected interface HystrixClient {
        @RequestMapping(method = RequestMethod.GET, value = "/hello")
        Hello iFailSometimes();
    }
    
    @Component
    static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
        @Override
        public HystrixClient create(Throwable cause) {
            return new HystrixClientWithFallBackFactory() {
                @Override
                public Hello iFailSometimes() {
                    return new Hello("fallback; reason was: " + cause.getMessage());
                }
            };
        }
    }
    

    当然也可以用@EnableCircuitBreaker+@HystrixCommand的方式,可能更方便一点儿

    Feign和@Primary

    当使用Feign与Hystrix回退时,在同一类型的ApplicationContext中有多个bean。这将导致@Autowired不起作用,因为没有一个bean,或者标记为主。要解决这个问题,Spring Cloud Netflix将所有Feign实例标记为@Primary,所以Spring Framework将知道要注入哪个bean。在某些情况下,这可能是不可取的。要关闭此行为,将@FeignClient的primary属性设置为false。

    @FeignClient(name = "hello", primary = false)
    public interface HelloClient {
        // methods here
    }
    

    参考:https://www.cnblogs.com/cjsblog/p/7988979.html
    参考:https://www.cnblogs.com/andyfengzp/p/6830963.html
    参考:https://www.cnblogs.com/cralor/p/9228678.html

    相关文章

      网友评论

        本文标题:SpringCloud 服务间的调用

        本文链接:https://www.haomeiwen.com/subject/bhkdoftx.html