在不同的服务之间进行通信,需要服务的注册中心,便于服务之间的通信,中介者模式
Nacos是一个应用于服务注册与发现、配置管理的平台。基于SpringBoot写的程序
官网地址: https://nacos.io/zh-cn/docs/quick-start.html
市面上常用注册中心:
Zookeeper(搭建集群设计雅虎Apache), Eureka(Netfix不更新), Consul(Google)
Nacos(Alibaba开源双十一 活跃度,稳定可靠,简单易用,功能,性能,学习成本)
下载与安装
前提条件:确保你电脑已配置JAVA_HOME环境变量(Nacos启动时需要),MySQL版本为5.7以上(MariaDB10.5以上)
下载地址:https://github.com/alibaba/nacos/releases下载解压
导入Nacos需要的配置数据
解压文件中的conf目录的sql脚本没有创建数据库和字符编码的语句需要添加
DROP DATABASE IF EXISTS `nacos_config`; CREATE DATABASE `nacos_config` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; USE `nacos_config`;
再在小黑窗口执行Sql文件即可;
source d:/nacos-mysql.sql
常见异常
Nacos中配置连接数据库
打开/nacos/conf/application.properties里打开默认配置,配置要连接的数据库
### 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
启动与访问
默认条件下是集群方式启动,开发阶段没有必要集群启动,浪费内存
在解压文件的目录中打开小黑窗口,输入如下命令:
当然也可以使用Idea启动nacosidea启动sentinel,mysql,nacos常用配置_闪耀太阳de超级piracy的博客-CSDN博客startup.cmd -m standalone
打开浏览器,输入http://localhost:8848/nacos
用户名:nacos 密码:nacos
discovery该依赖会对web依赖服务进行监听,当监听到服务启动的时,discovery依赖就会对web依赖发起请求,进行服务的注册; discovery依赖每隔5s通过任务调度ScheduledExecutorService向nacos服务发送BeatInfo进行服务监听,15s监听不到BeatInfo包健康实例数就会显示为0,30s监听不到BeatInfo包服务就会消失
创建服务提供者工程sca-provider继承parent工程(01-sca),其pom.xml文件内容如下:
01-sca com.jt 1.0-SNAPSHOT 4.0.0 sca-provider org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 创建并修改配置文件application.yml(或者application.properties),实现服务注册:
注意:服务名不要使用下划线(“_”),应使用横杠(“-”),这是规则。server: port: 8081 spring: application: name: sca-provider #进行服务注册必须配置服务名 cloud: nacos: discovery: server-addr: localhost:8848
创建启动类并启动
前提条件是mysql,nacos都需要启动@SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); } }
在sca-provider项目中创建服务提供方对象,基于此对象对外提供服务
@RestController public class ProviderController { /** * 基于@Value注解告诉springboot,读取配置文件(bootstrap.yml)中 * 的server.port的值,假如读取不到则给一个默认值8080. */ @Value("${server.port:8080}") private String server; @GetMapping("/provider/echo/{msg}") public String doRestEcho1(@PathVariable("msg") String msg){ //return server+" say hello "+msg; //假如不想执行字符串的拼接,可以采用如下方式 return String.format("%s say hello %s",server,msg); //如上语句中的s%为一个字符串占位符,server,msg的值最后会替换s% } }
创建服务消费者工程(sca-consumer),继承parent工程(01-sca),其pom.xml文件内容如下
01-sca com.jt 1.0-SNAPSHOT 4.0.0 sca-consumer org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery 创建sca-consumer服务中的配置文件application.yml
server: port: 8090 spring: application: name: sca-consumer #服务注册时,服务名必须配置 cloud: nacos: discovery: server-addr: localhost:8848 #从哪里去查找服务
创建消费端启动类并实现服务消费
且添加如下方法,用于创建RestTemplate对象.(模板方法模式)@SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class,args); } /** * 创建RestTemplate对象,可以基于此对象进行远端服务调用, * 此对象封装了远程服务调用的协议和相关方法. */ @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
定义sca-consumer服务的消费端Controller,在此对象方法内部实现远端服务调用
@RestController public class ConsumerController { //IOC-(资源整合-坐享其成)->Spring框架的目标(资源整合) //DI-依赖注入(属性注入,构造注入,set注入):注入之前要查找(依赖查找-DL) @Autowired //属性注入 (但是属性不能使用final修饰) private RestTemplate restTemplate; //@Autowired //推荐构造注入 (假如只有一个构造方法autowired也可以省略) //public ConsumerController(RestTemplate restTemplate){ // this.restTemplate=restTemplate; //} @Value("${server.port:8080}") private String serverPort; @GetMapping("/consumer/doRestEcho1") public String doRestEcho01(){ String url="http://localhost:8081/provider/echo/{msg}"; //基于RestTemplate借助http协议向远端服务发起Get请求 //getForObject远端请求类型 //url远端资源Url //String.class远端服务返回值类型 //serverPort调用远端服务需要传递的参数 return restTemplate.getForObject(url, String.class,serverPort); } }
LoadBalancerClien负载均衡
ConsumerController类,注入LoadBalancerClient接口,其实现类是RibbonLoadBalancerClient对象,负载均衡实现默认是Nacos集成了Ribbon来实现的,并添加doRestEcho2方法,然后进行服务访问.
@RestController public class ConsumerController { @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/consumer/doRestEcho2") public String doRestEcho02(){ //1)基于服务名(这个名字是nacos注册中心中的一个服务名)获取服务实例 ServiceInstance serviceInstance = loadBalancerClient.choose("sca-provider"); //2)基于服务实例构建远端服务url String url="http://%s:%s/provider/echo/{msg}"; String ip=serviceInstance.getHost(); int port=serviceInstance.getPort(); url=String.format(url,ip,port); //3)进行远端服务调用 return restTemplate.getForObject(url, String.class,serverPort); } }
@LoadBalanced
当使用 @LoadBalanced注解描述RestTemplate对象时,RestTemplate在发送请求的时候会被LoadBalancerInterceptor拦截,它的作用就是用于RestTemplate的负载均衡,拦截到请求url后,会基于请求url中的服务名获取具体的服务实例,然后基于服务实例重新构建新的url,再基于新的url进行服务调用.
@Bean @LoadBalanced public RestTemplate loadBalancedRestTemplate(){ return new RestTemplate(); }
@Autowired private RestTemplate loadBalancedRestTemplate; @GetMapping("/consumer/doRestEcho3") public String doRestEcho03(){ String url=String.format("http://%s/provider/echo/{msg}","sca-provider"); return loadBalancedRestTemplate.getForObject(url, String.class,serverPort); } }
Feign 是一种声明式Web服务客户端,底层封装了对Rest技术的应用,通过Feign可以简化服务消费方对远程服务提供方法的调用实现;Feign 最早是由 Netflix 公司进行维护的,后来 Netflix 不再对其进行维护,现有Spring cloud维护,更名为 OpenFeign。
添加openfeign依赖(负载均衡实现默认是Nacos集成了Ribbon来实现的)
org.springframework.cloud spring-cloud-starter-openfeign 启动类上添加@EnableFeignClients注解
@EnableFeignClients 用于描述启动类或配置类,项目在启动时,就会启动一个FeignStarter组件,这个组件会对项目中使用了@FeignClient注解描述的接口进行代理对象的创建.@EnableFeignClients @SpringBootApplication public class ConsumerApplication {…}
Consumer服务通过Service层进行服务调用
@FeignClient 注解描述的接口为[远程服务]调用接口,
此接口不需要写实现类,由底层创建接口实现类,属于JDK代理,直接生产字节码文件,对象会交给spring管理,此对象默认的bean名称默认使用name或value属性的值,推荐基于contextId手动指定一个Bean名字@FeignClient(name="sca-provider", contextId = "remoteProviderService", fallbackFactory = ProviderFallbackFactory.class) public interface RemoteProviderService {//明修栈道暗度陈仓 //Rest风格:通过URI定义资源,通过请求动作操作资源 @GetMapping("/provider/echo/{string}") String echoMessage(@PathVariable("string") String msg); }
创建controller层进行服务调用
@RestController @RequestMapping("/consumer/ ") public class FeignConsumerController { @Autowired private RemoteProviderService remoteProviderService; @GetMapping("/echo/{msg}") public String doFeignEcho(@PathVariable String msg){ return remoteProviderService.echoMessage(msg); } }
进行远程服务调用时,假如调用的服务突然不可用了或者调用过程超,需要进行异常的处理
创建异常处理类@Slf4j @Component public class ProviderFallbackFactory implements FallbackFactory
{ //创建日志对象 //private static final Logger log= //LoggerFactory.getLogger(ProviderFallbackFactory.class); @Override public RemoteProviderService create(Throwable throwable) { /** return new RemoteProviderService() { @Override public String echoMessage(String msg) { //...报警...(发短信,email,日志.....) return "服务暂时不可用,稍等片刻再访问"; } }; */ return msg -> {//lambda表达式 (JDK8中的语法) //...报警...(发短信,email,日志.....) log.error("error: {} ","sca-provider服务调用失败"); return "服务暂时不可用,稍等片刻再访问"; }; } } 配置文件application.yml中添加如下配置,启动feign方式调用时的服务中断处理机制
feign: hystrix: enabled: true #默认值为false logging: file: name: consumer.log #将日志记录到文件
Consumer服务通过Service层进行服务调用
@FeignClient(name = "sca-provider", contextId = "remoteProviderService", fallbackFactory = ProviderFallbackFactory.class) public interface RemoteProviderService { @GetMapping("/provider/echo/{msg}") public String echoMsg(@PathVariable String msg); }
方式一:在配置类/启动类中
/** 假如默认负载均衡策略不能满足我们需求 * 具体方案:构建IRule接口实现类对象, * 并将此对象交给spring管理即可. * 这里以随机负载均衡策略为例进行实践.*/ @Bean public IRule rule(){ return new RandomRule(); }
方式二:配置文件中,将来可以写到配置中心
#直接在配置文件中的配置,其优势是,可以将配置提取到配置中心 sca-provider: #基于服务名指定负载均衡策略 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule