200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > SpringCloud从入门到精通——微服务注册中心

SpringCloud从入门到精通——微服务注册中心

时间:2022-02-18 07:50:57

相关推荐

SpringCloud从入门到精通——微服务注册中心

目录

一、远程调用概念引入1. 什么是远程调用2. 如何远程调用接口3. HttpClient使用调用天气预报接口3.1 HttpClient基本介绍3.2 Maven依赖3.3 HttpClient工具类3.4 测试调用天气预报接口3.5 提供天气预报接口二、什么是服务治理1.1 微服务框架中名词1.2 微服务框架中常用名词三、使用HttpClient实现RPC1. Maven依赖2. demo-member-producer2.1 MemberService2.2 AppMember:2.3 application.yml:3. demo-order-consumer3.1 OrderToMemberService3.2 HttpClientUtils3.3 AppOrder3.4 application.yml4. 访问四、常用注册中心五、Eureka服务注册中心1. Eureka基本介绍2. Eureka环境构建2.1 EurekaServer端2.1.1 Maven依赖2.1.2 配置文件2.1.3 启动类2.2 EurekaClient端2.2.1 Maven2.2.2 配置文件2.2.3 启动类2.3 服务发现2.3.1 Maven2.3.2 配置文件2.3.3 启动类2.2.4 接口2.2.5 工具类2.4 Eureka 常用配置解析3. 效果展示3.1 启动EurekaServer端3.2 启动EurekaClient端3.3 启动服务发现3.4 接口测试六、Nacos服务注册中心1. Nacos基本介绍1.1 什么是 Nacos2. NacosServer端环境搭建2.1 Nacos2.0启动常见错误3. 手动注册服务与发现4. NacosClient环境搭建4.1 Maven依赖4.2 配置文件4.3 接口4.4 启动项目5. NacosClient实现服务发现5.1 Maven依赖5.2 配置文件5.3 接口5.4 工具类5.5 启动类5.3 启动项目5.4 接口测试七、Resttemplate1. 接口2. 启动类八、本地负载均衡算法1. 轮询算法2. 随机算法3. 故障转移算法4. 权重算法

一、远程调用概念引入

1. 什么是远程调用

RPC是远程过程调用(Remote Procedure Call)的缩写形式。SAP系统RPC调用的原理其实很简单,有一些类似于三层构架的C/S系统,第三方的客户程序通过接口调用SAP内部的标准或自定义函数,获得函数返回的数据进行处理后显示或打印。

例如我们平台需要获取到天气预报,就可以直接调用中国天气预报接口。

接口名称:/weather_mini?city=北京

返回接口信息:

2. 如何远程调用接口

HttpClient org.apache.httpcomponentsOkHttp 对原生HttpURLConnection 封装(JDK本身自带)EasyHttp 基于OkHttp的网络请求框架

3. HttpClient使用调用天气预报接口

3.1 HttpClient基本介绍

实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)

支持 HTTPS 协议

支持代理服务器(Nginx等)等

支持自动(跳转)转向

3.2 Maven依赖

<dependencies><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.5</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.66</version></dependency></dependencies>

3.3 HttpClient工具类

package com.demo.utils;import mons.logging.Log;import mons.logging.LogFactory;import org.apache.http.HttpEntity;import org.apache.http.NameValuePair;import org.apache.http.ParseException;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** @Author: JYC* @Title: HttpClientUtils* @Description: TODO* @Date: /4/20 23:10*/public class HttpClientUtils {private static final CloseableHttpClient httpClient;public static final String CHARSET = "UTF-8";private static final Log log = LogFactory.getLog(HttpClientUtils.class);// 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象static {RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build();httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();}public static String doGet(String url, Map<String, String> params) {return doGet(url, params, CHARSET);}public static String doPost(String url, Map<String, String> params) throws IOException {return doPost(url, params, CHARSET);}/*** HTTP Get 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容*/public static String doGet(String url, Map<String, String> params, String charset) {try {if (params != null && !params.isEmpty()) {List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}// 将请求参数和url进行拼接url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));}HttpGet httpGet = new HttpGet(url);CloseableHttpResponse response = httpClient.execute(httpGet);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpGet.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);response.close();return result;} catch (Exception e) {log.error("请求服务器端出错:" + e);return null;}}/*** HTTP Post 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容* @throws IOException*/public static String doPost(String url, Map<String, String> params, String charset)throws IOException {List<NameValuePair> pairs = null;if (params != null && !params.isEmpty()) {pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}}HttpPost httpPost = new HttpPost(url);if (pairs != null && pairs.size() > 0) {httpPost.setEntity(new UrlEncodedFormEntity(pairs, CHARSET));}CloseableHttpResponse response = null;try {response = httpClient.execute(httpPost);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpPost.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);return result;} catch (ParseException e) {log.error("请求服务器端出错:" + e);return null;} finally {if (response != null)response.close();}}}

3.4 测试调用天气预报接口

public class HttpClientUtilsTest {public static void main(String[] args) {String url = "/weather_mini";HashMap<String, String> params = new HashMap<String, String>();params.put("city", "北京");String result = HttpClientUtils.doGet(url, params);System.out.println("result:" + result);}}

3.5 提供天气预报接口

package com.demo.controller;import com.alibaba.fastjson.JSONObject;import com.demo.utils.HttpClientUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;/*** @Author: JYC* @Title: WeatherController* @Description: TODO* @Date: /4/21 9:37*/@RestControllerpublic class WeatherController {@RequestMapping("/getWeather")public Object getWeather(String city) {String url = "/weather_mini";HashMap<String, String> params = new HashMap<String, String>();params.put("city", city);String jsonStr = HttpClientUtils.doGet(url, params);// 转化成jsonJSONObject jsonObject = JSONObject.parseObject(jsonStr);// 获取今天天气return jsonObject.getJSONObject("data").getJSONArray("forecast");}}

启动类:

package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author: JYC* @Title: App* @Description: TODO* @Date: /4/21 9:47*/@SpringBootApplicationpublic class App {public static void main(String[] args) {SpringApplication.run(App.class);}}

二、什么是服务治理

在RPC远程调用过程中,服务与服务之间依赖关系非常大,服务Url地址管理非常复杂,所以这时候需要对我们服务的url实现治理,通过服务治理可以实现服务注册与发现、负载均衡、容错等。

1.1 微服务框架中名词

生产者----提供接口

消费者----调用生产者提供的接口

1.2 微服务框架中常用名词

生产者:提供接口

消费者:调用生产者提供的接口

服务的注册:当我们服务启动时会将服务的ip和端口注册存放在注册中心上

容器:存放服务的接口IP和端口号码

服务的发现:消费者如果调用接口时根据服务名称去服务注册中心查找该对应服务接口地址,在本地实现RPC远程调用

三、使用HttpClient实现RPC

demo-order-consumer----订单服务 消费者 调用会员服务的接口

demo-member-producer—会员服务 生产者 提供接口

1. Maven依赖

<modules><module>demo-member-producer</module><module>demo-order-consumer</module></modules><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.4.1</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.5</version></dependency></dependencies>

2. demo-member-producer

2.1 MemberService

package com.demo.service;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** @Author: JYC* @Title: MemberService* @Description: TODO* @Date: /4/21 13:57*/@RestControllerpublic class MemberService {/*** 会员服务提供接口* @return*/@RequestMapping("/getMember")public String getMember() {return "我是会员服务接口";}}

2.2 AppMember:

package com.demo.service;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author: JYC* @Title: com.demo.service.AppMember* @Description: TODO* @Date: /4/21 13:59*/@SpringBootApplicationpublic class AppMember {public static void main(String[] args) {SpringApplication.run(AppMember.class);}}

2.3 application.yml:

server:port: 8080

3. demo-order-consumer

3.1 OrderToMemberService

package com.demo.service;import com.demo.utils.HttpClientUtils;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestControllerpublic class OrderToMemberService {/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {// HttpClient 工具类 实现RPC远程调用String memberUrl = "http://192.168.66.1:8080/getMember";return HttpClientUtils.doGet(memberUrl, null);}}

3.2 HttpClientUtils

package com.demo.utils;import mons.logging.Log;import mons.logging.LogFactory;import org.apache.http.HttpEntity;import org.apache.http.NameValuePair;import org.apache.http.ParseException;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Map;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/public class HttpClientUtils {private static final CloseableHttpClient httpClient;public static final String CHARSET = "UTF-8";private static final Log log = LogFactory.getLog(HttpClientUtils.class);// 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象static {RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build();httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();}public static String doGet(String url, Map<String, String> params) {return doGet(url, params, CHARSET);}public static String doPost(String url, Map<String, String> params) throws IOException {return doPost(url, params, CHARSET);}/*** HTTP Get 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容*/public static String doGet(String url, Map<String, String> params, String charset) {try {if (params != null && !params.isEmpty()) {List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}// 将请求参数和url进行拼接url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));}HttpGet httpGet = new HttpGet(url);CloseableHttpResponse response = httpClient.execute(httpGet);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpGet.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);response.close();return result;} catch (Exception e) {log.error("请求服务器端出错:" + e);return null;}}/*** HTTP Post 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容* @throws IOException*/public static String doPost(String url, Map<String, String> params, String charset)throws IOException {List<NameValuePair> pairs = null;if (params != null && !params.isEmpty()) {pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}}HttpPost httpPost = new HttpPost(url);if (pairs != null && pairs.size() > 0) {httpPost.setEntity(new UrlEncodedFormEntity(pairs, CHARSET));}CloseableHttpResponse response = null;try {response = httpClient.execute(httpPost);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpPost.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);return result;} catch (ParseException e) {log.error("请求服务器端出错:" + e);return null;} finally {if (response != null)response.close();}}}

3.3 AppOrder

package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author: JYC* @Title: AppOrder* @Description: TODO* @Date: /4/21 14:10*/@SpringBootApplicationpublic class AppOrder {public static void main(String[] args) {SpringApplication.run(AppOrder.class);}}

3.4 application.yml

server:port: 8070

4. 访问

http://locahost:8070/orderToMember

四、常用注册中心

Eureka、Zookeeper、Consule、Nacos、Redis 等。

五、Eureka服务注册中心

1. Eureka基本介绍

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。

SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。

Eureka包含两个组件:Eureka Server和Eureka Client

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。

在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

2. Eureka环境构建

2.1 EurekaServer端

2.1.1 Maven依赖

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Finchley.RC2</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories>

2.1.2 配置文件

server:port: 9090 #服务注册中心端口号spring:application:name: spring-cloud-eureka-servereureka:instance:hostname: 127.0.0.1 #服务注册中心IP地址client:registerWithEureka: false #是否向服务注册中心注册自己fetchRegistry: false #是否检索服务serviceUrl: #服务注册中心的配置内容,指定服务注册中心的位置defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

2.1.3 启动类

package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.flix.eureka.server.EnableEurekaServer;/*** @Author: JYC* @Title: AppEurekaServer* @Description: TODO* @Date: /4/21 18:46*/@SpringBootApplication@EnableEurekaServerpublic class AppEurekaServer {public static void main(String[] args) {SpringApplication.run(AppEurekaServer.class);}}

2.2 EurekaClient端

2.2.1 Maven

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Finchley.RC2</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories>

2.2.2 配置文件

server:port: 8080spring:application:name: demo-member #服务名称 在注册中心展示服务名称eureka:client:service-url: # 服务注册中心地址defaultZone: http://127.0.0.1:9090/eureka/

2.2.3 启动类

package com.demo.service;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.flix.eureka.EnableEurekaClient;/*** @Author: JYC* @Title: com.demo.service.AppMember* @Description: TODO* @Date: /4/21 13:59*/@SpringBootApplication@EnableEurekaClientpublic class AppMember {public static void main(String[] args) {SpringApplication.run(AppMember.class);}}

2.3 服务发现

2.3.1 Maven

<dependencies><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.5</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Finchley.RC2</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository></repositories>

2.3.2 配置文件

server:port: 8070spring:application:name: demo-order #服务名称 在注册中心展示服务名称eureka:client:service-url: # 服务注册中心地址defaultZone: http://127.0.0.1:9090/eureka/

2.3.3 启动类

package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @Author: JYC* @Title: AppOrder* @Description: TODO* @Date: /4/21 14:10*/@SpringBootApplication@EnableDiscoveryClientpublic class AppOrder {public static void main(String[] args) {SpringApplication.run(AppOrder.class);}}

2.2.4 接口

package com.demo.service;import com.demo.utils.HttpClientUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestControllerpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {// HttpClient 工具类 实现RPC远程调用// String memberUrl = "http://192.168.66.1:8080/getMember";/*** 根据服务名称,从注册中心获取会员的接口地址*/List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");ServiceInstance serviceInstance = instances.get(0);// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";return "订单服务调用会员服务:" + HttpClientUtils.doGet(memberUrl, null);}}

2.2.5 工具类

package com.demo.utils;import mons.logging.Log;import mons.logging.LogFactory;import org.apache.http.HttpEntity;import org.apache.http.NameValuePair;import org.apache.http.ParseException;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Map;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/public class HttpClientUtils {private static final CloseableHttpClient httpClient;public static final String CHARSET = "UTF-8";private static final Log log = LogFactory.getLog(HttpClientUtils.class);// 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象static {RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build();httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();}public static String doGet(String url, Map<String, String> params) {return doGet(url, params, CHARSET);}public static String doPost(String url, Map<String, String> params) throws IOException {return doPost(url, params, CHARSET);}/*** HTTP Get 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容*/public static String doGet(String url, Map<String, String> params, String charset) {try {if (params != null && !params.isEmpty()) {List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}// 将请求参数和url进行拼接url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));}HttpGet httpGet = new HttpGet(url);CloseableHttpResponse response = httpClient.execute(httpGet);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpGet.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);response.close();return result;} catch (Exception e) {log.error("请求服务器端出错:" + e);return null;}}/*** HTTP Post 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容* @throws IOException*/public static String doPost(String url, Map<String, String> params, String charset)throws IOException {List<NameValuePair> pairs = null;if (params != null && !params.isEmpty()) {pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}}HttpPost httpPost = new HttpPost(url);if (pairs != null && pairs.size() > 0) {httpPost.setEntity(new UrlEncodedFormEntity(pairs, CHARSET));}CloseableHttpResponse response = null;try {response = httpClient.execute(httpPost);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpPost.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);return result;} catch (ParseException e) {log.error("请求服务器端出错:" + e);return null;} finally {if (response != null)response.close();}}}

2.4 Eureka 常用配置解析

Eureka 常用配置解析(点击跳转)

3. 效果展示

3.1 启动EurekaServer端

浏览器访问:http://127.0.0.1:9090/

出现如下界面表示服务端启动成功

3.2 启动EurekaClient端

浏览器访问:http://127.0.0.1:9090/

在界面中能看到"DEMO-MEMBER"表示启动成功

3.3 启动服务发现

浏览器访问:http://127.0.0.1:9090/

在界面中能看到"DEMO-ORDER"表示启动成功

3.4 接口测试

在浏览器中输入:http://127.0.0.1:8070/orderToMember

六、Nacos服务注册中心

1. Nacos基本介绍

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。

*由阿里巴巴推出,Java语言编写

Nacos官网: https://nacos.io/zh-cn/docs/what-is-nacos.html

1.1 什么是 Nacos

服务(Service)是 Nacos 世界的一等公民。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理:

Kubernetes Service

gRPC & Dubbo RPC Service

Spring Cloud RESTful Service

Nacos 的关键特性包括:

服务发现和服务健康监测

Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。

Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。

动态配置服务

动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。

动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。

配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。

Nacos 提供了一个简洁易用的UI (控制台样例 Demo) 帮助您管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。

动态 DNS 服务

动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。

Nacos 提供了一些简单的 DNS APIs TODO 帮助您管理服务的关联域名和可用的 IP:PORT 列表.

服务及其元数据管理

Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。

2. NacosServer端环境搭建

服务注册中心 如何设计

服务注册中心 web页面 管理服务注册信息内容服务注册中心Api接口 服务注册提供jar 能够被客户端支持 实现服务注册。

Nacos 下载地址:/alibaba/nacos/releases

进入到:nacos-server-2.0.3\nacos\bin startup.cmd 启动即可。

访问:127.0.0.1:8848/nacos

默认账户密码:nacos/nacos

2.1 Nacos2.0启动常见错误

默认Nacos是集群方式启动,初学者建议先改成单机版本。

修改:D:\path\cloud\nacos\bin startup.cmd 改成:set MODE=“standalone”

在双击启动: startup.cmd

3. 手动注册服务与发现

1.实现服务注册

发送post请求:

‘http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=mayikt-member&ip=20.18.7.10&port=8080’

2.实现服务发现

http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=mayikt-member

详细步骤操作:https://nacos.io/zh-cn/docs/quick-start.html

注意:发送请求类型是为Post类型

一直刷新观察控制台,大概要等到30s才会把实例剔除(大概15s设置不健康)

该知识点设计服务续约问题

4. NacosClient环境搭建

4.1 Maven依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.66</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.5</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>0.2.2.RELEASE</version></dependency></dependencies>

4.2 配置文件

server:port: 8080spring:application:name: demo-member #服务名称 在 注册中心展示服务名称 --cloud:nacos:discovery:server-addr: 127.0.0.1:8848 # nacos服务注册中心Server端 地址

4.3 接口

package com.demo.service;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** @Author: JYC* @Title: MemberService* @Description: TODO* @Date: /4/21 13:57*/@RestControllerpublic class MemberService {@Value("${server.port}")private String serverPort;/*** 会员服务提供接口* @return*/@RequestMapping("/getMember")public String getMember() {return "我是会员服务接口...端口:" + serverPort;}}

4.4 启动项目

package com.demo.service;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author: JYC* @Title: com.demo.service.AppMember* @Description: TODO* @Date: /4/21 13:59*/@SpringBootApplicationpublic class AppMember {public static void main(String[] args) {SpringApplication.run(AppMember.class);}}

5. NacosClient实现服务发现

5.1 Maven依赖

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>0.2.2.RELEASE</version></dependency></dependencies>

5.2 配置文件

server:port: 8070spring:application:name: demo-order #服务名称 在注册中心展示服务名称cloud:nacos:discovery:server-addr: 127.0.0.1:8848 # nacos服务注册中心Server端 地址

5.3 接口

package com.demo.service;import com.demo.utils.HttpClientUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestControllerpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {// HttpClient 工具类 实现RPC远程调用// String memberUrl = "http://192.168.66.1:8080/getMember";/*** 根据服务名称,从注册中心获取会员的接口地址*/List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");ServiceInstance serviceInstance = instances.get(0);// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";return "订单服务调用会员服务:" + HttpClientUtils.doGet(memberUrl, null);}}

5.4 工具类

package com.demo.utils;import mons.logging.Log;import mons.logging.LogFactory;import org.apache.http.HttpEntity;import org.apache.http.NameValuePair;import org.apache.http.ParseException;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import java.io.IOException;import java.util.ArrayList;import java.util.List;import java.util.Map;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/public class HttpClientUtils {private static final CloseableHttpClient httpClient;public static final String CHARSET = "UTF-8";private static final Log log = LogFactory.getLog(HttpClientUtils.class);// 采用静态代码块,初始化超时时间配置,再根据配置生成默认httpClient对象static {RequestConfig config = RequestConfig.custom().setConnectTimeout(60000).setSocketTimeout(15000).build();httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();}public static String doGet(String url, Map<String, String> params) {return doGet(url, params, CHARSET);}public static String doPost(String url, Map<String, String> params) throws IOException {return doPost(url, params, CHARSET);}/*** HTTP Get 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容*/public static String doGet(String url, Map<String, String> params, String charset) {try {if (params != null && !params.isEmpty()) {List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}// 将请求参数和url进行拼接url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));}HttpGet httpGet = new HttpGet(url);CloseableHttpResponse response = httpClient.execute(httpGet);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpGet.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);response.close();return result;} catch (Exception e) {log.error("请求服务器端出错:" + e);return null;}}/*** HTTP Post 获取内容** @param url请求的url地址 ?之前的地址* @param params 请求的参数* @param charset 编码格式* @return 页面内容* @throws IOException*/public static String doPost(String url, Map<String, String> params, String charset)throws IOException {List<NameValuePair> pairs = null;if (params != null && !params.isEmpty()) {pairs = new ArrayList<NameValuePair>(params.size());for (Map.Entry<String, String> entry : params.entrySet()) {String value = entry.getValue();if (value != null) {pairs.add(new BasicNameValuePair(entry.getKey(), value));}}}HttpPost httpPost = new HttpPost(url);if (pairs != null && pairs.size() > 0) {httpPost.setEntity(new UrlEncodedFormEntity(pairs, CHARSET));}CloseableHttpResponse response = null;try {response = httpClient.execute(httpPost);int statusCode = response.getStatusLine().getStatusCode();if (statusCode != 200) {httpPost.abort();throw new RuntimeException("HttpClient,error status code :" + statusCode);}HttpEntity entity = response.getEntity();String result = null;if (entity != null) {result = EntityUtils.toString(entity, "utf-8");}EntityUtils.consume(entity);return result;} catch (ParseException e) {log.error("请求服务器端出错:" + e);return null;} finally {if (response != null)response.close();}}}

5.5 启动类

package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @Author: JYC* @Title: AppOrder* @Description: TODO* @Date: /4/21 14:10*/@SpringBootApplicationpublic class AppOrder {public static void main(String[] args) {SpringApplication.run(AppOrder.class);}}

5.3 启动项目

5.4 接口测试

在Nacos的服务管理界面中看到刚刚启动的两个服务,表示两个服务均启动成功。

访问接口:http://127.0.0.1:8070/orderToMember

七、Resttemplate

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现。接下来我们就来看看这些操作方法的使用。

底层是基于HttpClient封装的

1. 接口

package com.demo.service;import com.demo.utils.HttpClientUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestControllerpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate RestTemplate restTemplate;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {// HttpClient 工具类 实现RPC远程调用// String memberUrl = "http://192.168.66.1:8080/getMember";/*** 根据服务名称,从注册中心获取会员的接口地址*/List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");ServiceInstance serviceInstance = instances.get(0);// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";ResponseEntity<String> response = restTemplate.getForEntity(memberUrl, String.class);return "订单服务调用会员服务:" + response.getBody();}}

2. 启动类

package com.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;import org.springframework.web.client.RestTemplate;/*** @Author: JYC* @Title: AppOrder* @Description: TODO* @Date: /4/21 14:10*/@SpringBootApplicationpublic class AppOrder {public static void main(String[] args) {SpringApplication.run(AppOrder.class);}/*** 将restTemplate注入到spring ioc容器* @return*/@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}}

八、本地负载均衡算法

1. 轮询算法

package com.demo.loadbalance;import org.springframework.cloud.client.ServiceInstance;public interface LoadBalance {/*** 负载均衡算法:给我多个地址,负载均衡会取出一个地址返回使用* @param serviceId* @return*/ServiceInstance getInstances(String serviceId);}

package com.demo.loadbalance;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.ponent;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;/*** @Author: JYC* @Title: RoundLoadBalance* @Description: TODO* @Date: /4/22 11:23*/@Componentpublic class RoundLoadBalance implements LoadBalance {@Autowiredprivate DiscoveryClient discoveryClient;private AtomicInteger atomicCount = new AtomicInteger(0);@Overridepublic ServiceInstance getInstances(String serviceId) {// 1. 根据服务的名称,获取该服务集群地址列表List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);// 2. 判断是否为nullif (instances == null || instances.size() == 0) {return null;}// 3. 使用负载均衡算法int index = atomicCount.incrementAndGet();return instances.get(index);}}

package com.demo.service;import com.demo.loadbalance.RoundLoadBalance;import com.demo.utils.HttpClientUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestControllerpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate RoundLoadBalance roundLoadBalance;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {// HttpClient 工具类 实现RPC远程调用// String memberUrl = "http://192.168.66.1:8080/getMember";/*** 根据服务名称,从注册中心获取会员的接口地址*/// List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");// ServiceInstance serviceInstance = instances.get(0);ServiceInstance serviceInstance = roundLoadBalance.getInstances("demo-member");// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";return "订单服务调用会员服务:" + HttpClientUtils.doGet(memberUrl, null);}}

2. 随机算法

package com.demo.loadbalance;import org.springframework.cloud.client.ServiceInstance;public interface LoadBalance {/*** 负载均衡算法:给我多个地址,负载均衡会取出一个地址返回使用* @param serviceId* @return*/ServiceInstance getInstances(String serviceId);}

package com.demo.loadbalance;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.ponent;import java.util.List;import java.util.Random;/*** @Author: JYC* @Title: RandomLoadBalance* @Description: TODO* @Date: /4/22 11:37*/@Componentpublic class RandomLoadBalance implements LoadBalance{@Autowiredprivate DiscoveryClient discoveryClient;@Overridepublic ServiceInstance getInstances(String serviceId) {// 1. 根据服务的名称,获取该服务集群地址列表List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);// 2. 判断是否为nullif (instances == null || instances.size() == 0) {return null;}// 生成随机数范围Random random = new Random();int index = random.nextInt(instances.size());return instances.get(index);}}

package com.demo.service;import com.demo.loadbalance.RandomLoadBalance;import com.demo.loadbalance.RoundLoadBalance;import com.demo.utils.HttpClientUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestControllerpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate RoundLoadBalance roundLoadBalance;@Autowiredprivate RandomLoadBalance randomLoadBalance;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {// HttpClient 工具类 实现RPC远程调用// String memberUrl = "http://192.168.66.1:8080/getMember";/*** 根据服务名称,从注册中心获取会员的接口地址*/// List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");// ServiceInstance serviceInstance = instances.get(0);// ServiceInstance serviceInstance = roundLoadBalance.getInstances("demo-member");ServiceInstance serviceInstance = randomLoadBalance.getInstances("demo-member");// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";return "订单服务调用会员服务:" + HttpClientUtils.doGet(memberUrl, null);}}

3. 故障转移算法

package com.demo.service;import com.demo.loadbalance.RandomLoadBalance;import com.demo.loadbalance.RoundLoadBalance;import com.demo.utils.HttpClientUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestController@Slf4jpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {/*** 根据服务名称,从注册中心获取会员的接口地址*/List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");for (int i = 0; i < instances.size(); i++) {try {ServiceInstance serviceInstance = instances.get(i);// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";}catch (Exception e) {log.error("[rpc远程调用发生了故障,开始故障转移,切换下一个地址调用 e:{}]", e);}}return "fail";}}

4. 权重算法

package com.demo;import com.demo.loadbalance.LoadBalance;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.ponent;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;/*** @Author: JYC* @Title: WeightLoadBalance* @Description: TODO* @Date: /4/22 14:38*/@Componentpublic class WeightLoadBalance implements LoadBalance {@Autowiredprivate DiscoveryClient discoveryClient;private AtomicInteger countAtomicInteger = new AtomicInteger(0);@Overridepublic ServiceInstance getInstances(String serviceId) {// 1.根据服务Id名称,获取该接口多个实例List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);if (instances == null) {return null;}List<ServiceInstance> newInstances = new ArrayList<>();// 循环遍历该服务名称,对应的多个实例instances.forEach((service) -> {// 获取该服务实例对应的权重比例Double weight = Double.parseDouble(service.getMetadata().get("nacos.weight"));for (int i = 0; i < weight; i++) {newInstances.add(service);}});// 线程安全return newInstances.get(countAtomicInteger.incrementAndGet() % newInstances.size());}}

package com.demo.service;import com.demo.WeightLoadBalance;import com.demo.loadbalance.RandomLoadBalance;import com.demo.loadbalance.RoundLoadBalance;import com.demo.utils.HttpClientUtils;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.discovery.DiscoveryClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @Author: JYC* @Title: OrderToMemberService* @Description: TODO* @Date: /4/21 14:04*/@RestController@Slf4jpublic class OrderToMemberService {@Autowiredprivate DiscoveryClient discoveryClient;@Autowiredprivate WeightLoadBalance weightLoadBalance;/*** 订单服务,调用 会员服务接口*/@RequestMapping("/orderToMember")public String OrderToMember() {/*** 根据服务名称,从注册中心获取会员的接口地址*/// List<ServiceInstance> instances = discoveryClient.getInstances("demo-member");// ServiceInstance serviceInstance = instances.get(0);ServiceInstance serviceInstance = weightLoadBalance.getInstances("demo-member");// 会员服务的ip和端口String memberUrl = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/" + "getMember";return "订单服务调用会员服务:" + HttpClientUtils.doGet(memberUrl, null);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。