https://nacos.io/zh-cn/index.html
source 盘符:/nacos-mysql.sql
nacos_config
的数据库,会有多个表。conf/application.properties
文件,基于当前环境,修改对应的内容#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
./startup.sh -m standalone
startup.cmd -m standalone
http://localhost:8848/nacos
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
server:
port: 8081
spring:
application:
name: sca-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
package com.lofasp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author Sky-haohao
* @Date 2021/9/15 10:43
* @Version 1.0
*/
@Slf4j
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
package com.lofasp.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
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;
/**
* @Author Sky-haohao
* @Date 2021/9/17 14:07
* @Version 1.0
*/
/** - @RefreshScope 注解用于告诉spring,一旦配置中心数据发生变化
* 自动重新创建它描述的类的实例*/
@Slf4j
@RefreshScope
@RestController
public class ProviderController {
// 内部类不能放static
public ProviderController() {
System.out.println("ProviderController()");
}
// 此属性会在对象构建时初始化
// 服务器启动时会将所有的配置信息(配置文件或配置中心) 读取到 Environment 对象
// @Value用于告诉spring从 Environment 对象中,读取配置信息
// 将读取到的配置内容赋值给对象的属性
@Value("${logging.level.com.lofasp:debug}")
private String logLevel;
@GetMapping("/provider/doGetLevel")
public String doGetLogLevel(){
//日志的输出会随着配置中心日志级别的更新进行调整
log.trace("==log.trace==");//跟踪
log.debug("==log.debug==");//调试
log.info("==log.info==");//常规信息
log.warn("==log.warn==");//警告
log.error("==log.error==");//错误信息
return "log level is : " + logLevel;
}
// @Value读取application.yml 中的配置
// 要读取的配置需要写在 ${} 这个表达式中
// ${} 表达式中 ":" 后面的内容是默认值,当没有读到server.port的值时,补充
@Value("${server.port:8080}")
private String server;
/**
* 基于此方法,实现一个字符串的回显
* echo 回显
* rest 属于一种软件架构编码风格,可以基于这种风格定义URL
* 访问http://localhost:8081/provider/echo/nacos
* @return
*/
@GetMapping("/provider/echo/{msg}")
public String doRestEcho1(@PathVariable("msg") String msg){
/*try {
System.out.println("开始睡觉!!!");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
// 需求:通过配置中心动态日志级别设置,控制日志信息的输出
// 常用日志级别:trace < debug < info < warn < error
// 很多系统的默认日志级别是info,调试程序通常会用debug
// {} 表示占位符,后面的内容会填充到占位符中,可以写多个
log.info("doRestEcho1 start:{}",System.currentTimeMillis());
return server + "say hello" + msg;
}
}
http://localhost:8081/provider/echo/sca
,进行访问<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
server:
port: 8090
spring:
application:
name: sca-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
package com.lofasp;
/**
* @Author Sky-haohao
* @Date 2021/9/15 14:17
* @Version 1.0
*/
/**
* 服务消费方对象的启动类
* 业务描述:
* 当客户端(浏览器,手机app) 向服务消费方发起请求时,
* 服务消费方调用服务提供方的api,进而获取服务提供方的数据
* 例如: 我们访问一个订单模块数据(例如我的订单),订单模块中还要呈现商品信息
*/
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
/**
* 构建RestTemplate对象,并将此对象将给spring管理
* 后续我们会通过此对象进行远程服务调用
* @return
*/
@Bean
public RestTemplate restTemplate(){
System.out.println("===restTemplate()===");
return new RestTemplate();
}
@RestController
public class ConsumerController{
@Value("${spring.application.name}")
private String appName;
@Autowired
private RestTemplate restTemplate;
//http://ip:port/consumer/doRestEcho1
@GetMapping("/consumer/doRestEcho1")
public String doRestEcho1(){
//调用服务提供方API(http://ip:port/path)
//1.定义要调用的API
String url = "http://localhost:8081/provider/echo/" + appName;
// System.out.println("request url:"+url);
//2.谁去访问这个API restTemplate
return restTemplate.getForObject(url, String.class);
}
}
http://localhost:8090/consumer/doRestEcho1
进行访问一个服务实例可以处理请求是有限的,假如服务实例的并发访问比较大,我们会启动多个服务实例,让这些服务实例采用一定策略均衡(轮询,权重,随机,hash等)的处理并发请求,在Nacos中服务的负载均衡(Nacos客户端负载均衡)是如何应用的?
修改ConsumerController类
/**
* 负载均衡客户端对象
*/
@Autowired
private LoadBalancerClient loadBalancerClient;
/**
* 负载均衡方式调用
* http://ip:port/consumer/doRestEcho2
* @return
*/
@GetMapping("/consumer/doRestEcho2")
public String doRestEcho2(){
//1.从注册中心获取服务实例
ServiceInstance instance = loadBalancerClient.choose("sca-provider");
//2.基于RestTemplate进行服务实例调用
String ip = instance.getHost(); //获取IP地址
int port = instance.getPort(); //获取端口号
//String url = "http://" + ip + ":" + port + "/provider/echo/" + appName;
//可变参数的写法
String url = String.format("http://%s:%s/provider/echo/%s", ip,port,appName);
return restTemplate.getForObject(url, String.class);
}
修改sca-provider的配置文件端口,分别以8081,8082端口方式进行启动。
server:
port: 8082
spring:
application:
name: sca-provider
cloud:
nacos:
server-addr: localhost:8848
启动sca-consumer项目模块,打开浏览器访问
这里多个实例并发提供服务的方式为负载均衡,这里的负载均衡实现默认是因为Nacos集成了Ribbon来实现的,Ribbon配合RestTemplate,可以非常容易的实现服务之间的访问。
Ribbon是Spring Cloud核心组件之一,它提供的最重要的功能就是客户端的负载均衡(客户端可以采用一定算法,例如轮询访问,访问服务端实例信息),这个功能可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡方式的服务调用。
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
return new RestTemplate();
}
@Autowired
private RestTemplate loadBalancedRestTemplate;
@GetMapping("/consumer/doRestEcho3")
public String doRestEcho03(){
String serviceId = "sca-provider";
// 定义一个url
String url = String.format("http://%s/provider/echo/%s",serviceId,appName);
// 服务的调用
return loadBalancedRestTemplate.getForObject(url, String.class);
}
public ClientHttpResponse intercept(final HttpRequest request,
final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
return this.loadBalancer.execute(serviceName,
requestFactory.createRequest(request, body, execution));
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {…}
package com.lofasp.service;
/**
* @Author Sky-haohao
* @Date 2021/9/16 10:46
* @Version 1.0
*/
import com.lofasp.factory.RemoteProviderFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 定义用于实现远程provider服务调用的service接口
* 其中:
* 1.@FeignClient用于描述远程服务调用接口
* 2.name = "sca-provider" 为 你要远程调用的服务名
* 3.contentId 为 当前 bean的名称,假如没有指定contentId,默认会
* 采用@FeignClient注解中的name属性指定的名字作为bean的名字
* 4.fallbackFactory 用于定义服务调用超时等现象发生时,一种应对措施或处理机制
*/
@FeignClient(name = "sca-provider",contextId = "remoteProviderService")
public interface RemoteProviderService {
@GetMapping("/provider/echo/{msg}") //前提是远端需要有这个服务
public String echoMessage(@PathVariable("msg") String msg);
}//consumer.controller-->Feign interface-->remote call
//起步依赖,自动配置-autoconfiguration,监控监控-actuator,嵌入式(WEB-tomcat)
package com.lofasp.controller;
import com.lofasp.service.RemoteProviderService;
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;
/**
* @Author Sky-haohao
* @Date 2021/9/16 10:55
* @Version 1.0
*/
@RestController
@RequestMapping("/consumer")
public class FeignConsumerController {
@Autowired
private RemoteProviderService remoteProviderService;
/**
* 执行远程服务调用
* 访问: browser -> consumer -> provider
* 1.browser
* http://localhost:8090/consumer/echo/cgb
* 2.consumer
* http://sca-provider/provider/echo/cgb
*/
@GetMapping("/echo/{msg}")
public String doRestEcho(@PathVariable("msg") String msg){
return remoteProviderService.echoMessage(msg);
}
}
package com.lofasp.service;
import org.springframework.cloud.openfeign.FeignClient;
/**
* @Author Sky-haohao
* @Date 2021/9/16 14:12
* @Version 1.0
*/
@FeignClient(name = "sca-provider")
public interface RemoteOtherService {
}
The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
@FeignClient(name = "sca-provider",contextId = "remoteOtherService")
public interface RemoteOtherService {
}
package com.lofasp.factory;
import com.lofasp.service.RemoteProviderService;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @Author Sky-haohao
* @Date 2021/9/16 14:31
* @Version 1.0
*/
// @Slf4j
@Component
public class RemoteProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
/** 如果不定义该 Logger 对象,可以直接使用lombok提供的 @Slf4j 注解*/
private static final Logger log = LoggerFactory.getLogger(RemoteProviderFallbackFactory.class);
private static final int FAIL = 500;
@Override
public RemoteProviderService create(Throwable throwable) {
log.error("用户服务调用失败:{}", throwable.getMessage());
/* 初始写法
return new RemoteProviderService() {
@Override
public String echoMessage(String msg) {
//给运维人员发消息
return "服务忙,稍等片刻再访问,状态码"+ FAIL + ",信息:" + throwable.getMessage();
}
};*/
/** lambda 表达式的写法 */
return msg -> "服务忙,稍等片刻再访问,状态码"+ FAIL + ",信息:" + throwable.getMessage();
}
}
@FeignClient(name = "sca-provider",contextId = "remoteProviderService",fallbackFactory = RemoteProviderFallbackFactory.class)
feign:
hystrix:
enabled: true # 开启feign方式调用时,服务调用超时等问题