17.支付宝支付微信支付
简单介绍一下支付宝支付开发流程
我们先要在支付宝官网申请公司企业账号并开通一个应用,在应用里签约APP支付功能,然后获取到这几个值①商户appid ②商户公钥、私钥 ③支付宝公钥 ④支付宝网关地址
其中商户appid,商户公钥、私钥,都可以得到,支付宝公钥需要下载一个支付宝公钥生成器,
获取到支付宝公钥,然后将商户公钥上传到支付宝上,支付宝就可以解锁我们传过去带商户私钥的信息.将支付宝的SDK集成到项目系统里,下载java的SDK,解压后得alipay-sdk-java20180122110032.jar、commons-logging-1.1.1.jar,其中alipay-sdk-java20180122110032.jar需要安装到本地maven仓库,将这两个jar添加到pom.xml里,环境就搭建好了.
-
我们需要写3个对外提供的接口即可
1.用户点击“立即购买”时调用商户后台接口,后台返回加签后的订单信息字符串2.在支付完成之后,支付宝异步通知商户后台订单的付款情况
3.在支付完成之后,跳转回APP时,APP调用商户后台进行最终付款校验
实现支付宝接口详细过程
1.去支付宝官网申请公司企业账号并开通一个应用,在应用里签约APP支付功能
具体的申请截图步骤,在这里我就不详细说了,因为这不是文章的重点,可参考支付宝官网。
经过这一步,我们可以得过开发中需要用到的几个参数
①商户appid ②商户公钥、私钥 ③支付宝公钥 ④支付宝网关地址
解释一下这几个参数:
1.商户appid是识别商户的唯一ID,是让支付宝识别,我们到底是哪一个商户,这样支付宝就能识别商户对应的账号、用户号、收款账号...等等一系列信息。
2.商户公钥、私钥以及支付宝公钥这3个参数是对商户系统与支付宝进行信息交互的数字签名用的,相信各位大学里也有学过关于数字签名的一些知识,在这里,我就简单说一下我理解的过程:首先是商户系统需要给支付宝发送信息(支付、查询等等....),涉及钱方面,咱们当前要谨慎一点对吧,所以我们需要对发送之前的信息加把锁(用商户私钥进行签名),然后再发送给支付宝。支付宝收到商户发送的信息之后,发现上了把锁,那肯定得要一把钥匙(商户公钥)来解锁对吧,所以商户在跟支付宝签约APP支付功能的时候,就得把这把钥匙上传给支付宝了,支付宝就可以用商户的公钥进行解锁了。反过来也是一样,支付宝需要发送信息给商户信息,先用支付宝的私钥进行签名,再发送给商户系统,商户系统收到支付宝反馈过来的信息后,再用支付宝的公钥进行解密。在这里我们并没有用到支付宝的私钥,所以我们并不需要得到支付宝的私钥。这里放一个生成私钥公钥的支付宝官方工具
3.支付宝网关地址,是用来配置发送给支付宝的网关地址的。
2.将支付宝的SDK集成到项目系统里
支付宝的SDK指的就是支付宝提供的工具Jar包给我们开发者,SDK封装了大量的基础功能,使我们可以快速开发支付宝接口。这也是我在前面说的比微信支付接口更容易实现的原因。获取支付宝SDK地址:支付宝SDK下载地址,这里我选择JAVA的SDK。下载解压后的SDK得到:
alipay-sdk-java20180122110032.jar、commons-logging-1.1.1.jar是我们需要导入到项目里的,因为项目后台的大致的架构是maven+springBoot+jpa,所以我们需要从maven里导入jar包,首先是导入commons-logging-1.1.1.jar,不用多说,咱直接在pom.xml里加上:
然后是alipay-sdk-java20180122110032.jar,如果你也像上面一样直接加入到pom.xml,会发现,咦,怎么一直下载不下来。当然alipy的包在线上的maven仓库并没有,所以我们需要导入到本地的maven仓库。前提是配置好maven的环境变量,将包放在G:\alipay\sdk下,然后打开dos窗口,cd进入到G:\alipay\sdk下,执行maven如下命令:
mvn install:install-file -DgroupId=com.alipay -DartifactId=sdk-java -Dversion=20180122110032 -Dpackaging=jar -Dfile=alipay-sdk-java20180122110032.jar
导入成功后,在项目的pom.xml里继续添加
到此,我们就顺利把支付宝SDK集成到项目里,是不是有点小兴奋,我们很快可以开发了!
3.看支付宝提供的API和网上各位牛人总结的经验,后台使用支付宝的SDK与支付宝进行交互
先看一下支付宝支付流程:
首先,我们来理一理开发的思路,按照我当前项目的需求,关于支付这一块大概操作流程是:用户在APP上选好要购买的商品,点击“立即购买”,跳转到订单详细页面。选择支付方式,点击“确定支付”跳转到支付宝APP,付款完成后,跳转回APP,完成支付。这个过程,当用户点击“确定支付”时,APP需要调用商户后台接口。
这时候就是我们所需要做的事情:先是生成商户系统一笔未支付的订单,获得商户订单ID(商户系统生成)和订单的一些其他信息,然后再调用支付宝的SDK提供的数字签名方法,将需要传给支付宝的信息进行加签,然后把加签后的字符串返回给APP。APP拉起支付宝APP,再把这个加签的字符串传给支付宝,完成支付。APP接收到同步通知后,还需要再次调用商户后台的接口(虽然同步通知也有付款情况,但需要以后台通知为准),校验订单最终的付款情况。按照支付宝API上所说,当完成支付后,支付宝会做2个操作,一个是同步返回信息给APP,一个是异步通知商户后台返回支付状态等信息,并且最终的支付结果是以异步通知为准。所以我们还需要考虑到一点,就是当用户支付成功之后,商户系统暂时没有接收到支付宝的异步通知时。我们需要拿着这个商户订单ID主动调用SDK支付宝的查询接口,去获取该订单的支付情况,并最终返回给APP。这个查询的接口应该是给APP收到同步通知后,请求商户系统后台进行校验的时候调用的。
根据我们上面思考所得,后台只需要对外提供3个接口即可
1.用户点击“立即购买”时调用商户后台接口,后台返回加签后的订单信息字符串
2.在支付完成之后,支付宝异步通知商户后台订单的付款情况
3.在支付完成之后,跳转回APP时,APP调用商户后台进行最终付款校验
想通想明白之后,终于接一下我们要敲代码了,哈哈哈哈
首先,我们来准备一下需要传给支付宝SDK的公共基本参数,我把参数放到一个单独的类里,你也可以放到数据库里,代码如下:
public class AlipayConfig {
// 1.商户appid
//public static String APPID = "2017...";
//2.私钥 pkcs8格式的
public static String RSA_PRIVATE_KEY ="MIIEwAIBADANBg.....";
// 3.支付宝公钥
public static String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkq.....";
// 4.服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://www.xxx.com/alipay/notify_url.do";
//5.页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
public static String return_url = "http://www.xxx.com/alipay/return_url.do";
// 6.请求支付宝的网关地址
public static String URL = "https://openapi.alipay.com/gateway.do";
// 7.编码
public static String CHARSET = "UTF-8";
// 8.返回格式
public static String FORMAT = "json";
// 9.加密类型
public static String SIGNTYPE = "RSA2";
}
1.实现第一个接口:用户点击“立即购买”时调用商户后台接口,后台返回加签后的订单信息字符串。我把主要的处理逻辑写在Service层了,Controller层直接调用就可以,这里就不放Controller层的代码了
生成商户订单的代码,我就不放了,这个根据各自的业务需求来做,生成后订单之后,把订单信息传进来该方法进行处理,返回加签后的字符串,直接返回给APP即可,代码如下:
/**
* 获取支付宝加签后台的订单信息字符串
*
* @param request
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public String getAliPayOrderStr(OrderTest orderTest) {
//最终返回加签之后的,app需要传给支付宝app的订单信息字符串
String orderString = "";
logger.info("==================支付宝下单,商户订单号为:"+orderTest.getOutTradeNo());
//创建商户支付宝订单(因为需要记录每次支付宝支付的记录信息,单独存一个表跟商户订单表关联,以便以后查证)
AlipaymentOrder alipaymentOrder=new AlipaymentOrder();
alipaymentOrder.setClubOrderId(orderTest.getId().toString());//商家订单主键
alipaymentOrder.setOutTradeNo(orderTest.getOutTradeNo());//商户订单号
alipaymentOrder.setTradeStatus((byte) 0);//交易状态
alipaymentOrder.setTotalAmount(Double.parseDouble(orderTest.getTotalAmount()));//订单金额
alipaymentOrder.setReceiptAmount(0.00);//实收金额
alipaymentOrder.setInvoiceAmount(0.00);//开票金额
alipaymentOrder.setBuyerPayAmount(0.00);//付款金额
alipaymentOrder.setRefundFee(0.00); //总退款金额
try{
//实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID,
AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET,
AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE);
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
//业务参数传入,可以传很多,参考API
//model.setPassbackParams(URLEncoder.encode(request.getBody().toString())); //公用参数(附加数据)
model.setBody(orderTest.getBody()); //对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
model.setSubject(orderTest.getSubjecy()); //商品名称
model.setOutTradeNo(orderTest.getOutTradeNo()); //商户订单号(自动生成)
// model.setTimeoutExpress("30m"); //交易超时时间
model.setTotalAmount(orderTest.getTotalAmount()); //支付金额
model.setProductCode("QUICK_MSECURITY_PAY"); //销售产品码(固定值)
ali_request.setBizModel(model);
logger.info("====================异步通知的地址为:"+alipayment.getNotifyUrl());
ali_request.setNotifyUrl(AlipayConfig.notify_url); //异步回调地址(后台)
ali_request.setReturnUrl(AlipayConfig.return_url); //同步回调地址(APP)
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付宝订单信息(预处理)
orderString=alipayTradeAppPayResponse.getBody();//就是orderString 可以直接给APP请求,无需再做处理。
this.createAlipayMentOrder(alipaymentOrder);//创建新的商户支付宝订单
} catch (AlipayApiException e) {
e.printStackTrace();
logger.info("与支付宝交互出错,未能生成订单,请检查代码!");
}
return orderString;
}
2.实现第二个接口:在支付完成之后,支付宝异步通知商户后台订单的付款情况,这个是支付宝每隔一段时间来访问一次的接口,直到你返回success,才会停止访问,这里我分了2个地方进行调用
/**
* 支付宝支付成功后.异步请求该接口
* @param request
* @return
* @throws IOException
*/
@RequestMapping(value="/notify_url",method=RequestMethod.POST)
@ResponseBody
public String notify(HttpServletRequest request,HttpServletResponse response) throws IOException {
logger.info("==================支付宝异步返回支付结果开始");
//1.从支付宝回调的request域中取值
//获取支付宝返回的参数集合
Map aliParams = request.getParameterMap();
//用以存放转化后的参数集合
Map conversionParams = new HashMap();
for (Iterator iter = aliParams.keySet().iterator(); iter.hasNext();) {
String key = iter.next();
String[] values = aliParams.get(key);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "uft-8");
conversionParams.put(key, valueStr);
}
logger.info("==================返回参数集合:"+conversionParams);
String status=alipayMentOrderService.notify(conversionParams);
return status;
}
/**
* 支付宝异步请求逻辑处理
* @param request
* @return
* @throws IOException
*/
public String notify(Map conversionParams){
logger.info("==================支付宝异步请求逻辑处理");
//签名验证(对支付宝返回的数据验证,确定是支付宝返回的)
boolean signVerified = false;
try {
//调用SDK验证签名
signVerified = AlipaySignature.rsaCheckV1(conversionParams, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE);
} catch (AlipayApiException e) {
logger.info("==================验签失败 !");
e.printStackTrace();
}
//对验签进行处理
if (signVerified) {
//验签通过
//获取需要保存的数据
String appId=conversionParams.get("app_id");//支付宝分配给开发者的应用Id
String notifyTime=conversionParams.get("notify_time");//通知时间:yyyy-MM-dd HH:mm:ss
String gmtCreate=conversionParams.get("gmt_create");//交易创建时间:yyyy-MM-dd HH:mm:ss
String gmtPayment=conversionParams.get("gmt_payment");//交易付款时间
String gmtRefund=conversionParams.get("gmt_refund");//交易退款时间
String gmtClose=conversionParams.get("gmt_close");//交易结束时间
String tradeNo=conversionParams.get("trade_no");//支付宝的交易号
String outTradeNo = conversionParams.get("out_trade_no");//获取商户之前传给支付宝的订单号(商户系统的唯一订单号)
String outBizNo=conversionParams.get("out_biz_no");//商户业务号(商户业务ID,主要是退款通知中返回退款申请的流水号)
String buyerLogonId=conversionParams.get("buyer_logon_id");//买家支付宝账号
String sellerId=conversionParams.get("seller_id");//卖家支付宝用户号
String sellerEmail=conversionParams.get("seller_email");//卖家支付宝账号
String totalAmount=conversionParams.get("total_amount");//订单金额:本次交易支付的订单金额,单位为人民币(元)
String receiptAmount=conversionParams.get("receipt_amount");//实收金额:商家在交易中实际收到的款项,单位为元
String invoiceAmount=conversionParams.get("invoice_amount");//开票金额:用户在交易中支付的可开发票的金额
String buyerPayAmount=conversionParams.get("buyer_pay_amount");//付款金额:用户在交易中支付的金额
String tradeStatus = conversionParams.get("trade_status");// 获取交易状态
//支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id)
AlipaymentOrder alipaymentOrder=this.selectByOutTradeNo(outTradeNo);
if(alipaymentOrder!=null&&totalAmount.equals(alipaymentOrder.getTotalAmount().toString())&&AlipayConfig.APPID.equals(appId)){
//修改数据库支付宝订单表(因为要保存每次支付宝返回的信息到数据库里,以便以后查证)
alipaymentOrder.setNotifyTime(dateFormat(notifyTime));
alipaymentOrder.setGmtCreate(dateFormat(gmtCreate));
alipaymentOrder.setGmtPayment(dateFormat(gmtPayment));
alipaymentOrder.setGmtRefund(dateFormat(gmtRefund));
alipaymentOrder.setGmtClose(dateFormat(gmtClose));
alipaymentOrder.setTradeNo(tradeNo);
alipaymentOrder.setOutBizNo(outBizNo);
alipaymentOrder.setBuyerLogonId(buyerLogonId);
alipaymentOrder.setSellerId(sellerId);
alipaymentOrder.setSellerEmail(sellerEmail);
alipaymentOrder.setTotalAmount(Double.parseDouble(totalAmount));
alipaymentOrder.setReceiptAmount(Double.parseDouble(receiptAmount));
alipaymentOrder.setInvoiceAmount(Double.parseDouble(invoiceAmount));
alipaymentOrder.setBuyerPayAmount(Double.parseDouble(buyerPayAmount));
switch (tradeStatus) // 判断交易结果
{
case "TRADE_FINISHED": // 交易结束并不可退款
alipaymentOrder.setTradeStatus((byte) 3);
break;
case "TRADE_SUCCESS": // 交易支付成功
alipaymentOrder.setTradeStatus((byte) 2);
break;
case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
alipaymentOrder.setTradeStatus((byte) 1);
break;
case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
alipaymentOrder.setTradeStatus((byte) 0);
break;
default:
break;
}
int returnResult=this.updateByPrimaryKey(alipaymentOrder); //更新交易表中状态
if(tradeStatus.equals("TRADE_SUCCESS")) { //只处理支付成功的订单: 修改交易表状态,支付成功
if(returnResult>0){
return "success";
}else{
return "fail";
}
}else{
return "fail";
}
}else{
logger.info("==================支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id),不一致!返回fail");
return"fail";
}
} else { //验签不通过
logger.info("==================验签不通过 !");
return "fail";
}
}
3.实现第三个接口:在支付完成之后,跳转回APP时,APP调用商户后台进行最终付款校验。我把主要的处理逻辑写在Service层了,Controller层直接调用就可以,这里就不放Controller层的代码了。
微信支付
调用微信的支付接口,参考微信提供的 api使用了微信的统一下单接口和查询支付状态接口
每个接口需要的参数放入到 map 中使用微信提供的 sdk 转成 XML 字符串,httpClient
远程提交参数和接收结果
具体实现流程
1.二维码:前端使用qrious框架,导入js文件
2.共有10个接口,而我只负责两个
一个请求支付地址接口,另一个查询支付接口
3.用httpClient技术,封装了一个工具类
1.封装请求内容
2通过微信的工具类 将封装参数的map转为xml的字符串
3设置https的请求
4发送post
5接受到返回的xml字符串,通过微信工具类转为map
支付流程
我们用的支付模块是用的微信,当用户提交订单后,我们会根据用户提交的订单,通过第三方的算法生成一个二维码,显示到前台,服务端时刻监控这个二维码, 如果用户到了二维码页面一直未支付,或是关掉了支付页面,我们的代码会一直循环调用微信接口,这样会对程序造成很大的压力。所以我们要加一个时间限制或是循环次数限制,当超过时间或次数时,跳出循环。如果用户成功付款,则api返回过来一个支付成功的状态,我们根据这个成功的状态,做出对该订单的一个后续状态的改变,如果支付失败,则向前台返回一个支付失败的页面,我们是怎么处理之后的日志的问题呢?
(1)在用户下订单时,判断如果为微信支付,就想支付日志表添加一条记录,信息包括支付总金额、订单ID(多个)、用户ID 、下单时间等信息,支付状态为0(未支付)
(2)生成的支付日志对象放入redis中,以用户ID作为key,这样在生成支付二维码时就可以从redis中提取支付日志对象中的金额和订单号。
(3)当用户支付成功后,修改支付日志的支付状态为1(已支付),并记录微信传递给我们的交易流水号。根据订单ID(多个)修改订单的状态为2(已付款)。
修改好相应的日志之后,就可以根据相应的记录做其他的操作了…..
18.分页技术使用什么pagehelper
Maven依赖
这是一个github上的开源项目,可以方便的完成Java web项目中的翻页问题
使用Pageahelper
1.配置拦截器插件
在mybatis的配置文件中配置拦截器插件
2.在java类中使用PageInfo来包装查询结果
public String getEmps(@RequestParam(value = "pn",defaultValue="1")Integer pn,
Model model){
PageHelper .startPage(pn,5);
List list = employeeService.getAll();
//pageinfo里面封装了详细的信息,包括我们查询出来的数据。在构造函数里面的第二个参数,是每页连续显示数量
PageInfo page = new PageInfo(list,5);
model.addAttribute("pageInfo",page);
return "list";
}
使用pageinfo来包装查询出来的结果,再使用model返回给页面。
在页面我们可以使用page.getNativegatpageNums()方法来获取连续显示页面数
19.百度地图api的使用
创建一个名为"allmap"地图实例;
var map = new BMap.Map("allmap");
如何在页面中调用百度地图,直接在你想要插入的页面上调用百度地图代码即可
百度地图调用API地址:http://api.map.baidu.com/lbsapi/creatmap/index.html
1.设置定位中心:直接搜索你要找的位置即可。
调用百度地图代码
2.设置地图:设置地图样式,如大小,显示,功能等。
3.添加标注:添加你要标注的地方,自定义坐标位置
4.获取代码:点击获取代码即可,在你要插入百度地图的地方出入百度地图代码
只要插入部分的代码就行。
20.购物车功能实现session和cookie区别
1. cookie
cookie是由服务器产生,存储在客户端的一段信息。它定义了一种Web服务器在客户端存储和返回信息的机制,cookie文件它包含域、路径、生存期、和由服务器设置的变量值等内容。当用户以后访问同一个Web服务器时,浏览器会把cookie原样发送给服务器。通过让服务器读取原先保存到客户端的信息,网站能够为浏览者提供一系列的方便,例如在线交易过程中标识用户身份、安全要求不高的场合避免用户重复输入名字和密码、门户网站的主页定制、有针对性地投放广告等等。利用cookie的特性,大大扩展了WEB应用程序的功能,不仅可以建立服务器与客户机的联系,因为cookie可以由服务器定制,因此还可以将购物信息生成cookie值存放在客户端,从而实现购物车的功能。用基于cookie的方式实现服务器与浏览器之间的会话或购物车,有以下特点:
cookie存储在客户端,且占用很少的资源,浏览器允许存放300个cookie,每个cookie的大小为4KB,足以满足购物车的要求,同时也减轻了服务器的负荷;
cookie为浏览器所内置,使用方便。即使用户不小心关闭了浏览器窗口,只要在cookie定义的有效期内,购物车中的信息也不会丢失;
cookie不是可执行文件,所以不会以任何方式执行,因此也不会带来病毒或攻击用户的系统;
基于cookie的购物车要求用户浏览器必须支持并设置为启用cookie,否则购物车则失效;
存在着关于cookie侵犯访问者隐私权的争论,因此有些用户会禁止本机的cookie功能。
2. session
session是实现购物车的另一种方法。session提供了可以保存和跟踪用户的状态信息的功能,使当前用户在session中定义的变量和对象能在页面之间共享,但是不能为应用中其他用户所访问,它与cookie最重大的区别是,session将用户在会话期间的私有信息存储在服务器端,提高了安全性。在服务器生成session后,客户端会生成一个sessionid识别号保存在客户端,以保持和服务器的同步。这个sessionid是只读的,如果客户端禁止cookie功能,session会通过在URL中附加参数,或隐含在表单中提交等其他方式在页面间传送。因此利用session实施对用户的管理则更为安全、有效。
同样,利用session也能实现购物车,这种方式的特点是:
session用新的机制保持与客户端的同步,不依赖于客户端设置;
与cookie相比,session是存储在服务器端的信息,因此显得更为安全,因此可将身份标示,购物等信息存储在session中;
session会占用服务器资源,加大服务器端的负载,尤其当并发用户很多时,会生成大量的session,影响服务器的性能;
因为session存储的信息更敏感,而且是以文件形式保存在服务器中,因此仍然存在着安全隐患。
3. 结合数据库的方式
这也是目前较普遍的模式,在这种方式中,数据库承担着存储购物信息的作用,session或cookie则用来跟踪用户。这种方式具有以下特点:
数据库与cookie分别负责记录数据和维持会话,能发挥各自的优势,使安全性和服务器性能都得到了提高;
每一个购物的行为,都要直接建立与数据库的连接,直至对表的操作完成后,连接才释放。当并发用户很多时,会影响数据库的性能,因此,这对数据库的性能提出了更高的要求;
使cookie维持会话有赖客户端的支持。
各种方式的选择:
虽然cookie可用来实现购物车,但必须获得浏览器的支持,再加上它是存储在客户端的信息,极易被获取,所以这也限制了它存储更多,更重要的信息。所以一般cookie只用来维持与服务器的会话,例如国内最大的当当网络书店就是用cookie保持与客户的联系,但是这种方式最大的缺点是如果客户端不支持 cookie就会使购物车失效。
Session 能很好地与交易双方保持会话,可以忽视客户端的设置。在购物车技术中得到了广泛的应用。但session的文件属性使其仍然留有安全隐患。
结合数据库的方式虽然在一定程度上解决了上述的问题,但从上面的例子可以看出:在这种购物流程中涉及到对数据库表的频繁操作,尤其是用户每选购一次商品,都要与数据库进行连接,当用户很多的时候就加大了服务器与数据库的负荷
1、用户浏览系统,获取用户机器的MAC地址
2、如果用户购买物品,添加到数据库里面,同时插入机器的MAC地址,也是用户的ID标示
3、如果用户登录系统,用用户真实的ID,更新当前机器的MAC对应的记录。
4、如果结帐的话,更新用户的id,删除购物车里面的东西
5、用户没有登录,购物车记录根据MAC读取记录,如果登录系统根据用户的ID,读取记录
21.redis的了解
概念:
1、Redis是一个高性能key-value,它是基于内存操作的,它是一个key-value的非关系型数据库。
2、可以作为Nosql数据库,告诉缓存,消息队列的代理。
3、支持的数据类型:字符串,哈希,列表(list),集合,有序集合,位图。
对比:
Memcache是一个纯内存数据库,不能够持久化,只支持String数据类型
优点:
1、读写性能好,但是读的效率高于写的效率,但是可以使用Redis+ssdb+Lua脚本联合使用,因为Redis和ssdb共用一套客户端,即./Redis-cli,同样应用于ssdb中,所以启动Redis后就可以拿到ssdb中的数据,ssdb写的效率大于读的效率,即Redis作为读的,ssdb作为写的
2、支持数据的持久化
3、支持主从复制,主机回自动将数据同步到从机,可以进行读写分离
4、数据结构丰富
缺点:
1、Redis不具备自动容错和恢复功能(从机,主机宕机以后,会导致前端部分读写请求失败,需要等待机器的重启)
Redis的主从复制-->全量复制
1、复制过程中,主机或fork一个子进程对内存做一份快照,并将子进程的内存快照保存为文件,发送给从机,然而在该过程总需要确保主机有足够的空余内存,若快照文件比较大,对集群的服务能力会产生较大的影响
2、在主从复制的过程中有从机新加入集群或者从机和主机出现网络波动(断开连接),都会造成主机和从机之间的一次全量复制,这对实际的系统运营造成了不必要的麻烦
3、Redis较难支持在线扩容,在集群容量达到上限的时候,在线扩容会非常复杂,为解决这一问题,运维人员在系统上线的时候必须确保有足够的空间,否则会对资源造成很大的浪费
Redis的应用场景:
项目中的首页信息要存放在Redis中,因为访问频率高,允许有缓存的数据。
22.微服务
https://cloud.tencent.com/developer/article/1159826
23.solr搜索服务
solr是一个开源搜索平台,用于构建搜索应用程序。 它建立在Lucene(全文搜索引擎)之上。 Solr是企业级的,快速的和高度可扩展的。 使用Solr构建的应用程序非常复杂,可提供高性能。
为了在CNET网络的公司网站上添加搜索功能,Yonik Seely于2004年创建了Solr。并在2006年1月,它成为Apache软件基金会下的一个开源项目。并于2016年发布最新版本Solr 6.0,支持并行SQL查询的执行。
Solr可以和Hadoop一起使用。由于Hadoop处理大量数据,Solr帮助我们从这么大的源中找到所需的信息。不仅限于搜索,Solr也可以用于存储目的。像其他NoSQL数据库一样,它是一种非关系数据存储和处理技术。
总之,Solr是一个可扩展的,可部署,搜索/存储引擎,优化搜索大量以文本为中心的数据。
Apache Solr特点
Solr是Lucene的Java API的包装。因此,使用Solr,可以利用Lucene的所有功能。 让我们来看看Solr的一些最突出的特点 -
Restful APIs − 要与Solr通信,并非一定需要有Java编程技能。相反,您可以使用restful服务与它通信。可使用文件格式(如XML,JSON和.CSV)在Solr中作为输入文档,并以相同的文件格式获取结果。
全文搜索 - Solr提供了全文搜索所需的所有功能,例如令牌,短语,拼写检查,通配符和自动完成。
企业准备 - 根据企业/组织的需要,Solr可以部署在任何类型的系统(大或小),如独立,分布式,云等。
灵活和可扩展 - 通过扩展Java类并相应配置,可以轻松地定制Solr的组件。
NoSQL数据库 - Solr也可以用作大数据量级的NOSQL数据库,可以沿着集群分布搜索任务。
管理界面 - Solr提供了一个易于使用,用户友好,功能强大的用户界面,使用它可以执行所有可能的任务,如管理日志,添加,删除,更新和搜索文档。
高度可扩展 - 在使用Solr与Hadoop时,我们可以通过添加副本来扩展其容量。
以文本为中心并按相关性排序 - Solr主要用于搜索文本文档,结果根据与用户查询的相关性按顺序传送。
与Lucene不同,在使用Apache Solr时,可不需要具有Java编程技能。它提供了一个完整的准备部署服务,以构建一个自动完成的搜索框,Lucene是不提供的。 使用Solr可以扩展,分配和管理大规模(大数据)应用程序的索引。
Lucene在搜索应用程序
Lucene是简单但强大的基于Java的搜索库。 它可以在任何应用程序中用于添加搜索功能。 Lucene是一个可扩展的高性能库,用于索引和搜索几乎任何类型的文本。 Lucene库提供任何搜索应用程序所需的核心操作,例如索引和搜索。
如果有一个具有大量数据的门户网站或平台,那么我们将很可能需要在门户/平台中提取一个搜索引擎从巨大的数据库中提取相关信息。Lucene作为任何搜索应用程序的核心,提供与索引和搜索相关的重要操作。
常用JAVA应用架构
单一应用架构
- 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM) 是关键。
垂直应用架构
- 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC) 是关键。
分布式服务架构
- 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC) 是关键。
流动计算架构
- 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA) 是关键。