Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析

说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们应该如何进行网络请求的优化与处理呢?

到底有没有一些好的建议与方案呢?

下面这个文章将揭晓上面的问题,让你对SpringCloud微服务网络请求性能有一个全新的认识.

目录简介

  • 01.网络请求异常分类
  • 02.开发中注意问题
  • 03.原始的处理方式
  • 04.如何减少代码耦合性
  • 05.异常统一处理步骤
  • 06.完成版代码展示

01.网络请求异常分类

网络请求异常大概有哪些?

  • 第一种:访问接口异常,比如404,500等异常,出现这类异常,Retrofit会自动抛出异常。
  • 第二种:解析数据异常,数据体发生变化可能会导致这个问题。
  • 第三种:其他类型异常,比如服务器响应超时异常,链接失败异常,网络未连接异常等等。
  • 第四种:网络请求成功,但是服务器定义了异常状态,比如token失效,参数传递错误,或者统一给提示(这个地方比较拗口,比如购物app,你购买n件商品请求接口成功,code为200,但是服务器发现没有这么多商品,这个时候就会给你一个提示,然后客户端拿到这个进行吐司)

02.开发中注意问题

在获取数据的流程中,访问接口和解析数据时都有可能会出错,我们可以通过拦截器在这两层拦截错误。

  • 1.在访问接口时,我们不用设置拦截器,因为一旦出现错误,Retrofit会自动抛出异常。比如,常见请求异常404,500,503等等。为了方便后期排查问题,这个可以在debug环境下打印日志就可以。
  • 2.在解析数据时,我们设置一个拦截器,判断Result里面的code是否为成功,如果不成功,则要根据与服务器约定好的错误码来抛出对应的异常。比如,token失效后跳转登录页面,禁用同账号登陆多台设备,缺少参数,参数传递异常等等。
  • 3.除此以外,为了我们要尽量避免在View层对错误进行判断,处理,我们必须还要设置一个拦截器,拦截onError事件,然后使用ExceptionUtils,让其根据错误类型来分别处理。

03.原始的处理方式

  • 最简单的处理方式,直接对返回的throwable进行类型判断处理
//请求,对throwable进行判断
ServiceHelper.getInstance()
.getModelResult(param1, param2)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Model>() {
@Override
public void onCompleted() { } @Override
public void onError(Throwable e) {
if(e instanceof HttpException){
//获取对应statusCode和Message
HttpException exception = (HttpException)e;
String message = exception.response().message();
int code = exception.response().code();
}else if(e instanceof SSLHandshakeException){
//接下来就是各种异常类型判断...
}else if(e instanceof ...){ }...
} @Override
public void onNext(Model model) {
if(model.getCode != CODE_SUCCESS){
int code = model.getCode();
switch (code){
case CODE_TOKEN_INVALID:
ex.setDisplayMessage("重新登陆");
break;
case CODE_NO_OTHER:
ex.setDisplayMessage("其他情况");
break;
case CODE_SHOW_TOAST:
ex.setDisplayMessage("吐司服务器返回的提示");
break;
case CODE_NO_MISSING_PARAMETER:
ex.setDisplayMessage("缺少参数,用log记录服务器提示");
break;
default:
ex.setDisplayMessage(message);
break;
}
}else{
//正常处理逻辑
}
}
});

04.如何减少代码耦合性

  • 为了不改变以前的代码结构,那么如何做才能够彻底解耦呢?一般情况下使用Retrofit网络请求框架,会有回调方法,如下所示:
package retrofit2;

public interface Callback<T> {
void onResponse(Call<T> var1, Response<T> var2); void onFailure(Call<T> var1, Throwable var2);
}
  • 不管以前代码封装与否,都希望一句代码即可实现网络请求拦截处理逻辑。那么这个时候,我是怎么处理的呢?
public class ResponseData<T> {

    private int code;
private String message;
private T t; public int getCode() {
return code;
} public String getMessage() {
return message;
} public T getT() {
return t;
}
} new Callback<ResponseData<HomeBlogEntity>>(){
@Override
public void onResponse(Call<ResponseData<HomeBlogEntity>> call,
Response<ResponseData<HomeBlogEntity>> response) {
int code = response.body().getCode();
String message = response.body().getMessage();
HomeBlogEntity t = response.body().getT();
if (code!= CODE_SUCCESS){
//网络请求成功200,不过业务层执行服务端制定的异常逻辑
ExceptionUtils.serviceException(code,message);
} else {
//网络请求成功,业务逻辑正常处理
}
} @Override
public void onFailure(Call call, Throwable throwable) {
ExceptionUtils.handleException(throwable);
}
};

05.异常统一处理步骤

  • 第一步:定义请求接口网络层失败的状态码
/**
* 对应HTTP的状态码
*/
private static final int BAD_REQUEST = 400;
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int METHOD_NOT_ALLOWED = 405;
private static final int REQUEST_TIMEOUT = 408;
private static final int CONFLICT = 409;
private static final int PRECONDITION_FAILED = 412;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504;
  • 第二步,接口请求成功,业务层失败,服务端定义异常状态码

比如,登录过期,提醒用户重新登录;
比如,添加商品,但是服务端发现库存不足,这个时候接口请求成功,服务端定义业务层失败,服务端给出提示语,客户端进行吐司
比如,请求接口,参数异常或者类型错误,请求code为200成功状态,不过给出提示,这个时候客户端用log打印服务端给出的提示语,方便快递查找问题
比如,其他情况,接口请求成功,但是服务端定义业务层需要吐司服务端返回的对应提示语

/**
* 服务器定义的状态吗
* 比如:登录过期,提醒用户重新登录;
* 添加商品,但是服务端发现库存不足,这个时候接口请求成功,服务端定义业务层失败,服务端给出提示语,客户端进行吐司
* 请求接口,参数异常或者类型错误,请求code为200成功状态,不过给出提示,这个时候客户端用log打印服务端给出的提示语,方便快递查找问题
* 其他情况,接口请求成功,但是服务端定义业务层需要吐司服务端返回的对应提示语
*/
/**
* 完全成功
*/
private static final int CODE_SUCCESS = 0;
/**
* Token 失效
*/
public static final int CODE_TOKEN_INVALID = 401;
/**
* 缺少参数
*/
public static final int CODE_NO_MISSING_PARAMETER = 400400;
/**
* 其他情况
*/
public static final int CODE_NO_OTHER = 403;
/**
* 统一提示
*/
public static final int CODE_SHOW_TOAST = 400000; 第三步,自定义Http层的异常和服务器定义的异常类
public class HttpException extends Exception { private int code;
private String displayMessage; public HttpException(Throwable throwable, int code) {
super(throwable);
this.code = code;
} public void setDisplayMessage(String displayMessage) {
this.displayMessage = displayMessage;
} public String getDisplayMessage() {
return displayMessage;
} public int getCode() {
return code;
}
} public class ServerException extends RuntimeException { public int code;
public String message; public int getCode() {
return code;
} public void setCode(int code) {
this.code = code;
} @Override
public String getMessage() {
return message;
} public void setMessage(String message) {
this.message = message;
}
}
  • 第四步,统一处理异常逻辑如下所示
/**
* 这个可以处理服务器请求成功,但是业务逻辑失败,比如token失效需要重新登陆
* @param code 自定义的code码
*/
public static void serviceException(int code , String content){
if (code != CODE_SUCCESS){
ServerException serverException = new ServerException();
serverException.setCode(code);
serverException.setMessage(content);
handleException(serverException);
}
} /**
* 这个是处理网络异常,也可以处理业务中的异常
* @param e e异常
*/
public static void handleException(Throwable e){
HttpException ex;
//HTTP错误 网络请求异常 比如常见404 500之类的等
if (e instanceof retrofit2.HttpException){
retrofit2.HttpException httpException = (retrofit2.HttpException) e;
ex = new HttpException(e, ErrorCode.HTTP_ERROR);
switch(httpException.code()){
case BAD_REQUEST:
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case REQUEST_TIMEOUT:
case CONFLICT:
case PRECONDITION_FAILED:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
//均视为网络错误
default:
ex.setDisplayMessage("网络错误"+httpException.code());
break;
}
} else if (e instanceof ServerException){
//服务器返回的错误
ServerException resultException = (ServerException) e;
int code = resultException.getCode();
String message = resultException.getMessage();
ex = new HttpException(resultException, ErrorCode.SERVER_ERROR);
switch (code){
case CODE_TOKEN_INVALID:
ex.setDisplayMessage("token失效");
//下面这里可以统一处理跳转登录页面的操作逻辑
break;
case CODE_NO_OTHER:
ex.setDisplayMessage("其他情况");
break;
case CODE_SHOW_TOAST:
ex.setDisplayMessage("吐司");
break;
case CODE_NO_MISSING_PARAMETER:
ex.setDisplayMessage("缺少参数");
break;
default:
ex.setDisplayMessage(message);
break;
}
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException){
ex = new HttpException(e, ErrorCode.PARSE_ERROR);
//均视为解析错误
ex.setDisplayMessage("解析错误");
}else if(e instanceof ConnectException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//均视为网络错误
ex.setDisplayMessage("连接失败");
} else if(e instanceof java.net.UnknownHostException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//网络未连接
ex.setDisplayMessage("网络未连接");
} else if (e instanceof SocketTimeoutException) {
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//网络未连接
ex.setDisplayMessage("服务器响应超时");
} else {
ex = new HttpException(e, ErrorCode.UNKNOWN);
//未知错误
ex.setDisplayMessage("未知错误");
}
String displayMessage = ex.getDisplayMessage();
//这里直接吐司日志异常内容,注意正式项目中一定要注意吐司合适的内容
ToastUtils.showRoundRectToast(displayMessage);
}
  • 第五步,如何调用
@Override
public void onError(Throwable e) {
//直接调用即可
ExceptionUtils.handleException(e);
}

06.完成版代码展示

  • 如下所示
public class ExceptionUtils {

    /*
* 在使用Retrofit+RxJava时,我们访问接口,获取数据的流程一般是这样的:订阅->访问接口->解析数据->展示。
* 如上所说,异常和错误本质是一样的,因此我们要尽量避免在View层对错误进行判断,处理。
*
* 在获取数据的流程中,访问接口和解析数据时都有可能会出错,我们可以通过拦截器在这两层拦截错误。
* 1.在访问接口时,我们不用设置拦截器,因为一旦出现错误,Retrofit会自动抛出异常。
* 2.在解析数据时,我们设置一个拦截器,判断Result里面的code是否为成功,如果不成功,则要根据与服务器约定好的错误码来抛出对应的异常。
* 3.除此以外,为了我们要尽量避免在View层对错误进行判断,处理,我们必须还要设置一个拦截器,拦截onError事件,然后使用ExceptionHandler,让其根据错误类型来分别处理。
*/ /**
* 对应HTTP的状态码
*/
private static final int BAD_REQUEST = 400;
private static final int UNAUTHORIZED = 401;
private static final int FORBIDDEN = 403;
private static final int NOT_FOUND = 404;
private static final int METHOD_NOT_ALLOWED = 405;
private static final int REQUEST_TIMEOUT = 408;
private static final int CONFLICT = 409;
private static final int PRECONDITION_FAILED = 412;
private static final int INTERNAL_SERVER_ERROR = 500;
private static final int BAD_GATEWAY = 502;
private static final int SERVICE_UNAVAILABLE = 503;
private static final int GATEWAY_TIMEOUT = 504; /**
* 服务器定义的状态吗
* 比如:登录过期,提醒用户重新登录;
* 添加商品,但是服务端发现库存不足,这个时候接口请求成功,服务端定义业务层失败,服务端给出提示语,客户端进行吐司
* 请求接口,参数异常或者类型错误,请求code为200成功状态,不过给出提示,这个时候客户端用log打印服务端给出的提示语,方便快递查找问题
* 其他情况,接口请求成功,但是服务端定义业务层需要吐司服务端返回的对应提示语
*/
/**
* 完全成功
*/
private static final int CODE_SUCCESS = 0;
/**
* Token 失效
*/
public static final int CODE_TOKEN_INVALID = 401;
/**
* 缺少参数
*/
public static final int CODE_NO_MISSING_PARAMETER = 400400;
/**
* 其他情况
*/
public static final int CODE_NO_OTHER = 403;
/**
* 统一提示
*/
public static final int CODE_SHOW_TOAST = 400000; /**
* 这个可以处理服务器请求成功,但是业务逻辑失败,比如token失效需要重新登陆
* @param code 自定义的code码
*/
public static void serviceException(int code , String content){
if (code != CODE_SUCCESS){
ServerException serverException = new ServerException();
serverException.setCode(code);
serverException.setMessage(content);
handleException(serverException);
}
} /**
* 这个是处理网络异常,也可以处理业务中的异常
* @param e e异常
*/
public static void handleException(Throwable e){
HttpException ex;
//HTTP错误 网络请求异常 比如常见404 500之类的等
if (e instanceof retrofit2.HttpException){
retrofit2.HttpException httpException = (retrofit2.HttpException) e;
ex = new HttpException(e, ErrorCode.HTTP_ERROR);
switch(httpException.code()){
case BAD_REQUEST:
case UNAUTHORIZED:
case FORBIDDEN:
case NOT_FOUND:
case METHOD_NOT_ALLOWED:
case REQUEST_TIMEOUT:
case CONFLICT:
case PRECONDITION_FAILED:
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
//均视为网络错误
default:
ex.setDisplayMessage("网络错误"+httpException.code());
break;
}
} else if (e instanceof ServerException){
//服务器返回的错误
ServerException resultException = (ServerException) e;
int code = resultException.getCode();
String message = resultException.getMessage();
ex = new HttpException(resultException, ErrorCode.SERVER_ERROR);
switch (code){
case CODE_TOKEN_INVALID:
ex.setDisplayMessage("重新登陆");
break;
case CODE_NO_OTHER:
ex.setDisplayMessage("其他情况");
break;
case CODE_SHOW_TOAST:
ex.setDisplayMessage("吐司");
break;
case CODE_NO_MISSING_PARAMETER:
ex.setDisplayMessage("缺少参数");
break;
default:
ex.setDisplayMessage(message);
break;
}
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException){
ex = new HttpException(e, ErrorCode.PARSE_ERROR);
//均视为解析错误
ex.setDisplayMessage("解析错误");
}else if(e instanceof ConnectException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//均视为网络错误
ex.setDisplayMessage("连接失败");
} else if(e instanceof java.net.UnknownHostException){
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//网络未连接
ex.setDisplayMessage("网络未连接");
} else if (e instanceof SocketTimeoutException) {
ex = new HttpException(e, ErrorCode.NETWORK_ERROR);
//网络未连接
ex.setDisplayMessage("服务器响应超时");
} else {
ex = new HttpException(e, ErrorCode.UNKNOWN);
//未知错误
ex.setDisplayMessage("未知错误");
}
String displayMessage = ex.getDisplayMessage();
//这里直接吐司日志异常内容,注意正式项目中一定要注意吐司合适的内容
ToastUtils.showRoundRectToast(displayMessage);
}
}

感谢你能读到最后,希望能对你有所帮助。

 如果需要源代码或者架构文档的,请加QQ群:793305035

Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析的更多相关文章

  1. Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案

    Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案 说明:Java生鲜电商平台中由于采用了微服务架构进行业务的处理,买家,卖家,配送,销售,供应商等进行服务化,但是不可避免存在 ...

  2. Java生鲜电商平台-SpringCloud微服务架构中核心要点和实现原理

    Java生鲜电商平台-SpringCloud微服务架构中核心要点和实现原理 说明:Java生鲜电商平台中,我们将进一步理解微服务架构的核心要点和实现原理,为读者的实践提供微服务的设计模式,以期让微服务 ...

  3. Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战

    Java生鲜电商平台-SpringCloud微服务架构高并发参数优化实战 一.写在前面 在Java生鲜电商平台平台中相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这 ...

  4. Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲

    Java生鲜电商平台-SpringCloud微服务开发中的数据架构设计实战精讲 Java生鲜电商平台:   微服务是当前非常流行的技术框架,通过服务的小型化.原子化以及分布式架构的弹性伸缩和高可用性, ...

  5. Java生鲜电商平台-订单中心服务架构与异常订单逻辑

    Java生鲜电商平台-订单中心服务架构与异常订单逻辑 订单架构实战中阐述了订单系统的重要性,并从订单系统的信息架构和流程上对订单系统有了总体认知,同时还穿插着一些常见的订单业务规则和逻辑.上文写到订单 ...

  6. Java生鲜电商平台-SpringCloud分布式请求跟踪系统设计与实践

    Java生鲜电商平台-SpringCloud分布式请求跟踪系统设计与实践 Java生鲜电商平台微服务现状 某个服务挂了,导致上游大量报警,如何快速定位哪个服务出问题? 某个核心挂了,导致大量报错,如何 ...

  7. Java生鲜电商平台-商品基础业务架构设计-商品设计

    Java生鲜电商平台-商品基础业务架构设计-商品设计 在生鲜电商的商品中心,在电子商务公司一般是后台管理商品的地方.在前端而言,是商家为了展示商品信息给用户的地方,它是承担了商品的数据,订单,营销活动 ...

  8. Java生鲜电商平台-统一异常处理及架构实战

    Java生鲜电商平台-统一异常处理及架构实战 补充说明:本文讲得比较细,所以篇幅较长. 请认真读完,希望读完后能对统一异常处理有一个清晰的认识. 背景 软件开发过程中,不可避免的是需要处理各种异常,就 ...

  9. Java生鲜电商平台-订单模块状态机架构设计

    Java生鲜电商平台-订单模块状态机架构设计 说明:在Java生鲜电商平台中订单的状态流转业务        我们知道 一个订单会有很多种状态:临时单.已下单.待支付.待收货.待评价.已完成,退货中等 ...

随机推荐

  1. 【Linux命令】用户身份(useradd,groupadd,usermod,passwd,userdel)

    目录 用户身份 useradd userdel usermod groupadd groupdel passwd chage 用户身份 在linux系统中和windows一样有用户之分.root用户为 ...

  2. Charles 使用笔记

     一.介绍 Charles 属于抓包软件. Charles 多平台支持,mac.windows.linux. Charles 是收费软件,可以免费试用 30 天.试用期过后,未付费的用户仍然可以继续使 ...

  3. Maven学习 --- <distributionManagement>

    在使用maven过程中,我们在开发阶段经常性的会有很多公共库处于不稳定状态,随时需要修改并发布,可能一天就要发布一次,遇到bug时,甚至一天要发布N次.我们知道,maven的依赖管理是基于版本管理的, ...

  4. Python自动发送邮件--smtplib模块

    import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText f ...

  5. 一则sql优化实现接口耗时降低30倍的优化案例

    业务场景: 也测的业务,如上图,通过捕获业务的涉及的接口如下: 查询接口耗时大于7s,已经是非常的慢 经验提示: 一般接口响应时间慢的问题,最简单的方式就是监控接口相关的sql是否存在问题 开启mys ...

  6. ASP.Net 连接多个数据库之间的切换

    本次两个的两个数据是SQL Server 和ORCAL 首先在Web.congfig中 <connectionStrings> </connectionStrings>里面添加 ...

  7. OC-AVAudioSession的知识小记

    参考文章:https://www.cnblogs.com/junhuawang/p/7920989.html 音频输出作为硬件资源,对于iOS系统来说是唯一的,那么要如何协调和各个App之间对这个稀缺 ...

  8. Thymeleaf对象的使用:基本对象

    Thymeleaf中有许多内置对象,可以在模板中实现各种功能.下面有几个基本对象.Web对象常用有:request.session.servletContext.Thymeleaf提供了几个内置变量p ...

  9. 【心得】Lattice EPC3 PCS使用经验

    [博客导航] [导航]FPGA相关 一.目的 将使用过程中遇到的问题随时记录,共享经验心得. 二.心得随笔 1.仿真脚本 为避免生成PCS IP之后,仿真时忘记添加参数文件,可以在仿真脚本中添加以下代 ...

  10. Day9 - Python基础9 socket基础、粘包

    本节内容: 1.socket的介绍 2.基于tcp的socket 3.基于tcp的问题分析 4.基于udp的socket 5.基于udp的问题分析 6.基于udp的ntp服务 7.基于tcp的远程执行 ...