Spring-Cloud-Ribbon学习笔记(一):入门

简介

Spring Cloud Ribbon是一个基于Http和TCP的客户端负载均衡工具,它是基于Netflix Ribbon实现的。它不像服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个微服务的基础设施中。理解Ribbon对于我们使用Spring Cloud来讲非常的重要,因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。

在Spring Cloud中,有两种服务调用方式,一种是Ribbon+RestTemplate,另一种是Feign。文本先讲解下基于Ribbon+RestTemplate的用法。

当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。

实现

新建服务spring-cloud-ribbon

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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.yan</groupId>
    <artifactId>spring-cloud-ribbon</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
        </dependency>
    </dependencies>

</project>

配置

yml

server:
  port: 8004
spring:
  application:
    name: spring-cloud-ribbon
  cloud:
    config:
      discovery:
        enabled: true
        service-id: spring-cloud-config-server
  profiles:
    active: dev
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

启动类

核心是配有注解@LoadBalanced的RestTemplate的Bean配置

package com.yan;

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

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Controller

package com.yan.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.StringJoiner;

@RestController
@RequestMapping("/ribbon")
public class RibbonController {
    private final RestTemplate template;

    @Autowired
    public RibbonController(RestTemplate template) {
        this.template = template;
    }

    @GetMapping("/get")
    public Object get(String svcName, String url, String className) {
        Class<?> clazz = getResponseType(className);
        String uri = getUrl(svcName, url);
        return template.getForObject(uri, clazz);
    }

    @PostMapping("/post")
    public Object post(@RequestParam String svcName, @RequestParam String url, @RequestParam String className, @RequestBody HttpEntity<?> httpEntity) throws JsonProcessingException {
        Class<?> clazz = getResponseType(className);
        String url1 = getUrl(svcName, url);
        Object res = template.postForObject(url1, httpEntity, clazz);
        String resJson = new ObjectMapper().writeValueAsString(res);
        System.out.println(resJson);
        return res;
    }

    private Class<?> getResponseType(@RequestParam String className) {
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            clazz = String.class;
        }
        return clazz;
    }

    private String getUrl(String svcName, String url) {
        StringJoiner stringJoiner = new StringJoiner("/");
        String uri = stringJoiner.add(svcName).add(url).toString().replaceAll("/+", "/");
        return "http://" + uri;
    }

}

测试

假如我有两个名叫svcA的服务,同时注册到同一个Eureka上,只是端口号不同,它们都有一个post接口定义如下:

    @Value("${server.port}")
    private int port;

    @PostMapping("/post/{name}")
    public String post(@PathVariable String name, @RequestBody String type) {
        return "svcA:" + port + "===>\n" + "name:" + name + "\ntype:" + type;
    }

打开Postman:
发送请求:http://localhost:8004/ribbon/post?svcName=SVCA&url=/post/xixi&className=java.lang.String
Header:{'Content-Type': 'application/json'}
Body: {'raw': {
{
"headers": {"Content-Type": ["application/json"]},
"body": "haha"
}
}}

多次点击发送请求,在ribbon服务下看到日志:

"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"
"svcA:8081===>\nname:xixi\ntype:haha"
"svcA:8087===>\nname:xixi\ntype:haha"

两个服务交替接收请求,这说明Ribbon配置生效了。

posted @ 2019-02-24 22:46  舒山  阅读(1009)  评论(0编辑  收藏  举报