SpringCloud微服务小白也能搭(Hoxton.SR8)(二)Ribbon|服务消费者

简单上手,直接照搬,就可搭建微服务(Hoxton.SR8) 2020.8.28发布,SpringCloud搭建的文章正在整理,干货不要错过哦

摘要:

Spring Cloud Netflix Ribbon 是Spring Cloud Netflix 子项目的核心组件之一,主要给服务间调用及API网关转发提供负载均衡的功能,在微服务架构中,很多服务都会部署多个,其他服务去调用该服务的时候,如何保证负载均衡是个不得不去考虑的问题。负载均衡可以增加系统的可用性和扩展性,当我们使用RestTemplate来调用其他服务时,Ribbon可以很方便的实现负载均衡功能。本文将对其用法进行详细介绍。

1.创建一个 user-service 模块

注意:配置及搭建参考之前第(一)文章的 客户端 配置

   

1.1 创建 UserApiController 提供 接口服务

package com.zqh.www.controller;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 用户服务类
 */
@RestController
@RequestMapping("/api/user")
public class UserApiController {

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

    @GetMapping("/getUserList")
    public Map<String, Object> getUserList() {
        System.out.println("我是:" + port + "有人调用我啦");
        ArrayList<User> dataList = new ArrayList<>();
        User user = new User();
        user.setUserId(1);
        user.setUserName("我是测试1");
        dataList.add(user);
        User user2 = new User();
        user2.setUserId(2);
        user2.setUserName("我是测试2");
        dataList.add(user2);
        HashMap<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", "200");
        resultMap.put("msg", "获取成功");
        resultMap.put("data", dataList);
        return resultMap;
    }

    // 为了方便演示,所以我就直接在这里创建了一个类,大家不要照搬哦
    class User {
        private Integer userId;
        private String userName;

        public Integer getUserId() {
            return userId;
        }

        public void setUserId(Integer userId) {
            this.userId = userId;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }
    }
}

2.创建ribbon-service模块,用于调用user-service

注意:配置及搭建参考之前第(一)文章的 客户端 配置

  

  

2.1 创建 RibbonApiController 调用 UserApiController 

package com.zqh.www.controller;

import com.zqh.www.utils.RestTemplateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/api/ribbon")
public class RibbonApiController {

    @Autowired
    private RestTemplateUtils restTemplateUtils;

    private String userServiceUrl = "http://user-service";
//    private String userServiceUrl = "http://localhost:8084";

    @GetMapping("/getUserList")
    public Map<String, Object> getUserList() {
        return restTemplateUtils.exchange(userServiceUrl + "/api/user/getUserList", HttpMethod.GET, null, null, null, Map.class);
    }
}

2.2 RestTemplateUtils

package com.zqh.www.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@Component
public class RestTemplateUtils {

    private final static Logger logger = LoggerFactory.getLogger(RestTemplateUtils.class);

    @Autowired
    private RestTemplate restTemplate;

    /**
     * @param url           请求路径
     * @param method        请求方式? HttpMethod.POST/HttpMethod.GET
     * @param uriVariables  表单传参的参数
     * @param jsonObject    JSON格式传参的参数
     * @param requestHeader 请求头
     * @param responseType  返回结果Class
     * @return
     * @Description: JSON格式参数和表单参数一起传
     */
    public <T> T exchange(String url, HttpMethod method, Map<String, Object> uriVariables, Object jsonObject, HttpHeaders requestHeader, Class<T> responseType) {
//		一般的表单传参
        String uriVariablesParam = "";
        if (null != uriVariables && 0 != uriVariables.size()) {
            StringBuffer uriSb = new StringBuffer("?");
            uriVariables.forEach((k, v) -> {
                uriSb.append(k).append("=").append("{").append(k).append("}").append("&");
            });
            uriVariablesParam = uriSb.substring(0, uriSb.length() - 1).toString();
        }
        HttpHeaders requestHeaders = requestHeader;
        if (null == requestHeaders) {
            requestHeaders = new HttpHeaders();
        }

        HttpEntity<Object> requestEntity = new HttpEntity<>(null, requestHeaders);
        if (null != jsonObject) {
            requestEntity = new HttpEntity<>(jsonObject, requestHeaders);
        }
        url += uriVariablesParam;
        long startTime = System.currentTimeMillis();
        if (null == uriVariables || 0 == uriVariables.size()) {
            T body = restTemplate.exchange(url, method, requestEntity, responseType).getBody();
            logger.info("【接口请求】【{}】【处理时间:{}】【普通参数:{}】【JSON参数:{}】【返回结果:{}】", url, System.currentTimeMillis() - startTime, uriVariables, jsonObject, body);
            return body;
        }
        T body = restTemplate.exchange(url, method, requestEntity, responseType, uriVariables).getBody();
        logger.info("【接口请求】【{}】【处理时间:{}】【普通参数:{}】【JSON参数:{}】【返回结果:{}】", url, System.currentTimeMillis() - startTime, uriVariables, jsonObject, body);
        return body;
    }
}

2.3 RibbonConfig

package com.zqh.www.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class RibbonConfig {

    /**
     * 使用@LoadBalanced注解赋予RestTemplate负载均衡的能力,以及根据微服务名称调用接口的能力
     *
     * @return
     */
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.registerModule(simpleModule);

        RestTemplate restTemplate = new RestTemplate();
        List<HttpMessageConverter<?>> converters = new ArrayList<>();
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        jsonConverter.setObjectMapper(objectMapper);
        converters.add(jsonConverter);
        restTemplate.setMessageConverters(converters);
        return restTemplate;
    }
}

3.单服务测试调用

    

 

4.负载服务

注意:

  1. 先开启一个user-service 不用关闭,然后复制一个userService,改一下名字

      
  2. 修改 user-service  的yml 文件,注意之前的服务是没有关闭的还是在跑的,然后启动刚刚新配置的应用

      
  3. 此时的 eureka 注册中心会出现2个user-service

4.1 测试调用

第一次调用:发现8086端口的user-service 被调用了,8084的user-service未被调用

  

第一次调用:发现8084端口的user-service 被调用了,8086的user-service未被调用,负载完成
 

5 ribbon 常用配置

注意:

  1. 配置不生效,文档找了没发现原因。。如果有读者知道原因,请告知,非常感谢

5.1 全局配置

ribbon:
  httpclient:
    enabled: true
  ConnectTimeout: 3000 #服务请求连接超时时间(毫秒)
  ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
  OkToRetryOnAllOperations: true #对超时请求启用重试机制
  MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
  MaxAutoRetries: 1 # 切换实例后重试最大次数
  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法

5.2 单独服务配置

user-service:
  ribbon:
    httpclient:
      enabled: true
    ConnectTimeout: 3000 #服务请求连接超时时间(毫秒)
    ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
    OkToRetryOnAllOperations: true #对超时请求启用重试机制
    MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
    MaxAutoRetries: 1 # 切换实例后重试最大次数
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法

6. 总结

  1. 使用ribbon来进行服务调用,并测试了服务负载的情况下接口调用情况
  2. 处理使用 RestTemplate 返回结果为List 类型时数据缺失  

7.gitee地址

源码参考

猜你喜欢

转载自blog.csdn.net/itjavaee/article/details/109049288