赞
踩
nacos是一个服务注册于发现的平台 它可以做到将我们的服务展示出来 只需要去访问我们的nacos就可以浏览所有的服务,那它和我们的网关有什么关系,网关主要是提供给用户一个访问的接口,所有的用户通过网关访问,而nacos主要是提供给我们的所有服务其他服务的信息,包括给网关提供我们需要到达服务的信息。

nacos的简单使用:官网地址
https://nacos.io/zh-cn/docs/quick-start.html
官网推荐下载稳定的2.03
下载好我们的nacos包过后 将他上传到linux服务器上
执行命令解压tar -xvf nacos-server-$version.tar.gz
进入解压好的nacos的bin目录下
启动:

启动成功:可以到最下面那一行的推荐目录下观察它的日志输出:
根据网址进入即可:

版本对应文档:
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
同时如果是使用的nacos是2.0以上版本 还要打开9848 9849端口
版本一定要用好

我使用的是2.0.3版本的nacos,父类的在pom文件中添加这个:
<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba-version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring-boot.version}</version> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency> </dependencies>
接下来是服务提供者的pom文件: 记得把服务提供者配置为父类的子文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
接下来是服务的yml文件的,其实服务消费者和服务提供者 在我们的nacos中并没有什么不同,都是同样的一组组件 ,只是当我们的某个组件需要调用分布式中其他项目的时候 我们会把被调用的项目称作服务的提供者,另一个叫做服务消费者
server: port: 9001 spring: application: name: nacos-provider cloud: nacos: server-addr: 47.108.140.135:8848 management: endpoint: web: exposure: include: '*' ==================================服务消费者的======================================== server: port: 8080 spring: application: name: nacos-consumer cloud: nacos: server-addr: 47.108.140.135:8848 #消费者将要去访问的微服务名称 这个没有什么大用 就是方便用@Value拿而已 不用写直接字符串也可 #就是配置文件和代码的分离 #我们的服务注册中心会把 应用程序名和它的ip联系起来 service-url: nacos-user-service: http://nacos-provider
设计到服务的消费就需要设计到远程方法调用:
在消费者的主程序中添加这一个 好方便我们调用其他服务 ribbon为我们提供了负载均衡
@Bean
@LoadBalanced //开启负载均衡(利用hash码的均匀分布 达成负载均衡)
public RestTemplate restTemplate(){
return new RestTemplate();
}
远程方法调用:
提供者的 @Value("${server.port}") private String port; @GetMapping("/serverport") public String getServerPort(){ return "hello nacos discovery "+port; } ============================================================================ 消费者的 @Value("${service-url.nacos-user-service}") private String url; @Autowired private RestTemplate restTemplate; @GetMapping("/consumer/nacos") public String getDiscovery(){ //第一个参数是访问的地址 第二个是返回值的类型 如果返回值类型是 list 要写成arr数组类型 //第三个参数是传给url的动态参数 使用该参数需要 在url中加上 {1}{2}{3}占位符 return restTemplate.getForObject(url+"/serverport",String.class); }
接下来可以执行方法 可以看出 我们的调用结果是不同的,证明我们的ribbon确实办到了负载均衡(复杂的我没有测试,就是权重 每个服务器性能不同我们设置不同权重 让性能差的分的请求少一些)


nacos的核心功能:
接下来我们来了解一下nacos服务是如何运行起来的,下载源码的过程网上都可以找到:
我们来看一看nacos给我们留下的naming测试类:
@Test public void testServiceList() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "www.gsxa.top:8848");//nacos的连接信息 properties.put(PropertyKeyConst.USERNAME, "nacos"); properties.put(PropertyKeyConst.PASSWORD, "nacos"); properties.put(CommonParams.NAMING_REQUEST_TIMEOUT,20); Instance instance = new Instance();//实例信息 将自身的信息发送给nacos服务器 instance.setIp("1.1.1.1"); instance.setPort(800); instance.setWeight(2); Map<String, String> map = new HashMap<String, String>(); map.put("netType", "external"); map.put("version", "2.0"); instance.setMetadata(map); NamingService namingService = NacosFactory.createNamingService(properties); namingService.registerInstance("nacos.test.1", instance); ThreadUtils.sleep(5000L); List<Instance> list = namingService.getAllInstances("nacos.test.1"); System.out.println(list); }
首先 它创建了两种信息 1. 连接信息 即properties属性 里面主要存储了 与nacos服务端连接需要什么信息。 2. Instance 实例信息 就是自生服务的信息元数据传送给服务器 然他了解 我们直接看 registerInstance方法。
@Override public void registerInstance(String serviceName, Instance instance) throws NacosException { registerInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { NamingUtils.checkInstanceIsLegal(instance);//检查心跳时间是否设置有问题 clientProxy.registerService(serviceName, groupName, instance); } ======================================================================================== public static void checkInstanceIsLegal(Instance instance) throws NacosException { //这几个属性都是Instance的默认设置的值 比如发送心跳时间5s 每5s向nacos服务端发送心跳信息 证明自己还活着 15s心跳异常时间 如果15s没有发送信息 那么该实例将被标记为异常 30s删除服务 如果30s没有发送信息 nacos服务端会直接将这个服务从他的列表中删除 //查看一下心跳的间隔发送时间 是否大于了心跳的销毁时间 或者 心跳发生异常的时间 如果超过了 证明你设置出了问题 if (instance.getInstanceHeartBeatTimeOut() < instance.getInstanceHeartBeatInterval() || instance.getIpDeleteTimeout() < instance.getInstanceHeartBeatInterval()) { throw new NacosException(NacosException.INVALID_PARAM, "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'."); } } ======================================================================================= 上面检测完过后调用这个方法 去注册服务 先查看 getExecuteClientProxy @Override public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { getExecuteClientProxy(instance).registerService(serviceName, groupName, instance); } //使用那种请求去发送 http 还是 grpc private NamingClientProxy getExecuteClientProxy(Instance instance) { //用grpc 还是 http协议来发送请求 如果是瞬时对象就会用grpc //grpc 是rpc同学的一个实现 2.0版本默认使用grpc return instance.isEphemeral() ? grpcClientProxy : httpClientProxy; } instance中的属性 默认为true 我们在nacos的官方文档中也可以看到 默认用grpc发送连接请求等信息 上面有图片 /** * If instance is ephemeral. * * @since 1.0.0 */ private boolean ephemeral = true; ======================================================================================== 所以我们直接调用grpcClientProxy的 @Override public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName, instance);//日志信息 InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.REGISTER_INSTANCE, instance);//创建实例请求对象 requestToServer(request, Response.class);//向服务端发送请求 namingGrpcConnectionEventListener.cacheInstanceForRedo(serviceName, groupName, instance);//缓存 为了下次再次发送 } ========================================================================================= 对象创建没有什么好看的 直接去看 requestToServer查看发送请求到服务端的 private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass) throws NacosException { try { request.putAllHeader(getSecurityHeaders());//往请求头里面放数据 request.putAllHeader(getSpasHeaders( NamingUtils.getGroupedNameOptional(request.getServiceName(), request.getGroupName()))); Response response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); //如果请求超时时间为小于0 就使用默认的3s 如果不是 就使用我们自己规定的 //其实求实发送请求 获取响应对象信息 if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {//请求失败 抛异常 throw new NacosException(response.getErrorCode(), response.getMessage()); } if (responseClass.isAssignableFrom(response.getClass())) { return (T) response; } NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'", response.getClass().getName(), responseClass.getName()); } catch (Exception e) { throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e); } throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response"); } ====================================================================================== 没有什么好看的 其实就是发送请求 获取响应 我删除了一些 太多trycache了 public Response request(Request request, long timeoutMills) throws NacosException { int retryTimes = 0;//重试次数 默认为3次 Response response = null; Exception exceptionThrow = null; long start = System.currentTimeMillis();//计时器 防止超时 while (retryTimes < RETRY_TIMES && System.currentTimeMillis() < timeoutMills + start) {//如果没有超时 那么就反复重试 boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { waitReconnect = true; throw new NacosException(NacosException.CLIENT_DISCONNECT, "Client not connected,current status:" + rpcClientStatus.get()); } response = this.currentConnection.request(request, timeoutMills);//通过当前连接发送请求 if (response == null) { throw new NacosException(SERVER_ERROR, "Unknown Exception."); } if (response instanceof ErrorResponse) {//如果响应是错误的响应 if (response.getErrorCode() == NacosException.UN_REGISTER) { synchronized (this) { waitReconnect = true; if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { LoggerUtils.printIfErrorEnabled(LOGGER, "Connection is unregistered, switch server,connectionId={},request={}", currentConnection.getConnectionId(), request.getClass().getSimpleName()); switchServerAsync(); } } } throw new NacosException(response.getErrorCode(), response.getMessage()); } // return response. lastActiveTimeStamp = System.currentTimeMillis(); return response;//返回响应 } catch (Exception e) { if (waitReconnect) { try { //wait client to re connect. Thread.sleep(Math.min(100, timeoutMills / 3)); } catch (Exception exception) { //Do nothing. } } LoggerUtils.printIfErrorEnabled(LOGGER, "Send request fail, request={}, retryTimes={},errorMessage={}", request, retryTimes, e.getMessage()); exceptionThrow = e; } retryTimes++; } }
大致就是这么一个流程

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。