All posts by 龙生
必须知道的SQL语句不走索引时的排查利器
前言: 在索引优化时,经常会看到的一句话:如果索引字段出现隐式字符集转换的话,那么索引将失效,进而转为全表扫描,查询效率将大大降低,要避免出现隐式字符集转换; 在此我想问问同学们: 大家知道为什么隐式字符集转换会导致索引失效吗? 实际场景中有没有遇到过隐式字符集转换导致索引失效的场景,具体排查的过程; 本文主线: 由上面的两个问题牵引出了本文的主线; 简单描述下隐式字符集转换导致索引失效的原因 然后模拟实际场景排查隐式字符集转换导致索引失效的过程 隐式字符集转换导致索引失效的原因 MySQL索引的数据结构是 B+Tree,想要走索引查询必须要满足其 最左前缀原则 ,否则无法通过索引树进行查找,只能进行全表扫描; 例如:下面的这个SQL由于在 索引字段 上使用函数进行运算,导致索引失效
1 |
select * from t_user where SUBSTR(name, 1, 2) = '李彤' |
上面的这个SQL怎么改造才能使索引生效呢?如下所示:
1 |
select * from t_user where name like '李彤%' |
通过上面的小例子可以知道,如果在索引字段上使用函数运算,则会导致索引失效,而索引字段的 隐式字符集转换 由于MySQL会自动的在索引字段上加上 转换函数 ,进而会导致索引失效; 那接下来我们就通过模拟的实际场景来具体看看是不是由于MySQL自动给加上了转换函数而导致索引失效的; 模拟场景 + 问题排查 由于导致索引失效的原因有很多,如果自己写的SQL怎么看都没问题,但是通过查看执行计划发现就是没有走索引查询,此时就会让很多人陷入困境,这到底是怎么导致的呢? 此时本文重点将要讲述的工具就要闪亮登场啦: explain extended + show warnings ; 使用这个工具可以将执行的SQL语句的一些扩展信息展示出来,这些扩展信息就包括:MySQL优化时可能会添加上字符集转换函数,使得字符集不匹配的SQL可以正确执行下去; 下面就来具体聊聊 explain extended + show warnings 的使用; 模拟隐式字符集转换的场景: 首先创建两个字符集不一样的表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
CREATE TABLE `t_department` ( `id` int(11) NOT NULL AUTO_INCREMENT, `de_no` varchar(32) NOT NULL, `info` varchar(200) DEFAULT NULL, `de_name` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_de_no` (`de_no`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4; CREATE TABLE `t_employees` ( `id` int(11) NOT NULL AUTO_INCREMENT, `em_no` varchar(32) NOT NULL, `de_no` varchar(32) NOT NULL, `age` int(11) DEFAULT NULL, `info` varchar(200) DEFAULT NULL, `em_name` varchar(200) DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_em_no` (`de_no`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; |
然后使用存储过程构造数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 如果存储过程存在则删除 DROP PROCEDURE IF EXISTS proc_initData; DELIMITER $ # 创建存储过程 CREATE PROCEDURE proc_initData() BEGIN DECLARE i INT DEFAULT 1; WHILE i<=30 DO # 新增数据 INSERT INTO t_employees ( em_no, de_no, info, em_name , age) VALUES ( CONCAT('001', i), '003', 'test11', 'test2', i ); #执行的sql语句 SET i = i+1; END WHILE; END $ # 调用存储过程 CALL proc_initData(); |
注意:在构造数据时,记得将 t_employees 表中的 de_no 字段值构造的 离散些 ,因为如果索引字段值的 区分度很低 的话,那么MyQSL优化器通过采样统计分析时,发现索引查询和全表扫描性能差不多,就会直接进行全表扫描了; 索引失效的查询SQL语句: 将表和数据构造完后,我们使用SQL语句进行查询下,然后再看看其执行计划;
1 2 |
explain select * from t_department a LEFT JOIN t_employees b on a.de_no = b.de_no where a.id = 16 |
其执行计划如下: 发现 t_employees 表中的 de_no 字段有索引,但是没有走索引查询,type=ALL 走的全表扫描,但是通过查看SQL语句发现其没有问题呀,表面看上去都是满足走索引查询的条件呀,排查到这发现遇到了困境,苦恼啊! 还好,通过在网络世界上遨游,最终发现了 explain extended + show warnings 利器,利用它快速发现了索引失效的根本原因,然后快速找到了解决方案; 下面就来聊聊这个利器的具体使用,开森! 使用利器快速排查问题: 注意:explain 后面跟的关键字 EXTENDED(扩展信息) 在MySQL5.7及之后的版本中废弃了,但是该语法仍被识别为向后兼容,所以在5.7版本及后续版本中,可以不用在 explain 后面添加 EXTENDED 了; EXTENDED关键字的具体查阅资料:https://dev.mysql.com/doc/refman/5.7/en/explain-extended.html 具体使用方法如下: ①、首先在MySQL的可视化工具中打开一个 命令列介面 :工具 --> 命令列介面 ②、然后输入下面的SQL并按回车:
1 2 |
explain EXTENDED select * from t_department a LEFT JOIN t_employees b on a.de_no = b.de_no where a.id = 4019; |
③、然后紧接着输入命令 show warnings; 并回车,会出现如下图所示内容: 通过展示出的执行SQL扩展信息,发现MySQL在字符集不一致时自动添加上字符集转换函数,因为是在 索引字段 de_no 上添加的转换函数,所以就导致了索引失效; 而如果我们没看扩展信息的话,那么可能直到我们查看表结构的时候才会发现是由于字符集不一致导致的,这样就会花费很多的时间; 扩展:隐式类型转换 咱们聊完上面的隐式字符集转换导致索引失效的情况,再来简单聊聊另一种 隐式类型转换 导致索引失效的情况; […]
View Details开发人员正从 Java 8 向 Java 11 转移
此前的 Java 社区报告曾指出,Java 8 仍是开发人员使用的主要版本,新版本并未“得宠”。但 Snyk 近期发布的 JVM Ecosystem Report 2021 则指出,开发人员已经逐渐从 Java 8 迁移到了 Java 11。 JVM Ecosystem Report 2021 展示了关于 JVM 生态系统状态的最大年度调查的结果。该调查在 2021 年 2 月和 3 月的六周时间里进行,收集了来自 2000 多名 Java 开发者的回复。 调查结果显示,有 44.1% 的受访者在生产中使用免费的 AdoptOpenJDK 发行版。但 Oracle 仍然是市场上的重要参与者,其 OpenJDK 构建占 28%,商业 Oracle JDK 占 23%。 40% 的调查参与者在生产中使用了一个以上的 Java 版本。升级到 8 版本以上的人也比预料的要多。目前,有 61.5% 的人在生产中使用 Java 11,近 12% 的人使用最新版本,即调查期间的 Java 15。 Snyk 方面在报告中指出,这表明开发人员确实将他们的 Java 版本升级到了 Java 8 以上的版本,有关大多数 Java 开发人员都乐于使用 Java 8 的现象似乎正在慢慢瓦解。不过值得注意的是,仍有一半的 Java 11 用户(目前使用最多的版本)在他们的生产堆栈中使用 Java 8。 从长远来看,虽然 JVM 语言的种类在过去几年中有所增长,但 Java 仍然是最受欢迎的语言。超过 90% 的开发者使用 Java;Kotlin 次之,为 17.7%。 而 JetBrains IntelliJ IDEA […]
View Details国外7个最好的域名注册商推荐
在创建网站之前,最重要的事情就像想好一个令人映像深刻的域名。这可能需要花费很多时间去思考,一旦你确定之后,就需要在域名注册商网站注册这个域名,然后才能开始搭建网站。本文推荐了一些国外知名的域名注册商。 注册其实很简单,但是如何选择域名注册商也需要慎重,以下是你需要注意的问题: 选择域名注册商注意事项 价格:比如,购买时很便宜,但在续费时变得很贵。域名后缀的价格也有所不同,有些注册商可能会提供便宜的.com域名,但.org可能会很贵。 额外费用:比如将域转移到另一个注册商之类的任务也可能需要额外的费用。 额外功能:Whois隐私服务可隐藏你的地址、电话号码和电子邮件地址,否则你可能会收到大量垃圾邮件和电话。这项费用每年高达$11.2,但一些注册商免费提供。 额外的托管服务:大多数域名注册商都提供网络托管服务,也就是用来搭建网站的虚拟主机,但你也可以选择其他托管服务。 客户支持:你可能根本不需要任何帮助,但是如果遇到紧急情况,在线客服的服务质量非常重要! 最好的域名注册商推荐 1. Domain.com +优点 购买域名即可免费建立网站 -缺点 最佳价格仅限于较长的订阅时间 Domain.com的主要用户群体时中小企业。它提供了最受欢迎的顶级域名和超过25个国家/地区代码顶级域名,并出售高级域名。 该公司是全球最大的域名注册商之一,拥有近二十年运营历史,在网络托管方面进行了扩展,现在提供了许多产品,包括网站构建器,全面的设计服务和网络托管。 你需要支付6.99美元的价格来保护隐私,这个价格还算合理。此外,你还可以添加电子邮件,网络托管,SSL证书和恶意软件保护,而这些都不是强制性的。你可以使用其基本的网站构建器,该构建器在每个域中都是免费的。这个网站构建器包含了许多有用的功能:大量的模板、SEO工具、PayPal集成。 提供24/7聊天客服、电子邮件和电话支持。Domain.com可能没有提供最便宜的价格,但它在各方面都比较均衡。 2. Bluehost +优点 域名定价性价比高 提供配套的托管服务 良好的客户支持 如果你正在寻找信誉良好,价格合理的域名注册商,那么Bluehost是最好的选择之一。例如,Bluehost每年以12.99美元的价格出售.com和.org域名,同时你可以使用配套的虚拟主机来搭建网站,这节省了你寻找虚拟主机的麻烦。 从Bluehost购买虚拟主机的用户将获得一个免费域名以及网站构建器,该订阅计划的费用通常在每月$2.75左右。Bluehost具有高性能和高质量实时聊天支持,可以帮助你解决任何问题。如果你对服务不满意,则可以享受30天的退款保证。 在注册你喜欢的域名时,Bluehost提供了一个简易的搜索系统,可以清楚地显示你的查询结果。此外,你可以在购买时添加域名隐私和保护,这样你的个人详细信息和个人资料都将通过Whois保持隐藏。 Bluehost还为你提供了自动续订的功能,这避免了你手动续订域名的麻烦,并且确保不会因意外而导致域名被抢注。 3.GoDaddy +优点 提供托管服务 提供在线支持 -缺点 价格昂贵 网络巨头GoDaddy是全球最大的域名注册商,目前为全球1700万客户管理着超过7500万个域名。 该公司以其低价标题价格而闻名,例如,.mobi域名在第一年的价格为7.17美元。.com和.org的起始价格相对较低,起始价格分别为$12.17和$10.17。但是要注意,它并没有看起来那么便宜。 首先GoDaddy的起始价格仅在你提前两年付款后才适用,而第二年的价格明显更高(.com升至$18.17,.uk和.co.uk域名分别为$12,.co,.org和.mobi高得离谱)。 Godaddy现在提供免费的基本Whois隐私,这是以前的可选服务。它以最简单的形式在Godaddy的WHOIS目录中编辑你的姓名、地址、电话号码和电子邮件,并阻止与域名相关的垃圾邮件。 4.Hover +优点 包含免费的Whois隐私 价格公道 -缺点 在线客服不是24/7 Hover是一家受欢迎的域名注册商,该公司还运营着eNom和域名转售平台OpenSRS。 Hover的网站清晰明了。域名定价页面允许你在支付之前检查注册费用,也可以使用搜索框立即找到顶级域名。 默认情况下,结果页面显示你可以注册的每个域及其价格,从而使你可以滚动浏览和阅读大量内容。 价格非常合理,.com域名第一年的价格为12.99美元,.org的价格为13.99美元,.mobi的价格为19.99美元。虽然你可能在其他地方发现较低的价格,但是Hover性价比非常高。 在Whois隐私方面有巨大优势,只要该域名由Hover管理,它就会免费提供。 如果你有任何疑问,可以通过电子邮件和聊天获得支持,尽管不是24/7。工作时间为周一至周五上午8点至晚上8点(美国东部时间),周末中午12点至下午5点。 5. Dynadot +优点 强大的搜索工具 便宜的价格 大量免费附加功能 Dynadot提供免费的附加功能。网站构建器使你可以构建和托管一个简单的响应式网站。如果你想将访问者重定向到其他地方,则可以使用免费的域名转发。DNS支持允许创建50个子域记录,10个电子邮件地址以及MX和TXT记录。 Dynadot的支持并不总是那么优秀,实时聊天有时回复非常慢。但是该网站提供一个公开的论坛,任何人都可以浏览常见问题,并查看Dynadot客户的满意程度。 6. Namecheap +优点 顾名思义,它很便宜 免费的Whois隐私 实时聊天支持 Namecheap成立于2000年,是一个受欢迎的域名注册商和网络托管商,目前管理着超过500万个域名。 Namecheap的网站允许搜索单个域名或同时搜索50个域名。如果该域名被占用,则可以查看Whois记录或提供从当前用户购买域名的建议。 .com域名的价格通常非常合理,为$7.38(续签价格为$12.98),.org为$8.98(续订价格为$14.98)。有一些特殊的域名,起使价格低至$0.48:.site .website .space .pw .press .host .tech .online .fun以及其他)。 除此之外,你可以免费获得WhoisGuard域名隐私保护。 Namecheap的帐单简单明了,并且在你的Namecheap购物车中清楚地描述了当前价格和续订价格,并且自动续订功能会开启。但是,如果你不了解某些内容,那么只需单击两下就可以找到有用的常见问题页面和实时聊天。 7. Google Domains +优点 出色的客户支持 易于使用 -缺点 价格高于平均水平 寻找域名注册商可能会涉及很多麻烦,选择一些知名度很低的注册商可能会让你遭受损失,那么Google Domains可能是个不错的选择。 Google Domains是Google的轻量级域名注册机构,这是一个简单的提供商,他们注重简单易用。 与其他注册商不同的是,Google […]
View Details在WebApi 中使用AutoFac
参考文档 https://www.cnblogs.com/htsboke/p/10956807.html https://www.cnblogs.com/lenmom/p/8510572.html https://www.cnblogs.com/yaopengfei/p/9479268.html 在WebApi项目中使用AutoFac,结构如下: 首先在Api项目当中引用AutoFac包,如下图所示: 仓储类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public interface IUsersRepository { int GetUserIsExists(UsersEntity criteria); } public class UsersRepository : IUsersRepository { Logger log = LogManager.GetLogger("UsersRepository"); /// <summary> /// 获取用户是否存在 /// </summary> /// <param name="criteria"></param> /// <returns></returns> public int GetUserIsExists(UsersEntity criteria) { string sql = "。。。。"; try { //查询sql代码,此处省略。。。。 } catch (Exception ex) { log.Fatal(ex, "获取用户是否存在异常:{0},SQL:{1}", ex.Message, sql); return 0; } } } |
服务类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//接口 public interface IUsersService { int GetUserIsExists(UsersEntity criteria); } //实现类 public class UsersService : IUsersService { private readonly IUsersRepository _usersrepository; public UsersService(IUsersRepository usersrepository) //通过构造函数注入 { _usersrepository = usersrepository; } /// <summary> /// 获取用户是否存在 /// </summary> /// <param name="criteria"></param> /// <returns></returns> public int GetUserIsExists(UsersEntity criteria) { return _usersrepository.GetUserIsExists(criteria); } } |
在Api接口项目中创建一个AutoFac工具类:AutofacUtil.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
public class AutofacUtil { private static IContainer _container; public static void ConfigureContainer() { #region AutoFac IOC容器 var builder = new ContainerBuilder(); try { //builder.RegisterControllers(Assembly.GetCallingAssembly()); //注册mvc控制器 需要引用package Autofac.Mvc //builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired(); //支持Api控制器属性注入 builder.RegisterApiControllers(Assembly.GetCallingAssembly()); //注册所有api控制器 构造函数注入 需要引用package Autofac.WebApi //注册程序集 #region Service var assemblysServices = Assembly.Load("WebApi.Service"); builder.RegisterAssemblyTypes(assemblysServices) .AsImplementedInterfaces() .InstancePerDependency(); #endregion #region Repository var assemblysRepository = Assembly.Load("WebApi.Repository"); builder.RegisterAssemblyTypes(assemblysRepository) .AsImplementedInterfaces() .InstancePerDependency(); #endregion _container = builder.Build(); //创建依赖注入 //设置MVC依赖注入 //DependencyResolver.SetResolver(new AutofacDependencyResolver(_container)); //设置WebApi依赖注入 GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(_container); } catch (Exception ex) { throw new Exception(ex.Message + "\n" + ex.InnerException); } #endregion } /// <summary> /// 从Autofac容器获取对象 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static T GetFromFac<T>() { return _container.Resolve<T>(); } } |
在 Global.asax.cs 全局中注册一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //AutoFac 注册 AutofacUtil.ConfigureContainer(); } } |
最后在接口中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class UsersController : ApiController { private readonly IUsersService _usersService; public UsersController(IUsersService usersService) //使用构造函数注入 { _usersService = usersService; } /// <summary> /// 获取用户是否存在 /// </summary> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> [HttpPost] public IHttpActionResult GetUserIsExists(string username, string password) { //验证是否存在当前用户 var obModel = new UsersEntity() { Username = username, Password = Encryption.MD5(password) }; var getresult = _usersService.GetUserIsExists(obModel); return Json(new { isexists = getresult > 0 }); } } |
测试结果: 最后:如果出现 未将对象引用的实例 的错误,检查一下是否引用相应的dll程序集了。 from:https://www.cnblogs.com/peterzhang123/p/12808922.html
View DetailsC# WebRequest请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
using System; using System.IO; using System.Net; using System.Text; namespace w3cnet.Utils { /// <summary> /// Web请求相关 /// </summary> public class WebUtil { /// <summary> /// 获取公网url内容 /// </summary> /// <param name="url"></param> /// <returns></returns> public static string GetPage(string url) { return RequestGet(url); } /// <summary> /// Post请求 /// </summary> /// <returns></returns> public static string RequestGet(string url) { //请求 var request = (HttpWebRequest)WebRequest.Create(url); request.Timeout = 3600000; //3600秒 //接收 var response = request.GetResponse(); var responseStream = response.GetResponseStream(); if (responseStream != null) { var reader = new StreamReader(responseStream); var result = reader.ReadToEnd(); reader.Close(); response.Close(); return result; } return string.Empty; } /// <summary> /// Post请求 /// </summary> /// <returns></returns> public static string RequestPost(string url, string data = null, Encoding encoding = null) { if (encoding == null) encoding = Encoding.UTF8; //请求 var request = (HttpWebRequest)WebRequest.Create(url); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; //参数 if (!string.IsNullOrEmpty(data)) { var byteArray = encoding.GetBytes(data); request.ContentLength = byteArray.Length; var requestStream = request.GetRequestStream(); requestStream.Write(byteArray, 0, byteArray.Length); requestStream.Close(); } //接收 var response = request.GetResponse(); var responseStream = response.GetResponseStream(); if (responseStream != null) { var reader = new StreamReader(responseStream, encoding); var result = reader.ReadToEnd(); reader.Close(); response.Close(); return result; } return string.Empty; } } } |
View Details
ABP(3.x)切换为MySQL
在EntityFrameworkCore项目下移除包Microsoft.EntityFrameworkCore.SqlServer 在EntityFrameworkCore项目下添加包Pomelo.EntityFrameworkCore.MySql v3.2.4 修改xxxDbContextConfigurer中的 删除EntityFraweorkCore下面的迁移文件夹 EntityFraweorkCore和WebHost的appsettings.json
1 2 3 |
"ConnectionStrings": { "Default": "server=127.0.0.1;database=abp;uid=root;pwd=123456;charset=utf8mb4;" }, |
添加数据迁移VS菜单:Tools -> NuGet Package Manager -> Package Manager Console运行以下命令:
1 |
Add-Migration DbName |
Update-Database Remove-Migration from:https://blog.csdn.net/qq_40353040/article/details/108770993
View Detailsjava.security.egd
SecureRandom在java各种组件中使用广泛,可以可靠的产生随机数。但在大量产生随机数的场景下,性能会较低。这时可以使用"-Djava.security.egd=file:/dev/./urandom"加快随机数产生过程。 以产生uuid的时候使用nextBytes产生随机数为入口,xSecureRandom的代码逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public static UUID randomUUID() { SecureRandom ng =Holder.numberGenerator; byte[] randomBytes = newbyte[16]; ng.nextBytes(randomBytes); randomBytes[6] &= 0x0f; /* clear version */ randomBytes[6] |=0x40; /* set to version 4 */ randomBytes[8] &= 0x3f; /* clear variant */ randomBytes[8] |=0x80; /* set to IETF variant */ return newUUID(randomBytes); } |
使用了SecureRandom.next*的方法。 在使用SecureRandom产生下一个随机数的时候调用nextLong或者nextBytes,最终会调用SecureRandom的nextBytes。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public long nextLong() { // it's okay that the bottom wordremains signed. return ((long)(next(32)) << 32)+ next(32); } final protected int next(int numBits) { int numBytes = (numBits+7)/8; byte b[] = new byte[numBytes]; int next = 0; nextBytes(b); for (int i = 0; i < numBytes; i++) next = (next << 8)+ (b[i] & 0xFF); return next >>> (numBytes*8 -numBits); } |
而nextBytes是一个同步的方法,在多线程使用时,可能会产生性能瓶颈。
1 2 3 4 5 |
synchronized public void nextBytes(byte[] bytes) { secureRandomSpi.engineNextBytes(bytes); } |
secureRandomSpi被初始化为sun.security.provider.SecureRandom secureRandomSpi是SecureRandom.NativePRNG的一个实例。 使用jvm参数-Djava.security.debug=all ,可以打印securityprovider列表,从中可以看出,SecureRandom.NativePRNG由sun.security.provider.NativePRNG提供服务。 Provider: Set SUN provider property[SecureRandom.NativePRNG/sun.security.provider.NativePRNG] 分析openjdk的源码,NativePRNG.engineNextBytes调用了NativePRNG.RandomIO.ensureBufferValid,而ensureBufferValid直接从urandom读取数据:
1 2 3 4 5 6 7 8 9 |
private void ensureBufferValid() throws IOException { ... readFully(urandomIn, urandomBuffer); ... } |
通过测试可以发现,hotspot需要使用配置项"-Djava.security.egd=file:/dev/./urandom"才能从urandom读取数据,这里openjdk做了优化,直接从urandom读取数据。 /dev/random在产生大量随机数的时候比/dev/urandom慢,所以,建议在大量使用随机数的时候,将随机数发生器指定为/dev/./urandom。 转自:https://blog.51cto.com/leo01/1795447 from:https://blog.csdn.net/wangooo/article/details/109139129
View Details禁用NGINX中的TLS 1.0
我有一个NGINX作为我们网站的反向代理,并且运作良好.对于需要ssl的网站,我遵循 raymii.org以确保尽可能强大的SSLLabs分数.其中一个站点需要符合PCI DSS,但基于最新的TrustWave扫描现在因为启用了TLS 1.0而失败.在nginx.conf中的http级别我有:
1 |
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; |
对于我有的特定服务器:
1 |
ssl_protocols TLSv1.1 TLSv1.2; |
我已经更改了密码,将事情从http级别移到了每个ssl站点服务器,但不管我运行的是什么:
1 |
openssl s_client -connect www.example.com:443 -tls1 |
我获得了TLS 1.0的有效连接. SSLLabs将网站的nginx设置为A但是使用TLS 1.0,所以我相信其余的设置是正确的,它不会关闭TLS 1.0. 关于我可能缺少什么的想法?
1 2 3 4 5 6 7 |
openssl version -a OpenSSL 1.0.1f 6 Jan 2014 built on: Thu Jun 11 15:28:12 UTC 2015 platform: debian-amd64 nginx -v nginx version: nginx/1.8.0 |
这里的问题是TLS协商的服务器名称指示部分是在协商连接本身之后完成的.并且在连接协商期间协商协议.如果将该虚拟主机配置为没有与其关联的其他虚拟主机的服务器上的IP地址,则可能无法为该虚拟主机强制执行TLS v1.0.因此,nginx将根据IP地址知道不允许TLS v 1.0. from:http://www.voidcn.com/article/p-vufkevsw-btw.html
View Details如何用Docker打包Springboot的四种方式
一:最基本的Dockerfile构建Springboot项目 在pom同目录下新建Dockerfile
1 2 3 4 |
FROM openjdk:8-jdk-alpine VOLUME /tmp COPY target/*.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"] |
将Springboot使用Maven打包,在终端中传参进去,进行构建Images。
1 2 3 |
docker build --build-arg=target/*.jar -t demo1/app . #构建完成后启动容器 docker run -p 8080:8080 demo1/app |
给Springboot启动时候传环境变量或者shell参数
1 2 3 4 5 |
FROM openjdk:8-jdk-alpine VOLUME /tmp ARG JAR_FILE COPY ${JAR_FILE} app.jar ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar ${0} ${@}"] |
指定JVM参数和shell参数
1 |
docker run -p 9000:9000 -e "JAVA_OPTS=-Ddebug -Xmx128m" demo1/app --server.port=9000 |
二:使用Idea可视化管理docker, 构建Springboot容器 这个在我另外一篇文章有讲到,都比较简单,有啥问题请留言。 三:使用Maven插件自动化构建Image https://github.com/spotify/dockerfile-maven ,能力强的可以直接去看README.md。 简单介绍一下这个插件做啥用的 通过pom配置docker构建Image过程,参数等 。 封装了自动化build,push,run等Maven命令 。 * 需要依赖Dockerfile,Dockerfile与pom.xml位于同一个目录下。 在pom.xml同目录下创建Dockerfile
1 2 3 4 |
FROM openjdk:8-jre ENTRYPOINT ["/usr/bin/java", "-jar", "/usr/share/myservice/app.jar"] ARG JAR_FILE ADD target/${JAR_FILE} /usr/share/myservice/app.jar |
在pom中添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.4.13</version> <executions> <execution> <id>default</id> <goals> <goal>build</goal> <goal>push</goal> </goals> </execution> </executions> <configuration> <!--你需要配置的地方--> <!--指定仓库名/镜像名--> <repository>myrep/${project.artifactId}</repository> <!--指定tag --> <tag>${project.version}</tag> <buildArgs> <!--指定参数jar--> <JAR_FILE>${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> </plugin> |
最后在Idea Maven插件Plugins点击docker:build即可。或者输入命令
1 |
mvn com.spotify:dockerfile-maven-plugin:build |
四:使用Google的Maven插件进行容器管理(重头戏) Google开源项目Jib,对比上面那个插件Jib的Start数为7.8k,dockerfile-maven 为2.4k。 maven 插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>1.6.0</version> <configuration> <!--配置基本镜像--> <from> <image>openjdk:8-jdk-alpine</image> </from> <!--配置最终推送的地址,仓库名,镜像名--> <to> <image>registry.hub.docker.com/maoduntt/test</image> </to> </configuration> <!--绑定到maven lifecicle--> <executions> <execution> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> |
配置maven docker hub账户和密码,在maven settings.xml中添加
1 2 3 4 5 |
<server> <id>registry.hub.docker.com</id> <username>你的dockerhub用户名</username> <password>你的dockerhub密码</password> </server> |
在idea maven插件中点击或者maven命令 mvn compile jib:buildTar 可以看到推送远程成功,Jib不需要写Dockerfile只需要你在插件中定义构建类型,所以使用时请多参考github的文档。最后现在也有这种开源的容器云平台,可以去了解下。 from:https://zhuanlan.zhihu.com/p/89161347
View DetailsSpringBoot 部署 docker 打包镜像
环境: 1、代码编写工具:IDEA 2、打包:maven 3、docker 4、linux 7、JDK1.8 8、Xshell 9、Xftp 第一步:使用idea创建简单的springboot项目 第二步:设置项目生成jar包(两种方式) 1、修改pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>ordinary</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ordinary</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> |
2、或者在生成项目的时候 可以选择jar和war 第三步:使用maven 生成包 (使用idea不用命令 直接界面操作就可以(如果Maven设置没问题 直接就可以生成 包 log会提示生成后的文件目录)) 第四步:docker概念 1、docker:最早是dotCloud公司出品的一套容器管理工具,但后来Docker慢慢火起来了,连公司名字都从dotCloud改成Docker。 2、dockerfile:它是Docker镜像的描述文件,可以理解成火箭发射的A、B、C、D……的步骤。 3、docker镜像:通过Dockerfile做出来的,包含操作系统基础文件和软件运行环境,它使用分层的存储方式。 4、docker容器:是运行起来的镜像,简单理解,Docker镜像相当于程序,容器相当于进程。 第四步:dockerfile指令 Dockerfile由多条指令组成,每条指令在编译镜像时执行相应的程序完成某些功能,由指令+参数组成,以逗号分隔,#作为注释起始符,虽说指令不区分大小写,但是一般指令使用大些,参数使用小写 第五步:dockerfile文件例子(我只是简单将springboot项目生成docker镜像没有什么多余配置) TODO:有一点需要注意的地方就是dockerfile文件没有任何后缀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Pull base image FROM java:8 MAINTAINER yihj "yihj@yinghaikeji.com" VOLUME /tmp # 添加 ADD ordinary.jar app.jar RUN bash -c 'touch /app.jar' # Define default command. ENTRYPOINT ["java","-Dspring.profiles.active=online","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] #设置时区 RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone |
第六步:将dockerfile文件和生成好的jar 使用ftp工具上传到linux服务器 随便找个文件夹 放进去 jar和dockerfile在同级目录下 第七步:使用 docker build -t ordinary:v1.0 . TODO: 1、 最后面的这个 . 代表在当前目录下面寻找 dockerfile 文件 2、ordinary 镜像名字 3、v1.0版本 第八步:查看镜像及启动 1、使用docker images 来查看生成的镜像 2、使用docker create 来创建容器 docker run 来创建并且运行容器 3、也可以使用docker logs 容器名 --tail 100 -f 来查看项目启动日志 看项目是否启动 3、如果上面步骤一切正常 可以直接调用IP加端口来访问项目 通过Docker run命令定义Spring Profile 可以将spring profile作为环境变量传递给docker run命令,使用 -e 标记。 例如 -e “SPRING_PROFILES_ACTIVE=dev”会将dev profile传递给Docker容器 docker run -d -p 8080:8080 -e "SPRING_PROFILES_ACTIVE=dev" […]
View Details