整合nacos配置中心的文章https://blog.csdn.net/rocklee/article/details/124299187
以下是SpringCloud整合Nacos注册中心的步骤
- SpringCloud的架构
- API模块
- 提供服务
- 依赖
- 配置文件
- api实现
- 消费者
- 依赖
- 配置文件
- 配置类
- 拦截
- ribbon方式调用
- feign方式调用
- api实现
- 调用
先说一下版本问题,本文引用框架的版本如下:
boot: 2.4.2
nacos: 2021.1
spring-cloud: 2020.0.5
相对来讲SpringCloud跟Dubbo类似的做法,也需要将api的接口独立出来,如easy-cloud-api。
比如我在这里定义一个TestService,要注意的是SpringCloud跟Dubbo不一样,它跑的协议和普通的boot应用一样,用的是restful,所以需要用到web的注解,所以我们需要将一些provider和consumer都需要用到的web注解都放到接口里面,省得两边都打,比如下面的@GetMapp和@RequestParam等。
public interface TestService {
@GetMapping("/test")
public Object test(@RequestParam("who") String who);
}
提供服务
依赖
SpringCloud的Provider端就是一标准的Springboot应用,但是要加入cloud的发现服务,我选择nacos作为服务自治中心,因此要改pom依赖,增加
配置文件com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
再修改bootstrap.yml,加上cloud的配置参数:
server:
tomcat:
accept-count: 800 #如果max线程都满负载工作,还能接受多少排队任务
threads:
max: 200 #最大线程数
min-spare: 12 #核心线程数
max-connections: 5000
port: 2000
spring:
#main: #不引用org.springframework.boot:spring-boot-starter-web就好
# web-application-type: none
application:
name: ${services.account-service}
profiles:
include: jdbc,config
output:
ansi:
enabled: always
cloud:
inetutils:
preferred-networks: 128.30
nacos:
username: seata
password: seatapass
config:
server-addr: ${nacos.server-addr}
#文件扩展名默认就是properties,还可以是yaml
file-extension: yml
refresh-enabled: true
#name: spec-config.yml
discovery:
server-addr: ${nacos.server-addr}
service: ${services.account-service}
api实现
接着写TestServie的实现,也就是一Controller,注意TestService接口里面有的注解,这里就不用再重复:
@RestController
@RequestMapping("/test")
public class TestController implements TestService {
@Resource
private DiscoveryClient discoveryClient;
@Resource
private Registration registration;
public Object test(@RequestParam("who") String who){
List instance=discoveryClient.getInstances(registration.getServiceId()) ;
return SentinelResponseEntity.fromResult(0,"Hello,"+who+",Test OK,"+new Date()+"n"+
instance.stream().map(i->i.getUri()+"/"+i.getServiceId()).
collect(Collectors.joining(","))
);
}
}
最后在启动类加上@EnableDiscoveryClient即可。
消费者 依赖因为要用feignclient和负载平衡,所以要引入pom依赖:
配置文件com.alibaba.cloud >spring-cloud-starter-alibaba-nacos-discoveryorg.springframework.cloud >spring-cloud-starter-loadbalancerorg.springframework.cloud >spring-cloud-starter-openfeign
和服务端差不多,spring.application.name要改为自己的名称,spring.cloud.nacos.discovery.service要改为要引用的服务的名称,如果要引用多个微服务,用逗号分隔,如 s e r v i c e s . a c c o u n t − s e r v i c e , {services.account-service}, services.account−service,{services.order-service}
配置类先写配置类,声明一个有负载平衡功能的resttemplate,并实现无论用ribbon还是feign来调用微服务都能进行调用拦截(方便加入token之类的额外信息)
@Configuration public class OrderServiceConfig implements ApplicationListener拦截{ @Bean @LoadBalanced public RestTemplate restTemplate(){ RestTemplate restTemplate=new RestTemplate(); return restTemplate; } @Bean public TokenRequestInterceptor tokenRequestInterceptor(){ return new TokenRequestInterceptor(); } @PostConstruct public void init(){ //System.out.println(service); } @Override public void onApplicationEvent(ApplicationReadyEvent event) { 扫描所有ribbon实现并加入拦截 event.getApplicationContext().getBeansOfType(RestTemplate.class).entrySet().forEach(e->{ e.getValue().getInterceptors().add(new ClientHttpRequestTokenInterceptor()); }); } }
下面是ribbon的拦截,模拟在header里面加入token
@Slf4j
public class ClientHttpRequestTokenInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
log.info("===request: {}", request.getURI());
request.getHeaders().set("token", "abc123");
return execution.execute(request, body);
}
}
下面是feign请求的全局拦截
@Slf4j
public class TokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
log.info("{}",template.header("token","abc123"));
}
}
ribbon方式调用
先确保服务方至少有一实例在运行
@Resource
private RestTemplate restTemplate;
@Value("${services.account-service}")
private String serviceId;
@Test
public void remoteCall(){
String who="陈大文";
String url = "http://"+serviceId + "/test/test?who=" + who;
String result = restTemplate.getForObject(url, String.class);
System.out.println("Invoke : " + url + ", return : " + result);
}
执行结果显示,拦截器有输出具体调用的url,调用也正常:
2022-04-20 09:03:39.765 INFO 21340 --- [ main] .f.s.o.ClientHttpRequestTokenInterceptor : ===request: http://128.30.202.25:2000/test/test?who=%E9%99%88%E5%A4%A7%E6%96%87
Invoke : http://easy-cloud-account-service/test/test?who=陈大文, return : {errCode=0, result=Hello,陈大文,Test OK,Wed Apr 20 09:03:50 CST 2022
http://128.30.202.25:2000/easy-cloud-account-service, id=0}
feign方式调用
api实现
建一个Feign的实现接口,因为格式要严格与服务端一致所以直接继承好了,加上consumer的注解即可使用:
@Service
@FeignClient(value = "${services.account-service}/test")
public interface TestService extends com.freestyle.easyspringcloud.api.service.TestService {
}
调用
@Resource
private TestService testService;
@Test
public void testFeign(){
Object entity= testService.test("陈大文");
System.out.println(entity);
}
执行结果:
2022-04-20 09:10:24.446 INFO 31108 --- [ main] c.f.s.order.TokenRequestInterceptor : GET /test?who=%E9%99%88%E5%A4%A7%E6%96%87 HTTP/1.1
token: abc123
Binary data
{errCode=0, result=Hello,陈大文,Test OK,Wed Apr 20 09:10:24 CST 2022
http://128.30.202.25:2000/easy-cloud-account-service, id=0}
可见,feign的拦截中的RequestTemplate并不能显示具体调用哪个实例的api,如果有需要,则自己配置feign Client,因为l默认的FeignBlockingLoadBalancerClient.java:88在execute时才确定用哪个实例:
ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest); org.springframework.cloud.client.loadbalancer.ResponselbResponse = new DefaultResponse( instance); ....



