首页 文章 精选 留言 我的

精选列表

搜索[静态网站托管],共10000篇文章
优秀的个人博客,低调大师

一次网站性能排查实录

接到一个求助电话,说是有个阿里云上的服务器,有性能瓶颈,但又没有什么具体的数据,只是说偶尔客户端有少数连接不上,或者连接会突然中断。我的天,最怕这种状况了,还得自己去找问题表现是什么,再去找什么原因所致。----懒人可直接点此处,不必辛苦看文字 因为是线上的环境,得分两步进行。先检查,看系统或者应用存在什么明显的印迹,分析原因和处理方法;协商什么时候可以处理,处理风险是什么。 怕影响用户体验,白天还不让搞。月黑风高,家里有没暖气啊(用一块闲置显卡挖矿取暖),没办法,只好熬夜苦战一番。 通过了解,该云主机带宽12M,其他方面的配置,我自己登录看就好。 既然收到带宽,就先看它了,看了好一阵,也不到10M嘛。 其他资源配置为: (1)cpu 8core (2)内存 16G (3)硬盘 50G系统加120G外挂空间。 其中系统负载一直稳定不高,IO也还过得去,系统日志也无明显的报错信息。查看网络状态,TIME_WAIT相对于ESTABLESHED来说,高了不少(因为文章为事后所写,无法再重现了)。根据经验,大致可以判断引起TIME_OUT高的原因主要有两种:一种是系统参数(sysctl.conf)设置,另一种就是应用服务配置。前一种比较好办,乘访问量少的时候,偷偷的修改/etc/sysctl.conf(修改前一定要记得备份哟),然后重载 sysctl -p,再执行netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}',发现TIME_OUT的数量并没有减少多少。由此可知,最大的可能就在系统上边的应用。 当然,作为系统管理员,不可能啥都去问人家,你上边安装了什么,都在啥目录啊,程序之间都什么关联?只要有系统登录权限,哪能难住咱们呢!敲ps auxww |more 加一点点耐心,能查个八九不离十。不就是一个nginx + php + mysql 嘛,挨个检查,应该能查到问题所在。这一回车,好多php进程呢,屏幕翻滚了好几屏。心想,没那么多连接数,跑那么多进程干啥呢?做过过滤,数一下php进程数,好家伙301个。 我有个习惯,喜欢从后边往前边查。于是就从数据库开查,show processlist,没线程连接上来,奇怪了啊。还以为自己眼花,又执行了好几遍,还是这样。检查mysql错误日志,数据目录,毫无所获,难道不是用这个mysql?吃个橘子压压惊,查看网络状态,看它连接到哪里去了。 居然没用本机的mysql(不知道装这个干啥),赶紧电话问,说是购买了阿里云的mysql服务,好吧,看来这个就不用查了。 接下来,该看看nginx了。这一查,还真找到几个不顺眼的地方。按照我的习惯,如果系统上要运行多个web站点,一般都会用明确包含的方式对配置文件进行书写,这样做的好处是,看到主配置文件,就知道有多少站点;而且做维护的时候,如果某些站点要临时变更或者变更后整个web服务启动不了,就可以通过注释掉相关的那行包含(include)语句。最不喜欢谁用什么 include *.conf,你写起来省事,可是后边维护就不那么省事。 没办法,我认了。再进具体包含文件的目录,随机打开一个,截取一段如下: 包含里边嵌套包含,那好,我们就看看它这个包含里边写的啥: [root@iZm5e64s4c3fznr1kxv1qaZ conf]# more enable-php.conf location ~ [^/]\.php(/|$) { try_files $uri =404; fastcgi_pass unix:/tmp/php-cgi.sock; fastcgi_index index.php; include fastcgi.conf 又有一个嵌套,狂晕啊!继续看这个fastcgi.conf,就是一个fastsgi_params文件的副本。 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;fastcgi_param QUERY_STRING $query_string;fastcgi_param REQUEST_METHOD $request_method;fastcgi_param CONTENT_TYPE $content_type;fastcgi_param CONTENT_LENGTH $content_length;fastcgi_param SCRIPT_NAME $fastcgi_script_name;fastcgi_param REQUEST_URI $request_uri;fastcgi_param DOCUMENT_URI $document_uri;fastcgi_param DOCUMENT_ROOT $document_root;fastcgi_param SERVER_PROTOCOL $server_protocol;fastcgi_param REQUEST_SCHEME $scheme;fastcgi_param HTTPS $https if_not_empty;fastcgi_param GATEWAY_INTERFACE CGI/1.1;fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;fastcgi_param REMOTE_ADDR $remote_addr;fastcgi_param REMOTE_PORT $remote_port;fastcgi_param SERVER_ADDR $server_addr;fastcgi_param SERVER_PORT $server_port;fastcgi_param SERVER_NAME $server_name;# PHP only, required if PHP was built with --enable-force-cgi-redirectfastcgi_param REDIRECT_STATUS 200; 各位看官,再往下看这个文件,又来了个fastcgi_pass 127.0.0.1 ,是要用什么方式关联php呢?前边那个include指定的"fastcgi_pass unix:/tmp/php-cgi.sock;" 后边又跟这么一个是什么用意? 看到没有,这个fastcgi_pass 127.0.0.1 根本没起作用啊,可能是从网上找的,胡乱粘贴一通。另外,也从php那边印证一下,确认服务是否有tcp 9000端口处于监听状态。 [root@iZm5e64s4c3fznr1kxv1qaZ ~]# netstat -anp|grep 9000|wc -l|grep -v grep0 打开其它站点配置文件,全是这个搞法。得统一做规范,方便自己也是方便他人嘛!不过乱归乱,还是能正常运行,nginx本身也不会对性能产生太大的影响。由这里可以推断,php估计也是胡乱从别的地方复制过来的配置,其内容如下: [global]pid = /usr/local/php/var/run/php-fpm.piderror_log = /usr/local/php/var/log/php-fpm.loglog_level = notice[www]listen = /tmp/php-cgi.socklisten.backlog = -1listen.allowed_clients = 127.0.0.1listen.owner = wwwlisten.group = wwwlisten.mode = 0666user = wwwgroup = wwwpm = dynamicpm.max_children = 300pm.start_servers = 80pm.min_spare_servers = 80pm.max_spare_servers = 300access.log = /mnt/log/phplog/$pool.access.logaccess.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"pm.max_requests = 1024request_terminate_timeout = 0request_slowlog_timeout = 120slowlog = /var/log/slow.log 这几个选项值,直接与性能相关。查看php进程数,一直就是设定的数量300(主进程不算)。最大300,而且一直维持不变,新来的请求无法获得新的进程,于是就只好杀掉已经运行的(有可能用户的连接还在),类似于自杀,可以从php的日志里可以看到大量的进程自杀消息。 一般情况下,应该把最大子进程数(pm.max_children = 300)设置得大一些,最大请求数(pm.max_requests)也需要设置大一些。 把这些问题,记录好汇总以后,电话跟其他人沟通,得到答复是等相关人等在线的时候进行修正,万一业务上有问题,可以一起协助处理,但得等两天以后。欲知详细的操作过程,调整后的效果,请猛戳此处即可。

优秀的个人博客,低调大师

网站动态属性的一个架构

睡不着,讲讲最近做的一个项目的架构的一部分吧,这是一个项目管理系统,支持动态属性,也就是说一个资料 –例如“项目”、“任务”就是资料,资料的属性 –例如“名称”、“时间”都是可以在系统运行时动态增删改的。 本文就讲一讲在.NET和SQL Server里实现动态属性的方法,虽然演示代码都是C#,但我相信可以很容易的移植到Java中。 首先定义几个名词: 资料–是对于系统最终用户来说其要维护的数据,例如“项目”、“任务”信息等。 属性–即资料的一个方面的数据,或者称作字段,在C#代码里应该就是一个Property。 元数据–是解释属性的方式,有时我也会把它称作元属性。 属性和元数据的关系呢,可以参照Excel的实现来理解,好比说我们在一个单元格里输入了一个数据,实际上我们是输入了一个字符串,假设是“1”,当我们设置Excel使用“数字”格式呈现时,那用户在单元格里实际看到的是“1.0”,当我们设置Excel使用“日期”格式呈现时,那用户在单元格里看到的可能就是“1900-1-1”。这里,字符串“1”就是属性,而元数据实际上就类似Excel里的格式。 对于资料来说,它只保存一个属性列表,而属性有一个外键指向定义其格式的元数据,下面是资料、属性和元数据的C#定义: 资料 1 public classGenericDynamicPropertiesEntity:IDynamicPropertiesTable,ISupportDefaultProperties 2{ 3 publicGenericDynamicPropertiesEntity() 4{ 5Properties= newList<Property>(); 6 this.FillDefaultProperties(); 7} 8 9 public stringGet( stringname) 10{ 11 varproperty= this.Property(name, false); 12 if(property!= null) 13{ 14 returnproperty.Value; 15} 16 else 17{ 18 return null; 19} 20} 21 22 publicPropertyGet(MetaPropertymeta) 23{ 24 varproperty= this.Property(meta.Title, false); 25 if(property!= null) 26{ 27 return this.Property(meta.Title, false); 28} 29 else 30{ 31 return null; 32} 33} 34 public voidSet( stringname, stringvalue) 35{ 36 varproperty= this.Property(name, true); 37 if(property.Meta.Valid(value)) 38property.Value=value; 39 else 40 throw newInvalidValueException( string.Format( " 字段\"{0}\"的值\"{1}\"无效,字段\"{0}\"的类型是\"{2}\",期望值的格式是\"{3}\" ", 41name,value,property.Meta.Type,property.Meta.ExpectedFormat)); 42} 43 44 public voidSet( stringname, doublevalue) 45{ 46 varproperty= this.Property(name, true); 47 if(property.Meta.Valid(value)) 48property.Value=value.ToString(); 49 else 50 throw newInvalidValueException( string.Format( " 字段\"{0}\"的值\"{1}\"无效,字段\"{0}\"的类型是\"{2}\",期望值的格式是\"{3}\" ", 51name,value,property.Meta.Type,property.Meta.ExpectedFormat)); 52} 53 54 publicList<Property>Properties{ get; private set;} 55 56[DataMember] 57 publicGuidId{ get; set;} 58 59 public staticTNew<T>() whereT:GenericDynamicPropertiesEntity, new() 60{ 61 return newT() 62{ 63Id=Guid.NewGuid() 64}; 65} 66 67 protected voidSetClassValue<T>( stringpropertyName,Tmember,Tvalue) 68{ 69member=value; 70Set(propertyName,value!= null?value.ToJson(): null); 71} 72 73 protected voidSetNullableDateTime<T>( stringpropertyName,T?member,T?value) whereT: struct 74{ 75member=value; 76Set(propertyName,value.HasValue?value.Value.ToString(): null); 77} 78 79 protected voidSetDateTime( stringpropertyName,DateTimemember,DateTimevalue) 80{ 81member=value; 82Set(propertyName,value.ToString()); 83} 84 85 protected voidSetSingle( stringpropertyName, floatmember, floatvalue) 86{ 87member=value; 88Set(propertyName,value); 89} 90 91 protected voidSetPrimeValue<T>( stringpropertyName,Tmember,Tvalue) whereT: struct 92{ 93member=value; 94Set(propertyName,value.ToString()); 95} 96 97 protectedDateTime?GetNullableDateTime( stringpropertyName,DateTime?date) 98{ 99 if(!date.HasValue) 100{ 101 varvalue=Get(propertyName); 102 if(value!= null) 103{ 104date=DateTime.Parse(value); 105} 106} 107 108 returndate; 109} 110 111 protected floatGetSingle( stringpropertyName, floatmember) 112{ 113 if( float.IsNaN(member)) 114{ 115 varproperty= this.Property(propertyName, false); 116 if(property!= null) 117{ 118member=Single.Parse(property.Value); 119} 120} 121 122 returnmember; 123} 124 125 protectedDateTimeGetDateTime( stringpropertyName,DateTimemember) 126{ 127 if(member==DateTime.MinValue) 128{ 129 varvalue=Get(propertyName); 130 if(value!= null) 131{ 132member=DateTime.Parse(value); 133 returnmember; 134} 135 else 136{ 137 throw newPropertyNotFoundException( string.Format( " 在Id为\"{0}\"的对象里找不到名为\"{1}\"的属性! ",Id,propertyName)); 138} 139} 140 else 141{ 142 returnmember; 143} 144} 145 146 publicDateTime?ClosedDate 147{ 148 get; 149 set; 150} 151 152 publicDateTimeOpenDate 153{ 154 get; 155 set; 156} 157 158 publicDateTimeLastModified 159{ 160 get; 161 set; 162} 163 164 public stringCreator 165{ 166 get; 167 set; 168} 169 170 public stringLastModifiedBy 171{ 172 get; 173 set; 174} 175} 属性 1 /// <summary> 2 /// 资料的属性 3 /// </summary> 4 public classProperty:ITable 5{ 6 /// <summary> 7 /// 获取和设置资料的值 8 /// </summary> 9 /// <remarks> 10 /// 对于普通类型,例如float等类型直接就保存其ToString的返回结果 11 /// 对于复杂类型,则保存其json格式的对象 12 /// </remarks> 13 // TODO:第二版-需要考虑国际化情形下,属性有多个值的情形! 14 public stringValue{ get; set;} 15 16 /// <summary> 17 /// 获取和设置属性的Id 18 /// </summary> 19 publicGuidId{ get; set;} 20 21 publicMetaPropertyMeta{ get; set;} 22 23 /// <summary> 24 /// 获取和设置该属性对应的元数据Id 25 /// </summary> 26 publicGuidMetaId{ get; set;} 27 28 /// <summary> 29 /// 该属性对应的资料的编号 30 /// </summary> 31 publicGuidEntityId{ get; set;} 32 33 /// <summary> 34 /// 获取和设置该属性所属的资料 35 /// </summary> 36 publicGenericDynamicPropertiesEntityEntity{ get; set;} 37} 元数据 1 public classMetaProperty:INamedTable,ISecret 2{ 3 publicGuidId{ get; set;} 4 5 public stringBelongsToMaterial{ get; set;} 6 7 publicStringTitle{ get; set;} 8 9 public stringType{ get; set;} 10 11 public stringDefaultValue{ get; private set;} 12 13 /// <summary> 14 /// 获取和设置属性的权限 15 /// </summary> 16 public intPermission{ get; set;} 17 18 public virtual stringExpectedFormat{ get{ return string.Empty;}} 19 20 public virtual boolValid( stringvalue) 21{ 22 return true; 23} 24 25 public virtual boolValid( doublevalue) 26{ 27 return true; 28} 29 30 public staticMetaPropertyNewString( stringname) 31{ 32 return newMetaProperty() 33{ 34Id=Guid.NewGuid(), 35Title=name, 36Type=Default.MetaProperty.Type.String, 37Permission=Default.Permission.Mask 38}; 39} 40 41 public staticMetaPropertyNewNumber( stringname, doubledefaultValue= 0.0) 42{ 43 return newMetaProperty() 44{ 45Id=Guid.NewGuid(), 46Title=name, 47Type=Default.MetaProperty.Type.Number, 48Permission=Default.Permission.Mask, 49DefaultValue=defaultValue.ToString() 50}; 51} 52 53 public staticMetaPropertyNewAddress( stringname) 54{ 55 return newMetaProperty() 56{ 57Id=Guid.NewGuid(), 58Title=name, 59Type=Default.MetaProperty.Type.Address, 60Permission=Default.Permission.Mask 61}; 62} 63 64 public staticMetaPropertyNewRelationship( stringname) 65{ 66 return newMetaProperty() 67{ 68Id=Guid.NewGuid(), 69Title=name, 70Type=Default.MetaProperty.Type.Relationship, 71Permission=Default.Permission.Mask 72}; 73} 74 75 public staticMetaPropertyNewDateTime( stringname) 76{ 77 return newMetaProperty() 78{ 79Id=Guid.NewGuid(), 80Title=name, 81Type=Default.MetaProperty.Type.DateTime, 82Permission=Default.Permission.Mask 83}; 84} 85 86 public staticMetaPropertyNewDate( stringname) 87{ 88 return newMetaProperty() 89{ 90Id=Guid.NewGuid(), 91Title=name, 92Type=Default.MetaProperty.Type.Date, 93Permission=Default.Permission.Mask 94}; 95} 96 97 public staticMetaPropertyNewTime( stringname) 98{ 99 return newMetaProperty() 100{ 101Id=Guid.NewGuid(), 102Title=name, 103Type=Default.MetaProperty.Type.Time, 104Permission=Default.Permission.Mask 105}; 106} 107 108 public staticMetaPropertyNewUser( stringname) 109{ 110 return newMetaProperty() 111{ 112Id=Guid.NewGuid(), 113Title=name, 114Type=Default.MetaProperty.Type.User, 115Permission=Default.Permission.Mask 116}; 117} 118 119 public staticMetaPropertyNewUrl( stringname) 120{ 121 return newUrlMetaProperty() 122{ 123Id=Guid.NewGuid(), 124Title=name, 125Type=Default.MetaProperty.Type.Url, 126Permission=Default.Permission.Mask 127}; 128} 129 130 public staticMetaPropertyNewTag( stringname) 131{ 132 return newMetaProperty() 133{ 134Id=Guid.NewGuid(), 135Title=name, 136Type=Default.MetaProperty.Type.Tag, 137Permission=Default.Permission.Mask 138}; 139} 140} 141 142 public classMetaProperties:List<MetaProperty> 143{ 144 publicMetaPropertyFind( stringname) 145{ 146 return this.SingleOrDefault(p=>String.Compare(p.Title,name)== 0); 147} 148} 维护资料时,使用类似下面的代码就可以给资料创建无限多的属性,可以事先、事后给属性关联元数据,以便定义编辑和显示方式(里面用到一些Ioc和Mock): 1[TestMethod] 2 public void验证客户资料的动态属性的可行性() 3{ 4 varrep= newMemoryContext(); 5MemoryMetaSetmetas= newMemoryMetaSet(); 6metas.Add( typeof(Customer),MetaProperty.NewString( " 姓名 ")); 7metas.Add( typeof(Customer),MetaProperty.NewNumber( " 年龄 ")); 8metas.Add( typeof(Customer),MetaProperty.NewAddress( " 地址 ")); 9metas.Add( typeof(Customer),MetaProperty.NewRelationship( " 同事 ")); 10rep.MetaSet=metas; 11 12 varbuilder= newContainerBuilder(); 13 varmocks= newMockery(); 14 varuser=mocks.NewMock<IUser>(); 15Expect.AtLeastOnce.On(user).GetProperty( " Email ").Will(Return.Value(DEFAULT_USER_EMAIL)); 16 17builder.RegisterInstance(user).As<IUser>(); 18builder.RegisterInstance(rep).As<IContext>(); 19 varback=IocHelper.Container; 20 try 21{ 22IocHelper.Container=builder.Build(); 23 24 varcustomer=Customer.New<Customer>(); 25customer.Set( " 姓名 ", " XXX "); 26customer.Set( " 年龄 ", 28); 27customer.Set( " 地址 ", " ZZZZZZZZZZZZZZZZZZZZZZ "); 28 29 varcolleague=Customer.New<Customer>(); 30colleague.Set( " 姓名 ", " YYY "); 31 32 // 对于稍微复杂一点的对象,我们可以用json对象 33 customer.Set( " 同事 ",Relationship.Colleague(customer,colleague).ToString()); 34 35Assert.AreEqual( " XXX ",customer.Get( " 姓名 ")); 36} 37 finally 38{ 39IocHelper.Container=back; 40} 41} 因为动态属性事先不知道其格式,为了实现搜索功能,无法在编写程序的时候拼接查询用的SQL语句,因此我抽象了一层,定义了一个小的查询语法,写了一个小小的编译器将查询语句转化成SQL语句,实现了对动态属性的查询功能,请看下面的测试用例: 1[TestMethod] 2 public void测试简单的条件组合查询() 3{ 4 using( varcontext=IocHelper.Container.Resolve<IContext>()) 5{ 6 varcustomer=Customer.New<Customer>(); 7customer.Set( " 姓名 ", " 测试简单的条件组合查询 "); 8customer.Set( " 年龄 ", 28); 9customer.Set( " 地址 ", " 上海市浦东新区 "); 10context.Customers.Add(customer); 11context.SaveChanges(); 12 13 varresult=context.Customers.Query( " (AND(姓名='测试简单的条件组合查询') "+ 14 " (年龄介于1到30) "+ 15 " ) "); 16Assert.IsTrue(result.Count()> 0); 17 varactual=result.First(); 18Assert.AreEqual( " 测试简单的条件组合查询 ",actual.Get( " 姓名 ")); 19Assert.AreEqual( " 28 ",actual.Get( " 年龄 ")); 20} 21} 上面测试用例里的查询语句: (AND (姓名='测试简单的条件组合查询') (年龄介于1到30) ) 经过Query函数的编译之后,会转化成下面的两段SQL语句: SELECTe. *,p. * FROMProperties ASp INNER JOINGenericDynamicPropertiesEntities ASe ONp.EntityId =e.Id INNER JOINMetaSet ASm ONp.MetaId =m.Id WHERE(( CASE WHENm.Type = ' 日期时间型 ' AND( CONVERT( datetime,p.Value) =N ' 测试简单的条件组合查询 ') AND(m.Title =N ' 姓名 ') THEN 1 WHENm.Type = ' 字符串 ' AND(p.Value =N ' 测试简单的条件组合查询 ') AND(m.Title =N ' 姓名 ') THEN 1 ELSE 0 END = 1)) SELECTe. *,p. * FROMProperties ASp INNER JOINGenericDynamicPropertiesEntities ASe ONp.EntityId =e.Id INNER JOINMetaSet ASm ONp.MetaId =m.Id WHERE(( CASE WHENm.Type = ' 数字型 ' AND( CONVERT( int,p.Value) BETWEEN 1 AND 30) AND(m.Title =N ' 年龄 ') THEN 1 WHENm.Type = ' 日期时间型 ' AND( CONVERT( datetime,p.Value) BETWEENN ' 1 ' ANDN ' 30 ') AND(m.Title =N ' 年龄 ') THEN 1 ELSE 0 END = 1)) 然后分别执行查询并在业务层将求解查询结果的交集,也许是可以直接生成一条SQL语句交给数据库处理成最精简的结果再返回的,但是因为开发时间、以及目标客户的关系,暂时没有花精力做这个优化。 当然上面的查询语句写起来还比较复杂,因此我做了一个界面方便用户编辑查询条件,另外对资料属性的编辑、元数据维护等内容,后面再写文章说,蚊子太多了…… 本文转自 donjuan 博客园博客,原文链接:http://www.cnblogs.com/killmyday/archive/2012/07/21/2601900.html ,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册