一、SpringCloud基本概念
1.为什么要使用SpringCloud
SpringCloud并不是rpc远程调用框架,而是一套全家桶的微服务解决框架,理念就是解决我们在微服务架构中遇到的任何问题。例如:服务注册中心、分布式配置、服务保护等。 SpringCloud 属于微服务全家桶框架 解决我们在 微服务架构中遇到难题。 2.微服务架构中常见问题
分布式服务注册中心(服务治理) Eureka、Zookeeper、Consule、Nacos、Redis、数据库等; 分布式配置中心 SpringCloud Config、携程阿波罗、Nacos Config; 分布式事务解决方案(MQ最终一致性/LCN(已经淘汰)/ Seata(阿里背书)) 分布式任务调度平台(xxl-job、elastic job、阿里巴巴Scheduler) 分布式日志采集系统ELK+Kafka 分布式服务追踪与调用链Zipkin、skywalking等。 分布式锁(Redis(Redisson)/Zookeeper(Curator)实现分布式锁) 服务的接口保护(hystrix/sentinel) 3.SpringCloud第一代和第二代区别
SpringCloud第一代:
SpringCloud Config 分布式配置中心 SpringCloud Netflix 核心组件 Eureka:服务治理 Hystrix:服务保护框架 Ribbon:客户端负载均衡器 Feign:基于ribbon和hystrix的声明式服务调用组件 Zuul: 网关组件,提供智能路由、访问过滤等功能 SpringCloud第二代
Spring Cloud Gateway 网关 Spring Cloud Loadbalancer 客户端负载均衡器 Spring Cloud r4j(Resilience4J) 服务保护 Spring Cloud Alibaba Nacos 服务注册 Spring Cloud Alibaba Nacos 分布式配置中心 Spring Cloud Alibaba Sentinel服务保护 SpringCloud Alibaba Seata分布式事务解决框架 Alibaba Cloud OSS 阿里云存储 Alibaba Cloud SchedulerX 分布式任务调度平台 Alibaba Cloud SMS 分布式短信系统 4.为什么Alibaba要推出SpringCloud组件
SpringCloud与alibaba相结合,技术上有人负责更新新的组件,也还可以继续使用Spring社区的技术
二、微服务服务注册中心
1.微服务服务注册中心概念
2.什么是远程调用
例如我们平台需要获取到天气预报,就可以直接调用中国天气预报接口:http://wthrcdn.etouch.cn/weather_mini?city=武汉
,返回的是json格式数据,可以使用https://www.sojson.com/
在线json格式化查看
{ "data" : { "yesterday" : { "date" : "26日星期六" , "high" : "高温 15℃" , "fx" : "北风" , "low" : "低温 10℃" , "fl" : "<![CDATA[2级]]>" , "type" : "阴" } , "city" : "武汉" , "forecast" : [ { "date" : "27日星期天" , "high" : "高温 19℃" , "fengli" : "<![CDATA[2级]]>" , "low" : "低温 7℃" , "fengxiang" : "北风" , "type" : "阴" } , { "date" : "28日星期一" , "high" : "高温 21℃" , "fengli" : "<![CDATA[3级]]>" , "low" : "低温 7℃" , "fengxiang" : "东南风" , "type" : "晴" } , { "date" : "29日星期二" , "high" : "高温 24℃" , "fengli" : "<![CDATA[3级]]>" , "low" : "低温 10℃" , "fengxiang" : "东南风" , "type" : "多云" } , { "date" : "30日星期三" , "high" : "高温 24℃" , "fengli" : "<![CDATA[2级]]>" , "low" : "低温 12℃" , "fengxiang" : "东风" , "type" : "中雨" } , { "date" : "31日星期四" , "high" : "高温 18℃" , "fengli" : "<![CDATA[4级]]>" , "low" : "低温 14℃" , "fengxiang" : "北风" , "type" : "小雨" } ] , "ganmao" : "感冒易发期,外出请适当调整衣物,注意补充水分。" , "wendu" : "16" } , "status" : 1000 , "desc" : "OK" }
3.什么是RPC
远程调用采用接口:接口协议+IP+端口/接口名称?接口参数
接口协议
1.netty 基于netty RPC自定义协议 dubbo 2.http协议接口 httpclient或者okhttp 3.WebService 底层soap+http+xml 微服务架构:http+json格式,json格式比xml格式更加轻量级 http传递参数
如果只有一个参数 可以直接使用?传递参数 如果是多个参数 json格式
三、使用HttpClient实现RPC远程调用
1.maven依赖
< dependency>
< groupId> org.apache.httpcomponents</ groupId>
< artifactId> httpclient</ artifactId>
< version> 4.5.5</ version>
</ dependency>
< dependency>
< groupId> com.alibaba</ groupId>
< artifactId> fastjson</ artifactId>
< version> 1.2.66</ version>
</ dependency>
2.工具类HttpClientUtils
import org. apache. commons. logging. Log ;
import org. apache. commons. 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 ;
public class HttpClientUtils {
private static final CloseableHttpClient httpClient;
public static final String CHARSET = "UTF-8" ;
private static final Log log = LogFactory . getLog ( HttpClientUtils . class ) ;
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) ;
}
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 += "?" + 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 ;
}
}
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.RpcController
import com. alibaba. fastjson. JSONArray ;
import com. alibaba. fastjson. JSONObject ;
import com. sjyl. utils. HttpClientUtils ;
import org. springframework. web. bind. annotation. RequestMapping ;
import org. springframework. web. bind. annotation. RestController ;
import java. util. HashMap ;
@RestController
public class RpcController {
@RequestMapping ( "/getWeather" )
public Object getWeather ( String city) {
String url = "http://wthrcdn.etouch.cn/weather_mini" ;
HashMap < String , String > params = new HashMap < String , String > ( ) ;
params. put ( "city" , city) ;
String result = HttpClientUtils . doGet ( url, params) ;
JSONObject json = JSONObject . parseObject ( result) ;
Integer status = json. getInteger ( "status" ) ;
if ( status != 1000 ) {
return "查询天气失败,请稍后重试!" ;
}
JSONArray forecast = json. getJSONObject ( "data" ) . getJSONArray ( "forecast" ) ;
return forecast;
}
}
4.测试
测试地址
http://127.0.0.1:8088/sjyl/getWeather?city=福州