下载 nacos
下载后就是一个压缩包
启动nacos
命令
startup.cmd -m standalone
linux 命令是
./startup.sh -m standalone
nacos
是进行服务的注册管理
比如想要编写一个 注册服务
需要将当前的服务 进行注册
注册之后 可以在nacos
里进行管理
服务的注册
在xml
文件里面继承一个父工程
<parent>
<artifactId>01-sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
创建服务
<dependencies>
<!--
创建服务 必须注册服务
注册服务 前提是 web 服务
-->
<!--web 服务 默认有tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
服务的注册和发现
<dependencies>
<!-- 服务的注册和发现
添加完成后 启动时 会向nacos 服务发送一些 心跳包 进行服务注册
添加了这个 依赖之后 表示 此项目是nacos 服务的一个客户端对象
-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
在resources
文件夹里创建application.yml
文件
# 配置服务的端口
server:
port: 8081
# 服务名 一般是自己的项目名称
spring:
application:
name: sca-provider
# 服务的注册地址 服务启动时向这个 地址返送 包
cloud:
nacos:
discovery:
server-addr: localhost:8848
创建启动类
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
服务的调用
编写controller
类
@RestController //相当于Controller注解和ResponseBody注解
public class ProviderController {
@Value("${server.port:8080}") //在参数后面加上参数表示 默认值
// 如果 server.port 没有获取到 将使用默认值
private String server;
@GetMapping("/provider/echo/{msg}")
// 在PathVariable注解里面的参数是为了 老版本的维护
// 代表 {msg} 传来的参数
public String doGetDemo(@PathVariable("msg") String msg){
return "say hello "+msg+" 端口 "+server;
}
}
创建其他工程用来进行调用
添加web和服务注册依赖
添加application.yml
远端服务调用
编写启动类
@SpringBootApplication
/* 启动类 本身就是一个 配置类 其他类需要调用 直接在此处注入 */
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
/**
* RestTemplate 是由spring 提供的远程 服务的调用
*
* 例如 sca-consumer 调用sca-provider
* */
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
编写controller
类
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 访问 http://localhost:8090/consumer/doRestDemo
*
* 内部 基于 RestTemplate 进行远端服务调用
* */
@GetMapping("/consumer/dRestDemo")
public String doRestDemo(){
// 远程调用的 请求地址
String url = "http://localhost:8081/provider/echo/狗蛋";
// 1.请求地址 2.响应返回的数据类型 3.参数
return restTemplate.getForObject(url, String.class);
}
}
当用户访问/consumer/dRestDemo
时
内部进行远程调用http://localhost:8081/provider/echo/狗蛋
并进行响应返回
负载均衡
如果请求的实例只有一个 但是
请求地址有多个
实现了负载均衡
spring-cloud
提供了LoadBalancerClient
对象
此对象可以获取 nacos
中的服务实例
/**
* 此对象可以获取 nacos 中的服务实例
* 通过 负载均衡算法 从所有实例中取其中一个
* */
@Autowired // 负载均衡 客户端 对象 spring cloud提供
private LoadBalancerClient loadBalancerClient;
// 动态获取 当前实例名称
@Value("${spring.application.name:狗蛋}")
private String appName;
@GetMapping("/consumer/dRestDemo2")
public String doRestDemo2(){
// 选择服务 ID 从nacos 获取服务实例 内置了负载均衡
ServiceInstance instance = loadBalancerClient.choose("sca-provider");
// 获取IP 地址
String ip = instance.getHost();
// 获取端口号
int port = instance.getPort();
// 使用了负载均衡 地址 需要动态变化
String url = "http://"+ip+":"+port+"/provider/echo/"+appName;
// 上面的字符串写法 太生硬 使用 string 提供的一个api
// %s 代表一个占位符 字符串类型 %d代表数组
String url2 = String.format("http://%s:%s/provider/echo/%s",ip,port,appName);
// 1.请求地址 2.响应返回的数据类型 3.参数
return restTemplate.getForObject(url, String.class);
}
方法二@LoadBalanced
使用@LoadBalanced
注解
描述 RestTemplate
对象
系统底层会基于 RestTemplate
进行 远程服务调用 会被一个拦截器拦截 进行功能增强
增强指 基于 loadBalancerClient
对象进行服务实例获取 并进行功能添加
服务实例获取过程 底层采用 负载均衡
/**
* 使用@LoadBalanced
* 描述 RestTemplate 对象
* 系统底层会基于 RestTemplate 进行 远程服务调用 会被一个拦截器拦截 进行功能增强
* 拦截器 (Interceptor)
* 增强指 基于loadBalancerClient对象进行服务实例获取 并进行功能添加
* 服务实例获取过程 底层采用 负载均衡
* */
@Bean
@LoadBalanced
public RestTemplate loadBalancedRestTemplate(){
return new RestTemplate();
}
使用
进行注入
/**
* 存入在Spring 管理容器里是一个 方法名 所以这里
* 遇到相同的 实例对象 会查找 方法名
* */
@Autowired
private RestTemplate loadBalancedRestTemplate;
@GetMapping("/consumer/dRestDemo3")
public String doRestDemo3(){
// 地址直接写 项目名称 sca-provider
// 增强工具直接会通过 名称进行查找
String url = String.format("http://sca-provider/provider/echo/%s",appName);
// 1.请求地址 2.响应返回的数据类型 3.参数
return loadBalancedRestTemplate.getForObject(url, String.class);
}
底层默认的负载均衡策略是
轮询策略
RoundRobinRule
更改负载均衡策略
最简单的方式在配置类里添加方法
/**
* 更改默认的负载均衡策略
* 这种配置方式不够灵活
* 项目上线之后 打包成jar包 再去调整不灵活
* */
@Bean
public IRule rule(){
return new RandomRule();
}
或者
更改application.yml
# 配置 sca-provider 服务的负载均衡配置 sca-provider是服务名
sca-provider:
ribbon: #ribbon 是一个负载均衡组件 是Ribbon 组件中提供了 IRule 接口及相关实现
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
总共的负载均衡方法
基于Feign
的远程服务调用
通过Feign可以 简化 服务 对远程服务的调用
添加feign
组件
进行远程服务调用
<!--添加feign组件 进行远程服务调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
编辑启动类
添加@EnableFeignClients
注解
/**
* 注解用于描述一些配置类
* 告诉系统 启动时为@FeignClient注解描述的接口创建实现了及对象
* 交给Spring管理
* */
@EnableFeignClients
@SpringBootApplication
/* 启动类 本身就是一个 配置类 其他类需要调用 直接在此处注入 */
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
创建一个抽象类
/**
* 用于描述远程服务的接口
* value 两层意思
* 1. 调用的远程服务名
* 2. 当前spring 管理bean的名字key
* */
@FeignClient("sca-provider") // 声明调用哪一个服务 sca-provider服务
public interface RemoteProviderService {
@GetMapping("/provider/echo/{msg}")// 声明调用的哪一个方法/接口
String echoMessage(@PathVariable("msg") String msg);
// @PathVariable("msg") 定义接收参数是 msg 必须写
}
使用
@RestController
@RequestMapping("/consumer")
public class FeignConsumerController {
@Autowired
private RemoteProviderService remoteProviderService;
@GetMapping("/echo/{msg}")
public String doFeignEcho(@PathVariable String msg){
//远程服务调用
return remoteProviderService.echoMessage(msg);
}
}
如果有多个抽象类同时 调用一个 服务
在FeignClient
注解里面添加contextId
属性
@FeignClient(name="sca-provider",contextId="remoteProviderService")
//sca-provider为服务提供者名称
interface RemoteProviderService{
@GetMapping("/provider/echo/{string}")//前提是远端需要有这个服务
public String echoMessage(@PathVariable("string") String string);
}
远程服务未响应
请求过程中会出现异常或超时
应该返回一个备用方案
设置一个ProviderFallbackFactory
类
实现FallbackFactory
接口并添加泛型处理对象的泛型
/**
* 基于此对象处理
* RemoteProviderService 接口调用时出现的服务中断,超时等问题
*/
@Component
public class ProviderFallbackFactory implements FallbackFactory<RemoteProviderService> {
/**
* 此方法会在 RemoteProviderService 接口服务调用时,出现了异常后执行.
* 用于接收异常
*/
@Override
public RemoteProviderService create(Throwable throwable) {
return new RemoteProviderService() {
@Override
public String echoMessage(String msg) {
return "请求超时 过后访问";
}
};
}
}
并配置yml
文件
开启熔断机制
启动备选方案
# 开启熔断机制 启动备选方案
feign:
hystrix:
enabled: true
当远程服务超时将会访问备选方案
服务的配置中心
除了代码之外 软件还有一些配置信息
比如数据库的用户名和密码
不停机就可以动态刷新服务内部的配置项
Nacos
配置入门
演示日志
运行程序时进行日志输出
创建Controller
对象 如
@RestController //交给 spring 进行管理
public class ProviderLogController {
// 使用 org.slf4j.Logger Java中的日志API规范
// log对象在哪个类中创建,getLogger方法中的就传入哪个类的字节码对象
// 使用 org.slf4j.LoggerFactory 提供的方法
// 可以传入字节码对象 或者 包名类名
// private static final Logger log= LoggerFactory.getLogger("com.jt.provider.controller.ProviderLogController");
private static final Logger log= LoggerFactory.getLogger(ProviderLogController.class);
// http://localhost:8081/provider/log/doLog01
@GetMapping("/provider/log/doLog01")
public String doLog01(){
System.out.println("=== sys提供的打印 ===");
log.trace("=== log for trace ===");
log.debug("=== log for debug ===");
log.info("=== log for info ===");
log.warn("=== log for warn ===");
log.error("=== log for error ===");
return "test log 01";
}
}
访问之后会打印
=== sys提供的打印 ===
2021-12-23 10:38:05.570 INFO 16072 --- [nio-8081-exec-1] c.j.p.controller.ProviderLogController : === log for info ===
2021-12-23 10:38:05.571 WARN 16072 --- [nio-8081-exec-1] c.j.p.controller.ProviderLogController : === log for warn ===
2021-12-23 10:38:05.571 ERROR 16072 --- [nio-8081-exec-1] c.j.p.controller.ProviderLogController : === log for error ===
打印顺序trace < debug < info < warn < error
在yml
文件配置
# 更换日志输出
logging:
level:
com.jt: debug
Nacos
配置中心
导入xml
依赖
添加配置服务依赖
<!-- 服务的配置 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
更新配置
在resources
下更新yml
文件
# 配置服务的端口
server:
port: 8081
# 服务名 一般是自己的项目名称
spring:
application:
name: sca-provider
# 服务的注册地址 服务启动时向这个 地址返送 包
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
在nacos
配置中心新建配置
将resources
文件里的yml
文件更改为
bootstrap.yml
如果不进行更改 将不生效
只需要更改Nacos
配置中心的文件项目也会进行更改
无需重新运行
自己所写 的配置文件权限最大
Nacos
配置管理模型
Nacos
配置管理模型是由三部分组成
-
Namespace
命名空间 对不同环境进行隔离
如: 开发环境和生产环境 -
Group
分组,将若干个服务或若干个配置归为一组 -
service/Datald
某一个服务或配置集, 一般对应一个配置文件
命名空间设置
在Nacos
中命名空间里面新建命名空间
输入别名和描述之后
会提供一个命名空间的ID
将之前的配置添加到新的命名空间
之后配置文件里进行设置
# 服务名 一般是自己的项目名称
spring:
application:
name: sca-provider
# 服务的注册地址 服务启动时向这个 地址返送 包
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848 #从这个地址 查找配置中心 的配置文件
file-extension: yml
namespace: 511075eb-b633-41a9-a0fe-669fee9bb83c #默认是 public 进行更改命名空间
将自动读取新的命名空间的配置
分组设置
一个服务在不同时间节点切换不同的配置
使用到分组设计实现
在Nacos
配置管理里
新建配置时 重新写一个Group
分组
在Java
配置文件里面
config:
server-addr: localhost:8848 #从这个地址 查找配置中心 的配置文件
file-extension: yml
namespace: 511075eb-b633-41a9-a0fe-669fee9bb83c #默认是 public 进行更改命名空间
group: DEFAULT_GROUP_51 # 配置分组默认是 DEFAULT_GROUP
在Controller
类中读取配置
@RefreshScope
@RestController
public class ProviderCacheController {
@Value("${goudan:false}")
private boolean goudan;
@GetMapping("/provider/cache01")
public String doCache01(){
return "读取配置文件内容是 "+goudan;
}
}
共享配置
多个配置文件中都有相同配置时
可以将这些配置进行提取
在nacos
创建共享配置
配置文件yml
文件
# 公共配置
shared-configs[0]:
data-id: app-public.yml # 公共配置文件名称
refresh: true # 开启动态更新
读取公共配置
@RefreshScope
@RestController
public class ProviderSecretController {
@Value("${app.ser:7777}")
private String ser;
@GetMapping("/provider/doGetSec")
public String doGetSec(){
return "this is num "+ser;
}
}