Three, Spring Cloud's soft load balancing Ribbon

Foreword

On the one we have learned Eureka registry, in fact, we have to use the Ribbon, but we did not go into detail, so we work together to learn about the Ribbon.

What is the Ribbon

Before exposure to load balancing load balancing is hard, what is hard load balancing it? Load balancing is hard in the past, large-scale system, there will be a single set of systems responsible for load balancing strategy, so our request will first go on a load balancing system, assigned to a different server processing.
For example, we are familiar with nginx. In fact, it can be counted as a load-balancing system, the client requests the interface will be assigned to a different server by the nginx load balancing strategy.
Here Insert Picture Description
Ribbon is not that so? That is what is it?
Ribbon is and Eureka are the same as Netflix launched open source product, and Eureka can complete seamless integration, Ribbon major client achieve load balancing.
So what is the client load balancing it?
That is, when the client requests it by balancing policy distribute requests to different servers, as shown
Here Insert Picture Description
in this figure is based on the Eureka section adapted to the architecture diagram, the main process is not changed, service consumers and service service providers will be registered with the center, then consumers will get a list of available service instances from the service center, which will select one instance accessed through load balancing strategy.

Well, we look at the code to use.

demo

We sake of simplicity, the registry service side, we still spend a single node Festival. I do not say how to configure. Then we create a module used to do a service provider, in fact, can also spend a service provider, but I'm afraid to rub together well, so we are all separate, but a lot of the code is the same.

service providers

We create a new module pom.xml file as follows:

<parent>
        <groupId>cn.quellanan</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0.0</version>
    </parent>

    <groupId>com.quellanan.springcloud</groupId>
    <artifactId>ribbon-provider-9004</artifactId>
    <version>1.0.0</version>
    <name>ribbon-provider-9004</name>
    <description>ribbon-provider-9004 服务提供者</description>

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

Mainly the introduction of eureka-client can register go to the registration center, and then start the classes with the @EnableEurekaClient notes
Here Insert Picture Description
plus configuration in the configuration file as follows:

server.port=9004
spring.application.name=ribbon-provider
eureka.client.service-url.defaultZone=http://localhost:8000/eureka/

我们在创建一个测试类:HelloController

@RestController
@Slf4j
public class HelloController {

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

    @RequestMapping("/hello")
    public String hello(){
        log.info(port);
        return "hello "+port;
    }
}

这样我们一个服务提供者就弄好了,为了看出负载均衡的效果,我们还需要创建一个一样的服务提供者。或者不同的端口启动。我们将端口改为9005.其他的和上面一样。

服务消费者

服务提供者有了,我们再来创建一个服务消费者的模块。pom.xml 较服务提供者就多了一个ribbon 的依赖

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

然后启动类中加上@EnableEurekaClient 和 RestTemplate

@SpringBootApplication
@EnableEurekaClient
public class RibbonConsumerApplication {

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

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

}

@LoadBalanced 注解就是来实现客户端负载均衡的。

配置文件中加入如下配置:

server.port=9003
#服务名,在注册时所用
spring.application.name=ribbon-consumer
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

最后我们来写一个调用服务提供者的接口,创建一个IndexController类,内容如下

@RestController
public class IndexController {
    private static final String applicationName = "ribbon-provider";

    @Autowired
    private RestTemplate restTemplate;
    @RequestMapping("/index")
    public String getHello(){
        String url = "http://"+ applicationName +"/hello";
        return  restTemplate.getForObject(url,String.class);
    }
}

测试

我们现在来启动服务中心,两个服务提供者,一个服务消费者。启动之后我们输入:

http://localhost:8000/

Here Insert Picture Description
重点看下ribbon-provider 有两个端口,分别对应的我们的两个服务提供者。他们的appliaction.name是相同的。
再来调下面接口看看

http://localhost:9003/index

Here Insert Picture Description
可以看到每次调用访问了不同的服务提供者,两个服务端提供者是轮寻调用的。从而实现客户端的负载均衡

RestTemplate

上面说的负载均衡,其实还是RestTemplate 对象加上@LoadBalanced来实现的。并且前面只是简单的调用,没有涉及参数和请求方式,接下来我们看看常见的请求方式和有参数的调用。

Get 请求

其实我们之前写的就是get 请求的方式,我们在来写一个有参数的请求

@RequestMapping("index2")
    public String getHello2(){
        String url = "http://"+ applicationName +"/hello2?name={1}";
        return  restTemplate.getForObject(url,String.class,"quellanan");
    }

可以看到url 中请求的参数有占位符代替,getForObject或者getForEntity的第三个参数就是我们实际传的参数。这里说明一下getForObject 是直接获取返回的内容,而getForEntity返回的是一个http对象,包含相应状态码,想要回去内容需要getForEntity().getBody() 才行。

那如果多个参数的呢?
多个参数的常用的有两种方式,一个是和上面一样,直接在后面加参数就好了如下:

@RequestMapping("index3")
    public String getHello3(){
        //多个参数拼接
        String url = "http://"+ applicationName +"/hello3?name={1}&age={2}";
        return  restTemplate.getForObject(url,String.class,"quellanan","18");
    }

还有一种方式就是将参数封装到map 中,传过去。一样的也可以解析

@RequestMapping("index4")
    public String getHello4(){
        //多参数组装
        Map<String,String> parms=new HashMap<>();
        parms.put("name","quellanan");
        parms.put("age","18");
        String url = "http://"+ applicationName +"/hello3?name={name}&age={age}";
        return  restTemplate.getForObject(url,String.class,parms);
    }

我们在提供者中写两个方法便于测试

 @RequestMapping("/hello2")
    public String hello2(@RequestParam("name") String name){
        log.info(name);
        return "hello "+name+port;
    }
    @RequestMapping("/hello3")
    public String hello3(@RequestParam("name") String name,@RequestParam("age") String age){
        log.info(name+age);
        return "hello "+name+age+port;
    }

我们启动来看下结果
Here Insert Picture Description
可以看到参数是传递成功的啦。

Post 请求

post 请求和get 请求差不多,我这里就将参数封装到map中了
postForEntity

    @RequestMapping("index6")
    public String getHello6(){
        //postForEntity
        JSONObject params=new JSONObject();
        params.put("name","quellanan");
        params.put("age","18");
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity request = new HttpEntity(params.toJSONString(), headers);
        String url = "http://"+ applicationName +"/hello4";
        return  restTemplate.postForEntity(url,request,String.class).getBody();
    }

postForObject

@RequestMapping("index7")
    public String getHello7(){
        //postForObject
        JSONObject params=new JSONObject();
        params.put("name","quellanan");
        params.put("age","18");
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity request = new HttpEntity(params.toJSONString(), headers);
        String url = "http://"+ applicationName +"/hello4";
        return  restTemplate.postForObject(url,params,String.class);
    }

主要是先将参数封装在JSONObject 中,然后设置HttpHeaders 和HttpEntity ,然后请求。
我们采用的application/json 的格式,我们在服务提供者中加一个方法。用来接收json格式的参数。

    @RequestMapping("/hello4")
    public String hello4(@RequestBody Map<String, Object> parms){
        return "hello "+parms.get("name")+parms.get("age")+port;
    }

现在我们启动看下效果。
Here Insert Picture Description
证明都是可以正常传递的。

Feign 简化微服务调用

上面我们可以看到,我们进行微服务调用,不管是使用get 或者post 方法,带有参数过多就会导致代码变得很臃肿,所以我们就可以使用同样是netflix 推出的Feign 来简化微服务调用,Feign 结合了Ribbon 以及Hystrix.Ribbon的功能它都有,并且还进行了封装,更加方便我们使用。所以我们使用的时候,只用引入 Feign 的依赖就可以。

那我们现在对上面的这些进行调整一下。

pom.xml

在我们的ribbon-consumer 的pom 文件中删除ribbon 的依赖,增加feign 的依赖。

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

增加@EnableFeignClients

在启动类中增加@EnableFeignClients 注解
Here Insert Picture Description
在启动类中增加了@EnableFeignClients 注解,就可以在项目中使用Fegin 啦,那我们怎么使用呢?
其实还要使用@FeignClient 注解,这个注解是在具体的应用中注入的,用来指定服务提供者的服务名称。并且这个注解只能作用 interface 上。
前面我们调用服务提供者的接口需要写url,参数,返回类型等等非常的繁琐,所以Fegin 就帮我们进行了简化,让我们调用服务提供者的接口,可以像自身调用一样,非常放方便。

HelloService

我们来创建一个HelloService 的接口。内容如下

@FeignClient("ribbon-provider")
public interface HelloService {

    @RequestMapping("/hello")
    public String hello();

    @RequestMapping("/hello2")
    public String hello2(@RequestParam(value = "name") String name);

    @RequestMapping("/hello3")
    public String hello3(@RequestParam(value = "name") String name,@RequestParam(value = "age") String age);

    @RequestMapping("/hello4")
    public String hello4(@RequestBody Map<String, Object> parms);

}

可以看到,上面的接口内容主要是针对服务提供者暴露出的几个接口进行调用。
Here Insert Picture Description
对比服务消费者中的接口,和服务提供者中的接口,可以发现其实是服务提供者中的接口对消费者中的HelloService 的实现。这个待会再说。从HelloService 中我们可以看到,我们调用服务提供者的接口一样的采用@RequestMapping,@RequestParam,@RequestBody来操作,这样更加简洁和方便。

FeginController

我们再创建一个FeginController 来测试一下,内容如下:


@RestController
public class FeginController {

    @Autowired
    public HelloService helloService;


    @RequestMapping("/fegin")
    public String getHello(){
        return  helloService.hello();
    }

    @RequestMapping("/fegin2")
    public String getHello2(){
        String name="quellanan";
        return  helloService.hello2(name);
    }


    @RequestMapping("/fegin3")
    public String getHello3(){
        String name="quellanan";
        String age="18";
        return  helloService.hello3(name,age);
    }

    @RequestMapping("/fegin4")
    public String getHello4(){
        Map<String, Object> parms=new HashMap<>();
        parms.put("name","quellanan");
        parms.put("age","18");
        return  helloService.hello4(parms);
    }
}

可以看到就是普通的controller层调用service 层。

测试

好了我们现在来测试一下。
分别输入如下地址来看看效果:

http://localhost:9003/fegin
http://localhost:9003/fegin2
http://localhost:9003/fegin3
http://localhost:9003/fegin4

Here Insert Picture Description

继承

Well, for the last question, just like we said above the HelloController HelloService service providers and service consumers, and feel like HelloController achieved HelloService. Yes, when we formally developed, you will find the interface call is very large, and very complex, if by then follow the above manner, there will be a lot of code duplication and error-prone, so we can extract the service call alone into a What module, then separately introduced its dependence on service providers and service consumers, and then inherit their corresponding interface in consumption of HelloService. To achieve its corresponding interface in the service provider. Of course @FeignClient or in the service of consumers.

Here are just offer a new idea, is not given implementation, interested can look at "Spring cloud micro-combat service," and I can discuss the next hehe.

Extra

Finally finished, it is some idea of ​​the ribbon and fegin, at least you can now use them, and use them to project what the problem is no matter.

Code is uploaded to GitHub:
https://github.com/QuellanAn/springcloud

Come follow ♡

Am honored to participate in this year's star of the CSDN blog contest, help cast a vote, you can cast five votes, Thank you

CSDN blog Star contest

Finally we,
welcome attention to individual public number "Programmers love yogurt"

Share a variety of learning materials including java, linux, big data. Information includes video documentation and source code, while sharing themselves and delivery of high-quality technology blog.

If you like attention and remember to share yo ❤

file

Guess you like

Origin www.cnblogs.com/quellanan/p/12193968.html