一、Nacos+Ribbon
Nacos介绍请参考:什么是 Nacos
下载:https://github.com/alibaba/nacos/releases?page=3
安装方法:Nacos 快速开始
运行nacos:
切换到bin目录,在命令窗口输入命令:sh startup.sh -m standalone
成功启动获取访问地址:http://localhost:8848/nacos/index.html
初始用户名 / 密码:nacos / nacos
微服务注册到Nacos:
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
application.yml:
spring:
cloud:
nacos:
discovery:
#指定nacos server的地址,用于微服务发现
server-addr: localhost:8848
#设置机房名称(指定集群名称)
cluster-name: BeiJing
#在nacos中配置命名空间(指定namespace),并且获得uuid
#namespace: 980af6bb-ce43-43f2-91de-9878ca25fa3e
#cluster-name: NanJing
#指定实例元数据
#metadata:
# instance: I'm instance meta data
application:
#微服务名称
name: *******
server:
#端口号
port: ****
同集群优先
package personal.qin.usercenter.configuration;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
try {
//拿到配置文件中的集群名称BeiJing
String clusterName = nacosDiscoveryProperties.getClusterName();
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
//想要请求的微服务的名称
String name = loadBalancer.getName();
//获取服务发现的相关API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
//1、找到指定服务的所有实例A
List<Instance> instances = namingService.selectInstances(name, true);
//2、过滤出相同集群下的所有实例B
List<Instance> sameClusterInstances = instances.stream().filter(instance -> Objects.equals(instance.getClusterName(), clusterName)).collect(Collectors.toList());
//3、如果B是空,就用A
List<Instance> instancesToBeChoosen = new ArrayList<Instance>();
if(CollectionUtils.isEmpty(sameClusterInstances)){
instancesToBeChoosen = instances;
log.warn("发生跨集群的调用,name={}, clusterName={}, instances={}", name, clusterName, instances);
} else {
instancesToBeChoosen = sameClusterInstances;
}
//4、基于权重的负载均衡算法,返回一个实例
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChoosen);
log.info("选择的实例是:port={}, instance={}", instance.getPort(), instance);
return new NacosServer(instance);
} catch (NacosException e) {
log.error("发生异常了", e);
return null;
}
}
}
class ExtendBalancer extends Balancer {
public static Instance getHostByRandomWeight2(List<Instance> hosts){
return getHostByRandomWeight(hosts);
}
}
创建Ribbon配置类
注意:该包一定不能和BootApplication同包
package ribbonconfiguration;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import personal.qin.usercenter.configuration.NacosSameClusterWeightedRule;
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule(){
return new NacosSameClusterWeightedRule(); //同集群优先规则
}
}
创建Ribbon全局配置类
package personal.qin.usercenter.configuration;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;
import ribbonconfiguration.RibbonConfiguration;
@Configuration
//Ribbon全局配置
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}
二、feign
pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
在BootApplication中添加@EnableFeignClients注解:
package personal.qin.contentcenter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class ContentCenterApplication {
public static void main(String[] args) {
SpringApplication.run(ContentCenterApplication.class, args);
}
}
新建FeignClient实现Feign远程请求
package personal.qin.contentcenter.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import personal.qin.contentcenter.domain.dto.UserDTO;
//Feign访问微服务的名称
@FeignClient(name = "user-center")
public interface UserCenterFeignClient {
/**
* Feign会自动构建URL为http://user-center/user/{id}
* @param id
* @return
*/
@GetMapping("/user/{id}")
UserDTO findById(@PathVariable Integer id);
}
新建测试Controller:
package personal.qin.contentcenter.controller;
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;
import personal.qin.contentcenter.domain.dto.UserDTO;
import personal.qin.contentcenter.feignclient.UserCenterFeignClient;
@RestController
@RequestMapping("/content")
public class ContentController {
@Autowired
private UserCenterFeignClient userCenterFeignClient;
@GetMapping("/{id}")
public UserDTO findById(@PathVariable Integer id){
return userCenterFeignClient.findById(id);
}
}
Feign自定义日志级别
在application.yml中结合log添加属性:
#设置日志输出级别
logging:
level:
com.itmuch.contentcenter.feignclient.UserCenterFeignClient: debug
feign:
client:
config:
#想要调用的微服务的名称
user-center:
loggerLevel: full
Feign支持的配置项:
多参数可以使用@SpringQueryMap注解实现:
package com.itmuch.contentcenter.feignclient;
import com.itmuch.contentcenter.domain.dto.user.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "user-center")
public interface TestUserCenterFeignClient {
@GetMapping("/q")
UserDTO query(@SpringQueryMap UserDTO userDTO);
}
在开发中可能会出现多个Feign接口访问同一个微服务的情况,解决方法:
在application.yml中添加属性配置:
spring:
#解决Feign多个Client接口指向同一个微服务出现异常的问题
main:
allow-bean-definition-overriding: true
脱离Nacos单独使用Feign:
package com.itmuch.contentcenter.feignclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "baidu", url = "http://www.baidu.com")
public interface TestBaiduFeignClient {
@GetMapping("")
public String index();
}
@Autowired
private TestBaiduFeignClient testBaiduFeignClient;
@GetMapping("baidu")
public String baiduIndex(){
return testBaiduFeignClient.index();
}
Feign的性能优化
pom.xml中添加依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
application.yml中添加属性配置连接池:
feign:
httpclient:
#让feign使用Apache httpclient做请求,而不是默认的urlconnection
enabled: true
#feign的最大连接数
max-connections: 200
#feign单个路径的最大连接数
max-connections-per-route: 50
将日志的级别设置为basic,不要使用full
feign:
client:
config:
#想要调用的微服务的名称或全局配置
default:
loggerLevel: basic
三、Sentinel
pom.xml中添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.yml中添加属性配置:
# 配置actuator和Sentinel联动
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
下载Sentinel地址:https://github.com/alibaba/Sentinel/releases
整合微服务到控制台:
在application.yml中配置属性:
spring:
cloud:
sentinel:
transport:
#指定sentin控制台的地址
dashboard: localhost:8080
Feign整合Sentinel:
在application.yml中配置属性:
feign:
sentinel:
enabled: true
微服务对限流后的处理fallbackfactory
新建工厂类:
package personal.qin.contentcenter.feignclient.fallbackfactory;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import personal.qin.contentcenter.domain.dto.UserDTO;
import personal.qin.contentcenter.feignclient.UserCenterFeignClient;
@Slf4j
@Component
public class UserCenterFeignClientFallbackFactory implements FallbackFactory<UserCenterFeignClient> {
@Override
public UserCenterFeignClient create(Throwable e) {
return new UserCenterFeignClient() {
@Override
public UserDTO findById(Integer id) {
log.warn("远程调用被限流或者降级了", e);
UserDTO userDTO = new UserDTO();
userDTO.setWxNickname("一个默认用户");
return userDTO;
}
};
}
}
在Feign的客户端接口上添加引用:
@FeignClient(name = "user-center", fallbackFactory = UserCenterFeignClientFallbackFactory.class)
//name:微服务名称
//fallbackFactory:工厂类